apple_clis/cli/
device_name.rs

1use crate::prelude::*;
2use crate::xcrun::simctl::XcRunSimctlInstance;
3
4#[derive(clap::Args, Debug)]
5#[group(required = true, multiple = false)]
6pub struct DeviceSimulatorUnBootedArgs {
7	/// Automatically selects the latest iPad simulator
8	#[arg(long, group = "device_name")]
9	ipad: bool,
10
11	/// Automatically selects the latest iPhone simulator
12	#[arg(long, group = "device_name")]
13	iphone: bool,
14
15	/// Provide an exact name, e.g. "iPhone 12 Pro Max"
16	#[arg(group = "device_name")]
17	name: Option<DeviceName>,
18}
19
20#[derive(clap::Args, Debug)]
21#[group(required = true)]
22pub struct DeviceSimulatorBootedArgs {
23	/// Will find *any* device that is already booted
24	/// as opposed to [DeviceSimulatorBooted].ipad (--ipad) or [DeviceSimulatorBooted].iphone (--iphone)
25	#[arg(long, group = "device_name")]
26	booted: bool,
27
28	/// Will automatically boot a device if none matching the criteria is already booted
29	#[arg(long, requires = "device_name")]
30	auto_boot: bool,
31
32	/// Automatically selects a booted iPad simulator
33	#[arg(long, group = "device_name")]
34	ipad: bool,
35
36	/// Automatically selects a booted iPhone simulator
37	#[arg(long, group = "device_name")]
38	iphone: bool,
39
40	/// Provide an exact name, e.g. "iPhone 12 Pro Max"
41	/// Will confirm it is booted
42	#[arg(group = "device_name")]
43	name: Option<DeviceName>,
44}
45
46impl DeviceSimulatorUnBootedArgs {
47	pub fn resolve(self, simctl_instance: &XcRunSimctlInstance) -> color_eyre::Result<DeviceName> {
48		match (self.ipad, self.iphone, self.name) {
49			(false, false, Some(name)) => Ok(name),
50			(true, false, None) => {
51				let list = simctl_instance.list()?;
52				let latest_ipad = list
53					.get_success_reported()?
54					.devices()
55					.names()
56					.ipads()
57					.max()
58					.ok_or_else(|| eyre!("No simulator iPads found!"))?;
59				Ok((*latest_ipad).into())
60			}
61			(false, true, None) => {
62				let list_output = &simctl_instance.list()?;
63				let latest_iphone = list_output
64					.get_success_reported()?
65					.devices()
66					.names()
67					.iphones()
68					.max()
69					.ok_or_else(|| eyre!("No simulator iPhones found!"))?;
70				Ok((*latest_iphone).into())
71			}
72			_ => Err(eyre!("Clap arguments should prevent this")),
73		}
74	}
75}
76
77impl DeviceSimulatorBootedArgs {
78	pub fn resolve(self, simctl_instance: &XcRunSimctlInstance) -> color_eyre::Result<DeviceName> {
79		let list = simctl_instance.list()?;
80		let booted_devices = list
81			.get_success_reported()?
82			.devices()
83			.filter(|d| d.ready())
84			.collect::<Vec<_>>();
85		if booted_devices.is_empty() && !self.auto_boot {
86			Err(
87				eyre!("No booted devices found!")
88					.with_suggestion(|| "try using the flag --auto-boot")
89					.with_suggestion(|| "try running `apple-clis xcrun simctl boot` to boot a simulator"),
90			)
91		} else {
92			match (
93				self.booted,
94				self.ipad,
95				self.iphone,
96				self.name,
97				self.auto_boot,
98			) {
99				(true, false, false, None, auto_boot) => {
100					if auto_boot {
101						let a_device_name = &list
102							.get_success_reported()?
103							.devices()
104							.a_device()
105							.ok_or_else(|| eyre!("Couldn't find any simulators installed"))?
106							.name;
107						info!(device_name = %a_device_name, "Since auto-boot is enabled, booting a simulator");
108						simctl_instance.boot(a_device_name)?.success()?;
109						Ok(a_device_name.clone())
110					} else {
111						Ok(booted_devices[0].name.clone())
112					}
113				}
114				(false, true, false, None, auto_boot) => {
115					if !auto_boot {
116						booted_devices
117							.iter()
118							.filter_map(|d| d.name.get_ipad())
119							.max()
120							.cloned()
121							.map(DeviceName::from)
122							.ok_or_else(|| eyre!("No booted iPads found!"))
123							.with_suggestion(|| {
124								"try running `apple-clis xcrun simctl boot --ipad` to boot a simulator"
125							})
126							.with_note(|| format!("Booted devices: {:?}", &booted_devices))
127					} else {
128						let a_device_name = (*list
129							.get_success_reported()?
130							.devices()
131							.names()
132							.an_ipad()
133							.ok_or_else(|| eyre!("Couldn't find any iPad simulators installed"))?)
134						.into();
135						info!(device_name = %a_device_name, "Since auto-boot is enabled, booting an iPad simulator");
136						simctl_instance.boot(&a_device_name)?.success()?;
137						Ok(a_device_name)
138					}
139				}
140				(false, false, true, None, auto_boot) => {
141					if !auto_boot {
142						booted_devices
143							.iter()
144							.filter_map(|d| d.name.get_iphone())
145							.max()
146							.cloned()
147							.map(DeviceName::from)
148							.ok_or_else(|| {
149								eyre!("No booted iPhones found!")
150									.with_suggestion(|| {
151										"try running `apple-clis xcrun simctl boot --iphone` to boot a simulator"
152									})
153									.with_note(|| format!("Booted devices: {:?}", &booted_devices))
154							})
155					} else {
156						let a_device_name: DeviceName = (*list
157							.get_success_reported()?
158							.devices()
159							.names()
160							.an_iphone()
161							.ok_or_else(|| eyre!("Couldn't find any iPhone simulators installed"))?)
162						.into();
163						info!(device_name = %a_device_name, "Since auto-boot is enabled, booting an iPhone simulator");
164						simctl_instance.boot(&a_device_name)?.success()?;
165						Ok(a_device_name)
166					}
167				}
168				(false, false, false, Some(name), auto_boot) => {
169					let output = list.get_success_reported()?;
170					let devices = output.devices().names().collect::<Vec<_>>();
171					if !devices.iter().any(|n| n == &&name) {
172						Err(
173							eyre!("The provided device name is not installed")
174								.with_suggestion(|| {
175									format!(
176										"try running `apple-clis xcrun simctl boot --name {}` to boot a simulator",
177										name
178									)
179								})
180								.with_suggestion(|| {
181									"try running `apple-clis xcrun simctl list` to see installed simulators"
182								})
183								.with_note(|| format!("Installed devices: {:?}", &devices)),
184						)
185					} else if auto_boot {
186						simctl_instance.boot(&name)?.success()?;
187						Ok(name)
188					} else {
189						Ok(name)
190					}
191				}
192				_ => Err(eyre!("Clap arguments should prevent this")),
193			}
194		}
195	}
196}