nativ 0.3.0

Nativ CLI — compile .nativ DSL to real SwiftUI and Jetpack Compose
use clap::{Args, ValueEnum};
use std::path::Path;
use std::process::Command;

#[derive(Args)]
pub struct SubmitArgs {
    /// Platform to submit
    #[arg(value_enum)]
    pub target: SubmitTarget,

    /// Fastlane lane name (default: beta for iOS, internal for Android)
    #[arg(long)]
    pub lane: Option<String>,

    /// Project directory (default: current directory)
    #[arg(short, long, default_value = ".")]
    pub dir: String,

    /// Print the fastlane command without running it
    #[arg(long)]
    pub dry_run: bool,
}

#[derive(Clone, Copy, ValueEnum)]
pub enum SubmitTarget {
    Ios,
    Android,
}

pub fn run(args: SubmitArgs) -> Result<(), Box<dyn std::error::Error>> {
    let project_dir = Path::new(&args.dir);
    let config_path = project_dir.join("nativ.toml");
    let _config = nativ_config::NativConfig::load(&config_path)?;

    let fastfile = project_dir.join("fastlane").join("Fastfile");
    if !fastfile.is_file() {
        return Err(format!(
            "{} not found. Create user-owned Fastlane lanes before running `nativ submit`.",
            fastfile.display()
        )
        .into());
    }

    let lane = args
        .lane
        .unwrap_or_else(|| default_lane(args.target).into());
    let platform = fastlane_platform(args.target);

    if args.dry_run {
        println!("Would run: fastlane {platform} {lane}");
        return Ok(());
    }

    let status = Command::new("fastlane")
        .arg(platform)
        .arg(&lane)
        .current_dir(project_dir)
        .status()
        .map_err(|err| {
            if err.kind() == std::io::ErrorKind::NotFound {
                "fastlane not found on PATH. Install Fastlane before running `nativ submit`.".into()
            } else {
                format!("failed to start fastlane: {err}")
            }
        })?;

    if !status.success() {
        return Err(format!("fastlane {platform} {lane} failed with {status}").into());
    }

    Ok(())
}

fn default_lane(target: SubmitTarget) -> &'static str {
    match target {
        SubmitTarget::Ios => "beta",
        SubmitTarget::Android => "internal",
    }
}

fn fastlane_platform(target: SubmitTarget) -> &'static str {
    match target {
        SubmitTarget::Ios => "ios",
        SubmitTarget::Android => "android",
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn defaults_to_common_fastlane_lanes() {
        assert_eq!(default_lane(SubmitTarget::Ios), "beta");
        assert_eq!(default_lane(SubmitTarget::Android), "internal");
        assert_eq!(fastlane_platform(SubmitTarget::Ios), "ios");
        assert_eq!(fastlane_platform(SubmitTarget::Android), "android");
    }
}