diff --git a/.changes/mobile-open.md b/.changes/mobile-open.md new file mode 100644 index 000000000..d504ac24a --- /dev/null +++ b/.changes/mobile-open.md @@ -0,0 +1,6 @@ +--- +"cli.rs": minor +"cli.js": minor +--- + +Added `android open` and `ios open` commands. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 7dc825708..0945082a6 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -272,7 +272,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=feat/library#d2b3f7248657c60dcc0669303329b802f04a6291" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=feat/library#c1365b7e90d341d67cf40f4d2f5532e932631907" dependencies = [ "bicycle", "bossy", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 8fd0c9263..777d4d53f 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -30,7 +30,7 @@ path = "src/main.rs" bossy = { git = "https://github.com/lucasfernog/bossy", branch = "fix/winapi-features" } [dependencies] -# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } +#cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "feat/library", default-features = false } bossy = "0.2" textwrap = { version = "0.11.0", features = ["term_size"] } diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index b3b3a2435..59dd3eba1 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -2,13 +2,37 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use cargo_mobile::{ + android::config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + config::{metadata::Metadata, Config}, + os, + util::cli::TextWrapper, +}; use clap::{Parser, Subcommand}; -use super::init::{command as init_command, Options as InitOptions, Target as InitTarget}; +use super::{ + ensure_init, + init::{command as init_command, Options as InitOptions}, + Target, +}; use crate::Result; pub(crate) mod project; +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("{0}")] + ProjectNotInitialized(String), + #[error(transparent)] + ConfigFailed(cargo_mobile::config::LoadOrGenError), + #[error(transparent)] + MetadataFailed(cargo_mobile::config::metadata::Error), + #[error("Android is marked as unsupported in your configuration file")] + Unsupported, + #[error(transparent)] + OpenFailed(os::OpenFileError), +} + #[derive(Parser)] #[clap( author, @@ -25,12 +49,38 @@ pub struct Cli { #[derive(Subcommand)] enum Commands { Init(InitOptions), + Open, } pub fn command(cli: Cli) -> Result<()> { match cli.command { - Commands::Init(options) => init_command(options, InitTarget::Android)?, + Commands::Init(options) => init_command(options, Target::Android)?, + Commands::Open => open()?, } Ok(()) } + +fn with_config( + wrapper: &TextWrapper, + f: impl FnOnce(&AndroidConfig, &AndroidMetadata) -> Result<(), Error>, +) -> Result<(), Error> { + let (config, _origin) = + Config::load_or_gen(".", true.into(), wrapper).map_err(Error::ConfigFailed)?; + let metadata = Metadata::load(config.app().root_dir()).map_err(Error::MetadataFailed)?; + if metadata.android().supported() { + f(config.android(), metadata.android()) + } else { + Err(Error::Unsupported) + } +} + +fn open() -> Result<()> { + let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); + with_config(&wrapper, |config, _metadata| { + ensure_init(config.project_dir(), Target::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) + })?; + Ok(()) +} diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 1089eff71..1fdd9e67b 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use super::Target; use crate::helpers::{app_paths::tauri_dir, template::JsonMap}; use crate::Result; use cargo_mobile::{ @@ -89,13 +90,6 @@ pub enum Error { OpenInEditor(util::OpenInEditorError), } -#[derive(PartialEq, Eq)] -pub enum Target { - Android, - #[cfg(target_os = "macos")] - Ios, -} - pub fn exec( target: Target, wrapper: &TextWrapper, diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 0b10b2d64..ff781d302 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -2,13 +2,37 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use cargo_mobile::{ + apple::config::{Config as AppleConfig, Metadata as AppleMetadata}, + config::{metadata::Metadata, Config}, + os, + util::cli::TextWrapper, +}; use clap::{Parser, Subcommand}; -use super::init::{command as init_command, Options as InitOptions, Target as InitTarget}; +use super::{ + ensure_init, + init::{command as init_command, Options as InitOptions}, + Target, +}; use crate::Result; pub(crate) mod project; +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("{0}")] + ProjectNotInitialized(String), + #[error(transparent)] + ConfigFailed(cargo_mobile::config::LoadOrGenError), + #[error(transparent)] + MetadataFailed(cargo_mobile::config::metadata::Error), + #[error("iOS is marked as unsupported in your configuration file")] + Unsupported, + #[error(transparent)] + OpenFailed(os::OpenFileError), +} + #[derive(Parser)] #[clap( author, @@ -25,12 +49,38 @@ pub struct Cli { #[derive(Subcommand)] enum Commands { Init(InitOptions), + Open, } pub fn command(cli: Cli) -> Result<()> { match cli.command { - Commands::Init(options) => init_command(options, InitTarget::Ios)?, + Commands::Init(options) => init_command(options, Target::Ios)?, + Commands::Open => open()?, } Ok(()) } + +fn with_config( + wrapper: &TextWrapper, + f: impl FnOnce(&AppleConfig, &AppleMetadata) -> Result<(), Error>, +) -> Result<(), Error> { + let (config, _origin) = + Config::load_or_gen(".", true.into(), wrapper).map_err(Error::ConfigFailed)?; + let metadata = Metadata::load(config.app().root_dir()).map_err(Error::MetadataFailed)?; + if metadata.apple().supported() { + f(config.apple(), metadata.apple()) + } else { + Err(Error::Unsupported) + } +} + +fn open() -> Result<()> { + let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); + with_config(&wrapper, |config, _metadata| { + ensure_init(config.project_dir(), Target::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) + })?; + Ok(()) +} diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 83baa249c..16ea4040a 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -2,7 +2,48 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use anyhow::{bail, Result}; +use std::path::PathBuf; + pub mod android; mod init; #[cfg(target_os = "macos")] pub mod ios; + +#[derive(PartialEq, Eq)] +pub enum Target { + Android, + #[cfg(target_os = "macos")] + Ios, +} + +impl Target { + fn ide_name(&self) -> &'static str { + match self { + Self::Android => "Android Studio", + #[cfg(target_os = "macos")] + Self::Ios => "Xcode", + } + } + + fn command_name(&self) -> &'static str { + match self { + Self::Android => "android", + #[cfg(target_os = "macos")] + Self::Ios => "ios", + } + } +} + +fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { + if !project_dir.exists() { + bail!( + "{} project directory {} doesn't exist. Please run `tauri {} init` and try again.", + target.ide_name(), + project_dir.display(), + target.command_name(), + ) + } else { + Ok(()) + } +}