1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use super::IosDeployCLIInstance;
use crate::prelude::*;

#[derive(Debug)]
#[cfg_attr(feature = "cli", derive(clap::Args))]
pub struct DetectDevicesConfig {
	#[cfg_attr(feature = "cli", clap(long, default_value_t = 1))]
	pub timeout: u8,

	#[cfg_attr(feature = "cli", clap(long, default_value_t = false))]
	pub wifi: bool,
}

impl Default for DetectDevicesConfig {
	#[instrument(level = "trace", skip())]
	fn default() -> Self {
		DetectDevicesConfig {
			timeout: 1,
			wifi: true,
		}
	}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Device {
	pub device_identifier: String,
	pub device_name: String,
	pub model_name: ModelName,
	pub interface: DeviceInterface,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum DeviceInterface {
	Usb,
	Wifi,
}

impl IosDeployCLIInstance {
	#[instrument(level = "trace")]
	pub fn detect_devices(&self, config: &DetectDevicesConfig) -> Result<Vec<Device>> {
		let mut command = self
			.bossy_command()
			.with_arg("--detect")
			.with_arg("--json")
			.with_args(["--timeout", config.timeout.to_string().as_str()]);

		if !config.wifi {
			command.add_arg("--no-wifi");
		}

		let output = match command.run_and_wait_for_string() {
			Ok(output) => output,
			Err(err) => {
				if err.status().and_then(|s| s.code()) == Some(253) {
					info!(exit_status = ?err.status(), "No devices detected, since ios-deploy exited with status code 253");
					return Ok(vec![]);
				}
				Err(err)?
			}
		};

		// after every } close brace, adds a comma
		// this is to handle { .. } \n { ... } even style messages
		let output = format!("[{}]", output);
		let output = output.replace("}{", "},{");

		#[derive(Debug, Deserialize)]
		struct Event {
			#[serde(rename(deserialize = "Interface"))]
			interface: DeviceInterface,

			#[serde(rename(deserialize = "Device"))]
			device: DeviceDetected,
		}

		#[derive(Debug, Deserialize)]
		struct DeviceDetected {
			#[serde(rename(deserialize = "DeviceIdentifier"))]
			device_identifier: String,

			#[serde(rename(deserialize = "DeviceName"))]
			device_name: String,

			#[serde(rename(deserialize = "modelName"))]
			model_name: ModelName,
		}

		let events = serde_json::from_str::<Vec<Event>>(&output)?;
		let devices = events
			.into_iter()
			.map(|event| Device {
				device_name: event.device.device_name,
				device_identifier: event.device.device_identifier,
				model_name: event.device.model_name,
				interface: event.interface,
			})
			.collect();

		Ok(devices)
	}
}