cargo-mobile2 0.22.4

Rust on mobile made easy!
Documentation
use super::{DeviceInfo, Event};
use crate::{
    apple::{
        device::{Device, DeviceKind},
        target::Target,
    },
    env::{Env, ExplicitEnv as _},
    util::cli::{Report, Reportable},
    DuctExpressionExt,
};
use std::collections::BTreeSet;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum DeviceListError {
    #[error("Failed to request device list from `{command}`: {error}")]
    DetectionFailed {
        command: String,
        error: std::io::Error,
    },
    #[error("{0:?} isn't a valid target arch.")]
    ArchInvalid(String),
}

impl Reportable for DeviceListError {
    fn report(&self) -> Report {
        Report::error("Failed to detect connected iOS devices", self)
    }
}

fn parse_device_list<'a>(
    output: &std::process::Output,
) -> Result<BTreeSet<Device<'a>>, DeviceListError> {
    let stdout = String::from_utf8_lossy(&output.stdout);
    Event::parse_list(&stdout)
        .into_iter()
        .flat_map(|event| event.device_info().cloned())
        .map(
            |DeviceInfo {
                 device_identifier,
                 device_name,
                 model_arch,
                 model_name,
             }| {
                Target::for_arch(&model_arch)
                    .map(|target| {
                        Device::new(
                            device_identifier,
                            device_name,
                            model_name,
                            target,
                            DeviceKind::IosDeployDevice,
                        )
                    })
                    .ok_or(DeviceListError::ArchInvalid(model_arch))
            },
        )
        .collect::<Result<_, _>>()
}

pub fn device_list<'a>(env: &Env) -> Result<BTreeSet<Device<'a>>, DeviceListError> {
    let cmd = duct::cmd(
        "ios-deploy",
        ["--detect", "--timeout", "1", "--json", "--no-wifi"],
    )
    .stdout_capture()
    .stderr_capture()
    .vars(env.explicit_env());
    match cmd.run() {
        Ok(output) => {
            if output.stdout.is_empty() && output.stderr.is_empty() {
                log::info!("device detection returned a non-zero exit code, but stdout and stderr are both empty; interpreting as a successful run with no devices connected");
                Ok(Default::default())
            } else {
                parse_device_list(&output)
            }
        }
        Err(err) => Err(DeviceListError::DetectionFailed {
            command: format!("{cmd:?}"),
            error: err,
        }),
    }
}