diff --git a/.changes/bundle-version.md b/.changes/bundle-version.md new file mode 100644 index 00000000..cfb53840 --- /dev/null +++ b/.changes/bundle-version.md @@ -0,0 +1,5 @@ +--- +"cargo-mobile2": minor +--- + +Fixed apple bundle version string validation. Removed the `VersionNumber` type. diff --git a/src/apple/cli.rs b/src/apple/cli.rs index a1918928..3f632369 100644 --- a/src/apple/cli.rs +++ b/src/apple/cli.rs @@ -341,9 +341,10 @@ impl Exec for Input { &detect_target_ok, &env, |target: &Target| { - let mut app_version = config.bundle_version().clone(); + let mut app_version = config.bundle_version().to_string(); if let Some(build_number) = build_number { - app_version.push_extra(build_number); + app_version.push('.'); + app_version.push_str(&build_number.to_string()); } target diff --git a/src/apple/config/mod.rs b/src/apple/config/mod.rs index adfd5a1e..cf940291 100644 --- a/src/apple/config/mod.rs +++ b/src/apple/config/mod.rs @@ -2,7 +2,6 @@ mod raw; pub use self::raw::*; -use super::version_number::{VersionNumber, VersionNumberError}; use crate::{ config::app::App, util::{ @@ -19,9 +18,9 @@ use std::{ use thiserror::Error; static DEFAULT_PROJECT_DIR: &str = "gen/apple"; -const DEFAULT_BUNDLE_VERSION: VersionNumber = VersionNumber::new(VersionTriple::new(1, 0, 0), None); -const DEFAULT_IOS_VERSION: VersionDouble = VersionDouble::new(13, 0); -const DEFAULT_MACOS_VERSION: VersionDouble = VersionDouble::new(11, 0); +const DEFAULT_BUNDLE_VERSION: &str = "1.0.0"; +const DEFAULT_IOS_VERSION: &str = "13.0"; +const DEFAULT_MACOS_VERSION: &str = "11.0"; #[derive(Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] @@ -218,18 +217,14 @@ pub enum Error { DevelopmentTeamEmpty, #[error("`apple.project-dir` invalid: {0}")] ProjectDirInvalid(ProjectDirInvalid), - #[error("`apple.app-version` invalid: {0}")] - BundleVersionInvalid(VersionTripleError), + #[error("`apple.bundle-version` can only contain numbers, separated by `.`")] + BundleVersionInvalid, + #[error("`apple.bundle-version-short` invalid: {0}")] + BundleVersionShortInvalid(VersionTripleError), #[error("`apple.ios-version` invalid: {0}")] IosVersionInvalid(VersionDoubleError), #[error("`apple.macos-version` invalid: {0}")] MacOsVersionInvalid(VersionDoubleError), - #[error("`apple.app-version` short and long version number don't match: {0}")] - IosVersionNumberInvalid(VersionNumberError), - #[error("`apple.app-version` short and long version number don't match")] - IosVersionNumberMismatch, - #[error("`apple.app-version` `bundle-version-short` cannot be specified without also specifying `bundle-version`")] - InvalidVersionConfiguration, #[error("Identifier cannot contain underscores on iOS")] IdentifierCannotContainUnderscores, } @@ -240,44 +235,6 @@ impl Error { } } -#[derive(Debug)] -pub(crate) struct VersionInfo { - pub version_number: Option, - pub short_version_number: Option, -} - -impl VersionInfo { - pub(crate) fn from_raw( - version_string: &Option, - short_version_string: &Option, - ) -> Result { - let version_number = version_string - .as_deref() - .map(VersionNumber::from_str) - .transpose() - .map_err(Error::IosVersionNumberInvalid)?; - let short_version_number = short_version_string - .as_deref() - .map(VersionTriple::from_str) - .transpose() - .map_err(Error::BundleVersionInvalid)?; - if short_version_number.is_some() && version_number.is_none() { - return Err(Error::InvalidVersionConfiguration); - } - if let Some((version_number, short_version_number)) = - version_number.as_ref().zip(short_version_number) - { - if version_number.triple != short_version_number { - return Err(Error::IosVersionNumberMismatch); - } - } - Ok(Self { - version_number, - short_version_number, - }) - } -} - #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "kebab-case")] pub struct Config { @@ -285,10 +242,10 @@ pub struct Config { app: App, development_team: Option, project_dir: String, - bundle_version: VersionNumber, - bundle_version_short: VersionTriple, - ios_version: VersionDouble, - macos_version: VersionDouble, + bundle_version: String, + bundle_version_short: String, + ios_version: String, + macos_version: String, use_legacy_build_system: bool, plist_pairs: Vec, enable_bitcode: bool, @@ -335,18 +292,26 @@ impl Config { Ok(DEFAULT_PROJECT_DIR.to_owned()) })?; - let (bundle_version, bundle_version_short) = - VersionInfo::from_raw(&raw.bundle_version, &raw.bundle_version_short).map(|info| { - let bundle_version = info - .version_number - .clone() - .unwrap_or(DEFAULT_BUNDLE_VERSION); - - let bundle_version_short = - info.short_version_number.unwrap_or(bundle_version.triple); + let bundle_version = raw + .bundle_version + .unwrap_or_else(|| DEFAULT_BUNDLE_VERSION.to_string()); + if bundle_version + .split('.') + .any(|part| part.parse::().is_err()) + { + return Err(Error::BundleVersionInvalid); + } - (bundle_version, bundle_version_short) - })?; + let bundle_version_short = raw.bundle_version_short.unwrap_or_else(|| { + bundle_version + .split('.') + .take(3) + .collect::>() + .join(".") + }); + if let Err(e) = VersionTriple::from_str(&bundle_version_short) { + return Err(Error::BundleVersionShortInvalid(e)); + } let export_options_plist_path = raw .export_options_plist_path @@ -364,13 +329,15 @@ impl Config { .map(|str| VersionDouble::from_str(&str)) .transpose() .map_err(Error::IosVersionInvalid)? - .unwrap_or(DEFAULT_IOS_VERSION), + .map(|v| v.to_string()) + .unwrap_or_else(|| DEFAULT_IOS_VERSION.to_string()), macos_version: raw .macos_version .map(|str| VersionDouble::from_str(&str)) .transpose() - .map_err(Error::IosVersionInvalid)? - .unwrap_or(DEFAULT_MACOS_VERSION), + .map_err(Error::MacOsVersionInvalid)? + .map(|v| v.to_string()) + .unwrap_or_else(|| DEFAULT_MACOS_VERSION.to_string()), use_legacy_build_system: raw.use_legacy_build_system.unwrap_or(true), plist_pairs: raw.plist_pairs.unwrap_or_default(), enable_bitcode: raw.enable_bitcode.unwrap_or(false), @@ -441,10 +408,14 @@ impl Config { format!("{}_iOS", self.app.name()) } - pub fn bundle_version(&self) -> &VersionNumber { + pub fn bundle_version(&self) -> &str { &self.bundle_version } + pub fn bundle_version_short(&self) -> &str { + &self.bundle_version_short + } + pub fn development_team(&self) -> Option<&str> { self.development_team.as_deref() } diff --git a/src/apple/mod.rs b/src/apple/mod.rs index 0c31e097..240f3a78 100644 --- a/src/apple/mod.rs +++ b/src/apple/mod.rs @@ -7,7 +7,6 @@ pub mod project; pub(crate) mod system_profile; pub mod target; pub mod teams; -mod version_number; use std::path::PathBuf; diff --git a/src/apple/target.rs b/src/apple/target.rs index 75ca83cb..c3ef16c8 100644 --- a/src/apple/target.rs +++ b/src/apple/target.rs @@ -1,7 +1,6 @@ use super::{ config::{Config, Metadata}, system_profile::{self, DeveloperTools}, - version_number::VersionNumber, AuthCredentials, }; use crate::{ @@ -470,14 +469,14 @@ impl<'a> Target<'a> { env: &Env, noise_level: opts::NoiseLevel, profile: opts::Profile, - build_number: Option, + new_version: Option, archive_config: ArchiveConfig, ) -> Result<(), ArchiveError> { - if let Some(build_number) = build_number { + if let Some(version) = new_version { util::with_working_dir(config.project_dir(), || { duct::cmd( "xcrun", - ["agvtool", "new-version", "-all", &build_number.to_string()], + ["agvtool", "new-version", "-all", &version.to_string()], ) .dup_stdio() .run() diff --git a/src/apple/version_number.rs b/src/apple/version_number.rs deleted file mode 100644 index d47e2b0b..00000000 --- a/src/apple/version_number.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::util::{VersionTriple, VersionTripleError}; -use serde::{ser::Serializer, Serialize}; -use std::{ - fmt::{self, Debug, Display}, - str::FromStr, -}; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum VersionNumberError { - #[error("Failed to parse version triple.")] - VersionTripleInvalid(#[from] VersionTripleError), - #[error("Failed to parse extra version from {version:?}: {source}")] - ExtraVersionInvalid { - version: String, - source: std::num::ParseIntError, - }, -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct VersionNumber { - pub triple: VersionTriple, - pub extra: Option>, -} - -impl Display for VersionNumber { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.triple)?; - if let Some(extra) = &self.extra { - for number in extra { - write!(f, ".{}", number)?; - } - } - Ok(()) - } -} - -impl Serialize for VersionNumber { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.collect_str(self) - } -} - -impl FromStr for VersionNumber { - type Err = VersionNumberError; - - fn from_str(v: &str) -> Result { - match v.split('.').count() { - 1..=3 => { - let triple = VersionTriple::from_str(v)?; - Ok(Self { - triple, - extra: None, - }) - } - // Even when splitting a string that does not contain the delimeter, we should always get at least 1 split - // (the full string, which could be the empty string) - 0 => unreachable!(), - _ => { - let mut s = v.split('.'); - let triple = VersionTriple::from_split(&mut s, v)?; - let extra = Some( - s.map(|s| { - s.parse() - .map_err(|source| VersionNumberError::ExtraVersionInvalid { - version: v.to_owned(), - source, - }) - }) - .collect::, _>>()?, - ); - Ok(Self { triple, extra }) - } - } - } -} - -impl VersionNumber { - pub fn new_from_triple(triple: VersionTriple) -> Self { - Self { - triple, - extra: None, - } - } - - pub const fn new(triple: VersionTriple, extra: Option>) -> Self { - Self { triple, extra } - } - - pub fn push_extra(&mut self, number: u32) { - self.extra.get_or_insert_with(Default::default).push(number); - } -}