소스 검색

refactor(cli): remove mobile Error enum, use anyhow instead (#5076)

Lucas Fernandes Nogueira 3 년 전
부모
커밋
8cf9af93eb

+ 9 - 39
tooling/cli/src/mobile/android.rs

@@ -6,13 +6,12 @@ use cargo_mobile::{
   android::{
     adb,
     config::{Config as AndroidConfig, Metadata as AndroidMetadata, Raw as RawAndroidConfig},
-    device::{Device, RunError},
-    env::{Env, Error as AndroidEnvError},
-    target::{BuildError, Target},
+    device::Device,
+    env::Env,
+    target::Target,
   },
   config::app::App,
   device::PromptError,
-  env::Error as EnvError,
   opts::NoiseLevel,
   os,
   util::prompt,
@@ -36,34 +35,6 @@ mod dev;
 mod open;
 pub(crate) mod project;
 
-#[derive(Debug, thiserror::Error)]
-enum Error {
-  #[error(transparent)]
-  EnvInitFailed(EnvError),
-  #[error(transparent)]
-  AndroidEnvInitFailed(AndroidEnvError),
-  #[error(transparent)]
-  InitDotCargo(super::init::Error),
-  #[error("invalid tauri configuration: {0}")]
-  InvalidTauriConfig(String),
-  #[error("{0}")]
-  ProjectNotInitialized(String),
-  #[error(transparent)]
-  OpenFailed(os::OpenFileError),
-  #[error("{0}")]
-  DevFailed(String),
-  #[error("{0}")]
-  BuildFailed(String),
-  #[error(transparent)]
-  AndroidStudioScriptFailed(BuildError),
-  #[error(transparent)]
-  RunFailed(RunError),
-  #[error("{0}")]
-  TargetInvalid(String),
-  #[error(transparent)]
-  FailedToPromptForDevice(PromptError<adb::device_list::Error>),
-}
-
 #[derive(Parser)]
 #[clap(
   author,
@@ -138,11 +109,10 @@ pub fn get_config(
 
 fn with_config<T>(
   cli_options: Option<CliOptions>,
-  f: impl FnOnce(&App, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result<T, Error>,
-) -> Result<T, Error> {
+  f: impl FnOnce(&App, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result<T>,
+) -> Result<T> {
   let (app, config, metadata, cli_options) = {
-    let tauri_config =
-      get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
+    let tauri_config = get_tauri_config(None)?;
     let tauri_config_guard = tauri_config.lock().unwrap();
     let tauri_config_ = tauri_config_guard.as_ref().unwrap();
     let cli_options =
@@ -153,9 +123,9 @@ fn with_config<T>(
   f(&app, &config, &metadata, cli_options)
 }
 
-fn env() -> Result<Env, Error> {
-  let env = super::env().map_err(Error::EnvInitFailed)?;
-  cargo_mobile::android::env::Env::from_env(env).map_err(Error::AndroidEnvInitFailed)
+fn env() -> Result<Env> {
+  let env = super::env()?;
+  cargo_mobile::android::env::Env::from_env(env).map_err(Into::into)
 }
 
 fn delete_codegen_vars() {

+ 5 - 7
tooling/cli/src/mobile/android/android_studio_script.rs

@@ -1,4 +1,4 @@
-use super::{detect_target_ok, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget};
+use super::{detect_target_ok, ensure_init, env, init_dot_cargo, with_config, MobileTarget};
 use crate::Result;
 use clap::Parser;
 
@@ -33,11 +33,10 @@ pub fn command(options: Options) -> Result<()> {
   };
 
   with_config(None, |app, config, metadata, cli_options| {
-    ensure_init(config.project_dir(), MobileTarget::Android)
-      .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
+    ensure_init(config.project_dir(), MobileTarget::Android)?;
 
     let env = env()?;
-    init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?;
+    init_dot_cargo(app, Some((&env, config)))?;
 
     call_for_targets_with_fallback(
       options.targets.unwrap_or_default().iter(),
@@ -53,10 +52,9 @@ pub fn command(options: Options) -> Result<()> {
             true,
             profile,
           )
-          .map_err(Error::AndroidStudioScriptFailed)
+          .map_err(Into::into)
       },
     )
-    .map_err(|e| Error::TargetInvalid(e.to_string()))?
+    .map_err(|e| anyhow::anyhow!(e.to_string()))?
   })
-  .map_err(Into::into)
 }

+ 9 - 10
tooling/cli/src/mobile/android/build.rs

@@ -1,6 +1,6 @@
 use super::{
   delete_codegen_vars, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config,
-  Error, MobileTarget,
+  MobileTarget,
 };
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
@@ -75,15 +75,13 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
       set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", "");
       set_var("WRY_RUSTWEBVIEW_CLASS_INIT", "");
 
-      ensure_init(config.project_dir(), MobileTarget::Android)
-        .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
+      ensure_init(config.project_dir(), MobileTarget::Android)?;
 
       let env = env()?;
-      init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?;
+      init_dot_cargo(app, Some((&env, config)))?;
 
       let open = options.open;
-      run_build(options, config, &env, noise_level)
-        .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?;
+      run_build(options, config, &env, noise_level)?;
 
       if open {
         open_and_wait(config, &env);
@@ -176,7 +174,7 @@ fn run_build(
   Ok(())
 }
 
-fn get_targets_or_all<'a>(targets: Vec<String>) -> Result<Vec<&'a Target<'a>>, Error> {
+fn get_targets_or_all<'a>(targets: Vec<String>) -> Result<Vec<&'a Target<'a>>> {
   if targets.is_empty() {
     Ok(Target::all().iter().map(|t| t.1).collect())
   } else {
@@ -190,10 +188,11 @@ fn get_targets_or_all<'a>(targets: Vec<String>) -> Result<Vec<&'a Target<'a>>, E
 
     for t in targets {
       let target = Target::for_name(&t).ok_or_else(|| {
-        Error::TargetInvalid(format!(
+        anyhow::anyhow!(
           "Target {} is invalid; the possible targets are {}",
-          t, possible_targets
-        ))
+          t,
+          possible_targets
+        )
       })?;
       outs.push(target);
     }

+ 18 - 10
tooling/cli/src/mobile/android/dev.rs

@@ -1,6 +1,6 @@
 use super::{
   delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config,
-  Error, MobileTarget,
+  MobileTarget, PromptError,
 };
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
@@ -12,7 +12,9 @@ use clap::Parser;
 
 use cargo_mobile::{
   android::{
+    adb,
     config::{Config as AndroidConfig, Metadata as AndroidMetadata},
+    device::RunError as DeviceRunError,
     env::Env,
   },
   config::app::App,
@@ -75,10 +77,8 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
         WEBVIEW_CLIENT_CLASS_EXTENSION,
       );
       set_var("WRY_RUSTWEBVIEW_CLASS_INIT", WEBVIEW_CLASS_INIT);
-      ensure_init(config.project_dir(), MobileTarget::Android)
-        .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
-      run_dev(options, app, config, metadata, noise_level)
-        .map_err(|e| Error::DevFailed(format!("{:#}", e)))
+      ensure_init(config.project_dir(), MobileTarget::Android)?;
+      run_dev(options, app, config, metadata, noise_level).map_err(Into::into)
     },
   )
   .map_err(Into::into)
@@ -110,7 +110,7 @@ fn run_dev(
   let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?;
 
   let env = env()?;
-  init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?;
+  init_dot_cargo(app, Some((&env, config)))?;
 
   let open = options.open;
   let exit_on_panic = options.exit_on_panic;
@@ -142,7 +142,7 @@ fn run_dev(
             });
             Ok(Box::new(c) as Box<dyn DevProcess>)
           }
-          Err(Error::FailedToPromptForDevice(e)) => {
+          Err(RunError::FailedToPromptForDevice(e)) => {
             log::error!("{}", e);
             open_and_wait(config, &env)
           }
@@ -153,13 +153,21 @@ fn run_dev(
   )
 }
 
+#[derive(Debug, thiserror::Error)]
+enum RunError {
+  #[error(transparent)]
+  FailedToPromptForDevice(PromptError<adb::device_list::Error>),
+  #[error(transparent)]
+  RunFailed(DeviceRunError),
+}
+
 fn run(
   options: MobileOptions,
   config: &AndroidConfig,
   env: &Env,
   metadata: &AndroidMetadata,
   noise_level: NoiseLevel,
-) -> Result<DevChild, Error> {
+) -> Result<DevChild, RunError> {
   let profile = if options.debug {
     Profile::Debug
   } else {
@@ -169,7 +177,7 @@ fn run(
   let build_app_bundle = metadata.asset_packs().is_some();
 
   device_prompt(env)
-    .map_err(Error::FailedToPromptForDevice)?
+    .map_err(RunError::FailedToPromptForDevice)?
     .run(
       config,
       env,
@@ -181,5 +189,5 @@ fn run(
       ".MainActivity".into(),
     )
     .map(DevChild::new)
-    .map_err(Error::RunFailed)
+    .map_err(RunError::RunFailed)
 }

+ 3 - 6
tooling/cli/src/mobile/android/open.rs

@@ -1,4 +1,4 @@
-use super::{ensure_init, env, with_config, Error, MobileTarget};
+use super::{ensure_init, env, with_config, MobileTarget};
 use crate::Result;
 use cargo_mobile::os;
 
@@ -6,12 +6,9 @@ pub fn command() -> Result<()> {
   with_config(
     Some(Default::default()),
     |_root_conf, config, _metadata, _cli_options| {
-      ensure_init(config.project_dir(), MobileTarget::Android)
-        .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
+      ensure_init(config.project_dir(), MobileTarget::Android)?;
       let env = env()?;
-      os::open_file_with("Android Studio", config.project_dir(), &env.base)
-        .map_err(Error::OpenFailed)
+      os::open_file_with("Android Studio", config.project_dir(), &env.base).map_err(Into::into)
     },
   )
-  .map_err(Into::into)
 }

+ 21 - 43
tooling/cli/src/mobile/android/project.rs

@@ -2,13 +2,14 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-use crate::helpers::template;
+use crate::{helpers::template, Result};
+use anyhow::Context;
 use cargo_mobile::{
   android::{
     config::{Config, Metadata},
     target::Target,
   },
-  bossy, os,
+  os,
   target::TargetTrait as _,
   util::{
     self,
@@ -19,45 +20,18 @@ use cargo_mobile::{
 use handlebars::Handlebars;
 use include_dir::{include_dir, Dir};
 
-use std::{
-  ffi::OsStr,
-  fs,
-  path::{Path, PathBuf},
-};
+use std::{ffi::OsStr, fs, path::Path};
 
 const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/android");
 
-#[derive(Debug, thiserror::Error)]
-pub enum Error {
-  #[error("failed to run rustup: {0}")]
-  RustupFailed(bossy::Error),
-  #[error("failed to process template: {0}")]
-  TemplateProcessingFailed(String),
-  #[error("failed to create directory at {path}: {cause}")]
-  DirectoryCreationFailed {
-    path: PathBuf,
-    cause: std::io::Error,
-  },
-  #[error("failed to symlink asset directory")]
-  AssetDirSymlinkFailed,
-  #[error("failed to copy {src} to {dest}: {cause}")]
-  FileCopyFailed {
-    src: PathBuf,
-    dest: PathBuf,
-    cause: std::io::Error,
-  },
-  #[error("asset source {0} is invalid")]
-  AssetSourceInvalid(PathBuf),
-}
-
 pub fn gen(
   config: &Config,
   metadata: &Metadata,
   (handlebars, mut map): (Handlebars, template::JsonMap),
   wrapper: &TextWrapper,
-) -> Result<(), Error> {
+) -> Result<()> {
   println!("Installing Android toolchains...");
-  Target::install_all().map_err(Error::RustupFailed)?;
+  Target::install_all().with_context(|| "failed to run rustup")?;
   println!("Generating Android Studio project...");
   let dest = config.project_dir();
   let asset_packs = metadata.asset_packs().unwrap_or_default();
@@ -143,7 +117,7 @@ pub fn gen(
       options.create_new(true).write(true).open(path)
     },
   )
-  .map_err(|e| Error::TemplateProcessingFailed(e.to_string()))?;
+  .with_context(|| "failed to process template")?;
 
   if !asset_packs.is_empty() {
     Report::action_request(
@@ -157,23 +131,27 @@ pub fn gen(
     let source_src = config.app().root_dir().join(&source);
     let source_file = source_src
       .file_name()
-      .ok_or_else(|| Error::AssetSourceInvalid(source_src.clone()))?;
+      .ok_or_else(|| anyhow::anyhow!("asset source {} is invalid", source_src.display()))?;
     fs::copy(&source_src, source_dest.join(source_file)).map_err(|cause| {
-      Error::FileCopyFailed {
-        src: source_src,
-        dest: source_dest.clone(),
-        cause,
-      }
+      anyhow::anyhow!(
+        "failed to copy {} to {}: {}",
+        source_src.display(),
+        source_dest.display(),
+        cause
+      )
     })?;
   }
 
   let dest = prefix_path(dest, "app/src/main/");
-  fs::create_dir_all(&dest).map_err(|cause| Error::DirectoryCreationFailed {
-    path: dest.clone(),
-    cause,
+  fs::create_dir_all(&dest).map_err(|cause| {
+    anyhow::anyhow!(
+      "failed to create directory at {}: {}",
+      dest.display(),
+      cause
+    )
   })?;
   os::ln::force_symlink_relative(config.app().asset_dir(), dest, ln::TargetStyle::Directory)
-    .map_err(|_| Error::AssetDirSymlinkFailed)?;
+    .map_err(|_| anyhow::anyhow!("failed to symlink asset directory"))?;
 
   Ok(())
 }

+ 19 - 55
tooling/cli/src/mobile/init.rs

@@ -7,10 +7,8 @@ use crate::helpers::{config::get as get_tauri_config, template::JsonMap};
 use crate::Result;
 use cargo_mobile::{
   android::{
-    self, config::Config as AndroidConfig, env::Env as AndroidEnv, ndk,
-    target::Target as AndroidTarget,
+    config::Config as AndroidConfig, env::Env as AndroidEnv, target::Target as AndroidTarget,
   },
-  bossy,
   config::app::App,
   dot_cargo,
   os::code_command,
@@ -23,7 +21,7 @@ use cargo_mobile::{
 use clap::Parser;
 use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError};
 
-use std::{env::current_dir, fs, io, path::PathBuf};
+use std::{env::current_dir, fs, path::PathBuf};
 
 #[derive(Debug, Parser)]
 #[clap(about = "Initializes a Tauri Android project")]
@@ -41,39 +39,8 @@ pub fn command(mut options: Options, target: Target) -> Result<()> {
   Ok(())
 }
 
-#[derive(Debug, thiserror::Error)]
-pub enum Error {
-  #[error("invalid tauri configuration: {0}")]
-  InvalidTauriConfig(String),
-  #[error("failed to create asset dir {asset_dir}: {cause}")]
-  AssetDirCreation {
-    asset_dir: PathBuf,
-    cause: io::Error,
-  },
-  #[error("failed to install LLDB VS Code extension: {0}")]
-  LldbExtensionInstall(bossy::Error),
-  #[error(transparent)]
-  DotCargoLoad(dot_cargo::LoadError),
-  #[error(transparent)]
-  DotCargoGenFailed(ndk::MissingToolError),
-  #[error(transparent)]
-  HostTargetTripleDetection(util::HostTargetTripleError),
-  #[cfg(target_os = "macos")]
-  #[error(transparent)]
-  IosInit(super::ios::project::Error),
-  #[error(transparent)]
-  AndroidEnv(android::env::Error),
-  #[error(transparent)]
-  AndroidInit(super::android::project::Error),
-  #[error(transparent)]
-  DotCargoWrite(dot_cargo::WriteError),
-}
-
-pub fn init_dot_cargo(
-  app: &App,
-  android: Option<(&AndroidEnv, &AndroidConfig)>,
-) -> Result<(), Error> {
-  let mut dot_cargo = dot_cargo::DotCargo::load(app).map_err(Error::DotCargoLoad)?;
+pub fn init_dot_cargo(app: &App, android: Option<(&AndroidEnv, &AndroidConfig)>) -> Result<()> {
+  let mut dot_cargo = dot_cargo::DotCargo::load(app)?;
   // Mysteriously, builds that don't specify `--target` seem to fight over
   // the build cache with builds that use `--target`! This means that
   // alternating between i.e. `cargo run` and `cargo apple run` would
@@ -84,21 +51,18 @@ pub fn init_dot_cargo(
   //
   // This behavior could be explained here:
   // https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags
-  dot_cargo
-    .set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?);
+  dot_cargo.set_default_target(util::host_target_triple()?);
 
   if let Some((env, config)) = android {
     for target in AndroidTarget::all().values() {
       dot_cargo.insert_target(
         target.triple.to_owned(),
-        target
-          .generate_cargo_config(config, env)
-          .map_err(Error::DotCargoGenFailed)?,
+        target.generate_cargo_config(config, env)?,
       );
     }
   }
 
-  dot_cargo.write(app).map_err(Error::DotCargoWrite)
+  dot_cargo.write(app).map_err(Into::into)
 }
 
 pub fn exec(
@@ -107,9 +71,8 @@ pub fn exec(
   non_interactive: bool,
   skip_dev_tools: bool,
   #[allow(unused_variables)] reinstall_deps: bool,
-) -> Result<App, Error> {
-  let tauri_config =
-    get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
+) -> Result<App> {
+  let tauri_config = get_tauri_config(None)?;
   let tauri_config_guard = tauri_config.lock().unwrap();
   let tauri_config_ = tauri_config_guard.as_ref().unwrap();
 
@@ -117,7 +80,12 @@ pub fn exec(
 
   let asset_dir = app.asset_dir();
   if !asset_dir.is_dir() {
-    fs::create_dir_all(&asset_dir).map_err(|cause| Error::AssetDirCreation { asset_dir, cause })?;
+    fs::create_dir_all(&asset_dir).map_err(|cause| {
+      anyhow::anyhow!(
+        "failed to create asset dir {path}: {cause}",
+        path = asset_dir.display()
+      )
+    })?;
   }
   if !skip_dev_tools && util::command_present("code").unwrap_or_default() {
     let mut command = code_command();
@@ -125,9 +93,7 @@ pub fn exec(
     if non_interactive {
       command.add_arg("--force");
     }
-    command
-      .run_and_wait()
-      .map_err(Error::LldbExtensionInstall)?;
+    command.run_and_wait()?;
   }
 
   let (handlebars, mut map) = handlebars(&app);
@@ -173,8 +139,7 @@ pub fn exec(
         let (app, config, metadata) =
           super::android::get_config(Some(app), tauri_config_, &Default::default());
         map.insert("android", &config);
-        super::android::project::gen(&config, &metadata, (handlebars, map), wrapper)
-          .map_err(Error::AndroidInit)?;
+        super::android::project::gen(&config, &metadata, (handlebars, map), wrapper)?;
         init_dot_cargo(&app, Some((&env, &config)))?;
         app
       }
@@ -188,7 +153,7 @@ pub fn exec(
           init_dot_cargo(&app, None)?;
           app
         } else {
-          return Err(Error::AndroidEnv(err));
+          return Err(err.into());
         }
       }
     },
@@ -206,8 +171,7 @@ pub fn exec(
         non_interactive,
         skip_dev_tools,
         reinstall_deps,
-      )
-      .map_err(Error::IosInit)?;
+      )?;
       init_dot_cargo(&app, None)?;
       app
     }

+ 7 - 46
tooling/cli/src/mobile/ios.rs

@@ -8,15 +8,15 @@ use cargo_mobile::{
       Config as AppleConfig, Metadata as AppleMetadata, Platform as ApplePlatform,
       Raw as RawAppleConfig,
     },
-    device::{Device, RunError},
+    device::Device,
     ios_deploy,
-    target::{CompileLibError, Target},
+    target::Target,
   },
   config::app::App,
   device::PromptError,
-  env::{Env, Error as EnvError},
+  env::Env,
   opts::NoiseLevel,
-  os, util,
+  os,
   util::prompt,
 };
 use clap::{Parser, Subcommand};
@@ -31,50 +31,12 @@ use crate::{
   Result,
 };
 
-use std::path::PathBuf;
-
 mod build;
 mod dev;
 mod open;
 pub(crate) mod project;
 mod xcode_script;
 
-#[derive(Debug, thiserror::Error)]
-enum Error {
-  #[error(transparent)]
-  EnvInitFailed(#[from] EnvError),
-  #[error(transparent)]
-  InitDotCargo(super::init::Error),
-  #[error("invalid tauri configuration: {0}")]
-  InvalidTauriConfig(String),
-  #[error("{0}")]
-  ProjectNotInitialized(String),
-  #[error(transparent)]
-  OpenFailed(os::OpenFileError),
-  #[error("{0}")]
-  DevFailed(String),
-  #[error("{0}")]
-  BuildFailed(String),
-  #[error(transparent)]
-  NoHomeDir(util::NoHomeDir),
-  #[error("SDK root provided by Xcode was invalid. {sdk_root} doesn't exist or isn't a directory")]
-  SdkRootInvalid { sdk_root: PathBuf },
-  #[error("Include dir was invalid. {include_dir} doesn't exist or isn't a directory")]
-  IncludeDirInvalid { include_dir: PathBuf },
-  #[error("macOS SDK root was invalid. {macos_sdk_root} doesn't exist or isn't a directory")]
-  MacosSdkRootInvalid { macos_sdk_root: PathBuf },
-  #[error("Arch specified by Xcode was invalid. {arch} isn't a known arch")]
-  ArchInvalid { arch: String },
-  #[error(transparent)]
-  CompileLibFailed(CompileLibError),
-  #[error(transparent)]
-  FailedToPromptForDevice(PromptError<ios_deploy::DeviceListError>),
-  #[error(transparent)]
-  RunFailed(RunError),
-  #[error("{0}")]
-  TargetInvalid(String),
-}
-
 #[derive(Parser)]
 #[clap(
   author,
@@ -146,11 +108,10 @@ pub fn get_config(
 
 fn with_config<T>(
   cli_options: Option<CliOptions>,
-  f: impl FnOnce(&App, &AppleConfig, &AppleMetadata, CliOptions) -> Result<T, Error>,
-) -> Result<T, Error> {
+  f: impl FnOnce(&App, &AppleConfig, &AppleMetadata, CliOptions) -> Result<T>,
+) -> Result<T> {
   let (app, config, metadata, cli_options) = {
-    let tauri_config =
-      get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
+    let tauri_config = get_tauri_config(None)?;
     let tauri_config_guard = tauri_config.lock().unwrap();
     let tauri_config_ = tauri_config_guard.as_ref().unwrap();
     let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios));

+ 5 - 7
tooling/cli/src/mobile/ios/build.rs

@@ -1,6 +1,6 @@
 use super::{
   detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config,
-  Error, MobileTarget,
+  MobileTarget,
 };
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
@@ -67,15 +67,13 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
   with_config(
     Some(Default::default()),
     |app, config, _metadata, _cli_options| {
-      ensure_init(config.project_dir(), MobileTarget::Ios)
-        .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
+      ensure_init(config.project_dir(), MobileTarget::Ios)?;
 
       let env = env()?;
-      init_dot_cargo(app, None).map_err(Error::InitDotCargo)?;
+      init_dot_cargo(app, None)?;
 
       let open = options.open;
-      run_build(options, config, &env, noise_level)
-        .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?;
+      run_build(options, config, &env, noise_level)?;
 
       if open {
         open_and_wait(config, &env);
@@ -157,7 +155,7 @@ fn run_build(
       anyhow::Result::Ok(())
     },
   )
-  .map_err(|e: TargetInvalid| Error::TargetInvalid(e.to_string()))?
+  .map_err(|e: TargetInvalid| anyhow::anyhow!(e.to_string()))?
   .map_err(|e: anyhow::Error| e)?;
 
   log_finished(out_files, "IPA");

+ 18 - 13
tooling/cli/src/mobile/ios/dev.rs

@@ -1,5 +1,5 @@
 use super::{
-  device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, Error, MobileTarget,
+  device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, MobileTarget,
 };
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
@@ -10,8 +10,9 @@ use crate::{
 use clap::Parser;
 
 use cargo_mobile::{
-  apple::config::Config as AppleConfig,
+  apple::{config::Config as AppleConfig, device::RunError as DeviceRunError, ios_deploy},
   config::app::App,
+  device::PromptError,
   env::Env,
   opts::{NoiseLevel, Profile},
 };
@@ -58,12 +59,10 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
   with_config(
     Some(Default::default()),
     |app, config, _metadata, _cli_options| {
-      ensure_init(config.project_dir(), MobileTarget::Ios)
-        .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
-      run_dev(options, app, config, noise_level).map_err(|e| Error::DevFailed(format!("{:#}", e)))
+      ensure_init(config.project_dir(), MobileTarget::Ios)?;
+      run_dev(options, app, config, noise_level).map_err(Into::into)
     },
   )
-  .map_err(Into::into)
 }
 
 fn run_dev(
@@ -76,8 +75,7 @@ fn run_dev(
   let mut interface = crate::dev::setup(&mut dev_options)?;
 
   let bundle_identifier = {
-    let tauri_config =
-      get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
+    let tauri_config = get_tauri_config(None)?;
     let tauri_config_guard = tauri_config.lock().unwrap();
     let tauri_config_ = tauri_config_guard.as_ref().unwrap();
     tauri_config_.tauri.bundle.identifier.clone()
@@ -92,7 +90,7 @@ fn run_dev(
   let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?;
 
   let env = env()?;
-  init_dot_cargo(app, None).map_err(Error::InitDotCargo)?;
+  init_dot_cargo(app, None)?;
 
   let open = options.open;
   let exit_on_panic = options.exit_on_panic;
@@ -124,7 +122,7 @@ fn run_dev(
             });
             Ok(Box::new(c) as Box<dyn DevProcess>)
           }
-          Err(Error::FailedToPromptForDevice(e)) => {
+          Err(RunError::FailedToPromptForDevice(e)) => {
             log::error!("{}", e);
             open_and_wait(config, &env)
           }
@@ -135,12 +133,19 @@ fn run_dev(
   )
 }
 
+#[derive(Debug, thiserror::Error)]
+enum RunError {
+  #[error(transparent)]
+  FailedToPromptForDevice(PromptError<ios_deploy::DeviceListError>),
+  #[error(transparent)]
+  RunFailed(DeviceRunError),
+}
 fn run(
   options: MobileOptions,
   config: &AppleConfig,
   env: &Env,
   noise_level: NoiseLevel,
-) -> Result<DevChild, Error> {
+) -> Result<DevChild, RunError> {
   let profile = if options.debug {
     Profile::Debug
   } else {
@@ -150,8 +155,8 @@ fn run(
   let non_interactive = true; // ios-deploy --noninteractive (quit when app crashes or exits)
 
   device_prompt(env)
-    .map_err(Error::FailedToPromptForDevice)?
+    .map_err(RunError::FailedToPromptForDevice)?
     .run(config, env, noise_level, non_interactive, profile)
     .map(DevChild::new)
-    .map_err(Error::RunFailed)
+    .map_err(RunError::RunFailed)
 }

+ 3 - 5
tooling/cli/src/mobile/ios/open.rs

@@ -1,4 +1,4 @@
-use super::{ensure_init, env, with_config, Error, MobileTarget};
+use super::{ensure_init, env, with_config, MobileTarget};
 use crate::Result;
 use cargo_mobile::os;
 
@@ -6,11 +6,9 @@ pub fn command() -> Result<()> {
   with_config(
     Some(Default::default()),
     |_root_conf, config, _metadata, _cli_options| {
-      ensure_init(config.project_dir(), MobileTarget::Ios)
-        .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
+      ensure_init(config.project_dir(), MobileTarget::Ios)?;
       let env = env()?;
-      os::open_file_with("Xcode", config.project_dir(), &env).map_err(Error::OpenFailed)
+      os::open_file_with("Xcode", config.project_dir(), &env).map_err(Into::into)
     },
   )
-  .map_err(Into::into)
 }

+ 15 - 35
tooling/cli/src/mobile/ios/project.rs

@@ -2,7 +2,8 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-use crate::helpers::template;
+use crate::{helpers::template, Result};
+use anyhow::Context;
 use cargo_mobile::{
   apple::{
     config::{Config, Metadata},
@@ -23,29 +24,6 @@ use std::{
 
 const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/ios");
 
-#[derive(Debug, thiserror::Error)]
-pub enum Error {
-  #[error(transparent)]
-  Rustup(bossy::Error),
-  #[error(transparent)]
-  RustVersionCheck(util::RustVersionError),
-  #[error("failed to install Apple dependencies: {0}")]
-  DepsInstall(deps::Error),
-  #[error("failed to process template: {0}")]
-  TemplateProcessing(String),
-  #[error("failed to symlink asset directory")]
-  AssetDirSymlink,
-  #[error("failed to create directory at {path}: {cause}")]
-  DirectoryCreation {
-    path: PathBuf,
-    cause: std::io::Error,
-  },
-  #[error("failed to run `xcodegen`: {0}")]
-  Xcodegen(bossy::Error),
-  #[error("failed to run `pod install`: {0}")]
-  PodInstall(bossy::Error),
-}
-
 // unprefixed app_root seems pretty dangerous!!
 // TODO: figure out what cargo-mobile meant by that
 pub fn gen(
@@ -56,13 +34,13 @@ pub fn gen(
   non_interactive: bool,
   skip_dev_tools: bool,
   reinstall_deps: bool,
-) -> Result<(), Error> {
+) -> Result<()> {
   println!("Installing iOS toolchains...");
-  Target::install_all().map_err(Error::Rustup)?;
-  rust_version_check(wrapper).map_err(Error::RustVersionCheck)?;
+  Target::install_all()?;
+  rust_version_check(wrapper)?;
 
   deps::install_all(wrapper, non_interactive, skip_dev_tools, reinstall_deps)
-    .map_err(Error::DepsInstall)?;
+    .with_context(|| "failed to install Apple dependencies")?;
 
   let dest = config.project_dir();
   let rel_prefix = util::relativize_path(config.app().root_dir(), &dest);
@@ -161,16 +139,18 @@ pub fn gen(
       File::create(path)
     },
   )
-  .map_err(|e| Error::TemplateProcessing(e.to_string()))?;
+  .with_context(|| "failed to process template")?;
 
   ln::force_symlink_relative(config.app().asset_dir(), &dest, ln::TargetStyle::Directory)
-    .map_err(|_| Error::AssetDirSymlink)?;
+    .map_err(|_| anyhow::anyhow!("failed to symlink asset directory"))?;
 
   // Create all asset catalog directories if they don't already exist
   for dir in asset_catalogs {
-    std::fs::create_dir_all(dir).map_err(|cause| Error::DirectoryCreation {
-      path: dest.clone(),
-      cause,
+    std::fs::create_dir_all(dir).map_err(|cause| {
+      anyhow::anyhow!(
+        "failed to create directory at {path}: {cause}",
+        path = dir.display()
+      )
     })?;
   }
 
@@ -181,13 +161,13 @@ pub fn gen(
     .with_args(&["generate", "--spec"])
     .with_arg(dest.join("project.yml"))
     .run_and_wait()
-    .map_err(Error::Xcodegen)?;
+    .with_context(|| "failed to run `xcodegen`")?;
 
   if !ios_pods.is_empty() || !macos_pods.is_empty() {
     bossy::Command::impure_parse("pod install")
       .with_arg(format!("--project-directory={}", dest.display()))
       .run_and_wait()
-      .map_err(Error::PodInstall)?;
+      .with_context(|| "failed to run `pod install`")?;
   }
   Ok(())
 }

+ 35 - 26
tooling/cli/src/mobile/ios/xcode_script.rs

@@ -1,4 +1,4 @@
-use super::{env, init_dot_cargo, with_config, Error};
+use super::{env, init_dot_cargo, with_config};
 use crate::Result;
 use clap::Parser;
 
@@ -43,24 +43,24 @@ pub fn command(options: Options) -> Result<()> {
 
   with_config(None, |root_conf, config, metadata, cli_options| {
     let env = env()?;
-    init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?;
+    init_dot_cargo(root_conf, None)?;
     // The `PATH` env var Xcode gives us is missing any additions
     // made by the user's profile, so we'll manually add cargo's
     // `PATH`.
-    let env = env.prepend_to_path(
-      util::home_dir()
-        .map_err(Error::NoHomeDir)?
-        .join(".cargo/bin"),
-    );
+    let env = env.prepend_to_path(util::home_dir()?.join(".cargo/bin"));
 
     if !options.sdk_root.is_dir() {
-      return Err(Error::SdkRootInvalid {
-        sdk_root: options.sdk_root,
-      });
+      return Err(anyhow::anyhow!(
+        "SDK root provided by Xcode was invalid. {} doesn't exist or isn't a directory",
+        options.sdk_root.display(),
+      ));
     }
     let include_dir = options.sdk_root.join("usr/include");
     if !include_dir.is_dir() {
-      return Err(Error::IncludeDirInvalid { include_dir });
+      return Err(anyhow::anyhow!(
+        "Include dir was invalid. {} doesn't exist or isn't a directory",
+        include_dir.display()
+      ));
     }
 
     let mut host_env = HashMap::<&str, &OsStr>::new();
@@ -71,7 +71,10 @@ pub fn command(options: Options) -> Result<()> {
         .sdk_root
         .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk");
       if !macos_sdk_root.is_dir() {
-        return Err(Error::MacosSdkRootInvalid { macos_sdk_root });
+        return Err(anyhow::anyhow!(
+          "macOS SDK root was invalid. {0} doesn't exist or isn't a directory",
+          macos_sdk_root.display()
+        ));
       }
       (
         format!("-isysroot {}", macos_sdk_root.display()),
@@ -98,7 +101,12 @@ pub fn command(options: Options) -> Result<()> {
       let triple = match arch.as_str() {
         "arm64" => "aarch64_apple_ios",
         "x86_64" => "x86_64_apple_ios",
-        _ => return Err(Error::ArchInvalid { arch }),
+        _ => {
+          return Err(anyhow::anyhow!(
+            "Arch specified by Xcode was invalid. {} isn't a known arch",
+            arch
+          ))
+        }
       };
       let cflags = format!("CFLAGS_{}", triple);
       let cxxflags = format!("CFLAGS_{}", triple);
@@ -114,21 +122,22 @@ pub fn command(options: Options) -> Result<()> {
       let target = if macos {
         &macos_target
       } else {
-        Target::for_arch(&arch).ok_or_else(|| Error::ArchInvalid {
-          arch: arch.to_owned(),
+        Target::for_arch(&arch).ok_or_else(|| {
+          anyhow::anyhow!(
+            "Arch specified by Xcode was invalid. {} isn't a known arch",
+            arch
+          )
         })?
       };
-      target
-        .compile_lib(
-          config,
-          metadata,
-          cli_options.noise_level,
-          true,
-          profile,
-          &env,
-          target_env,
-        )
-        .map_err(Error::CompileLibFailed)?;
+      target.compile_lib(
+        config,
+        metadata,
+        cli_options.noise_level,
+        true,
+        profile,
+        &env,
+        target_env,
+      )?;
     }
     Ok(())
   })