creator_simctl/list.rs
1//! Supporting types for the `simctl list` subcommand.
2
3use serde::Deserialize;
4use std::collections::HashMap;
5use std::path::PathBuf;
6use std::process::Stdio;
7
8use super::{Device, Result, Simctl};
9
10/// Indicates the state of a device.
11#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq)]
12pub enum DeviceState {
13 /// Indicates that the device is booted.
14 Booted,
15
16 /// Indicates that the device is shutdown.
17 Shutdown,
18
19 /// Indicates that the device is in an unknown state.
20 #[serde(other)]
21 Unknown,
22}
23
24/// Indicates the state of a pair of devices.
25#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq)]
26pub enum DevicePairState {
27 /// Indicates that this pair is unavailable because one of its components is
28 /// unavailable.
29 #[serde(rename = "(unavailable)")]
30 Unavailable,
31
32 /// Indicates that this pair is active but not connected.
33 #[serde(rename = "(active, disconnected)")]
34 ActiveDisconnected,
35
36 /// Indicates that this pair is in a state that is not (yet) recognized by
37 /// this library.
38 #[serde(other)]
39 Unknown,
40}
41
42/// Information about a device type.
43#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
44pub struct DeviceType {
45 /// Contains the minimum runtime version that this device type supports.
46 /// This is relevant for devices that are newer than the oldest runtime that
47 /// has been registered with `simctl`.
48 #[serde(rename = "minRuntimeVersion")]
49 pub min_runtime_version: usize,
50
51 /// Contains the maximum runtime version that this device type supports.
52 /// This is relevant for devices that have been deprecated before the newest
53 /// runtime that has been registered with `simctl`.
54 #[serde(rename = "maxRuntimeVersion")]
55 pub max_runtime_version: usize,
56
57 /// Contains a path to the bundle of this device type. This is usually not
58 /// relevant to end-users.
59 #[serde(rename = "bundlePath")]
60 pub bundle_path: PathBuf,
61
62 /// Contains a human-readable name for this device type.
63 pub name: String,
64
65 /// Contains a unique identifier for this device type.
66 pub identifier: String,
67
68 /// Contains a machine-readable name for the product family of this device
69 /// type.
70 #[serde(rename = "productFamily")]
71 pub product_family: String,
72}
73
74/// Information about a runtime.
75#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
76pub struct Runtime {
77 /// Contains a path to the bundle of this runtime. This is usually not
78 /// relevant to end-users.
79 #[serde(rename = "bundlePath")]
80 pub bundle_path: PathBuf,
81
82 /// Contains the build version of this runtime. This is usually not relevant
83 /// to end-users.
84 #[serde(rename = "buildversion")]
85 pub build_version: String,
86
87 /// Contains the root of this runtime. This is usually not relevant to
88 /// end-users.
89 #[serde(rename = "runtimeRoot")]
90 pub runtime_root: PathBuf,
91
92 /// Contains a unique identifier for this runtime.
93 pub identifier: String,
94
95 /// Contains a human-readable version string for this runtime.
96 pub version: String,
97
98 /// Indicates if this runtime is available. This is false when the runtime
99 /// was first created (automatically) with an older version of Xcode that
100 /// shipped with an older version of the iOS simulator and after upgrading
101 /// to a newer version. In that case, Xcode no longer has the runtime bundle
102 /// for this older runtime, but it will still be registered by `simctl`.
103 /// However, it's not possible to boot a device with an unavailable runtime.
104 #[serde(rename = "isAvailable")]
105 pub is_available: bool,
106
107 /// Contains a human-readable name for this runtime.
108 pub name: String,
109}
110
111/// Information about a device.
112#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
113pub struct DeviceInfo {
114 /// Note: this field is not directly present in JSON. Instead, the JSON
115 /// representation is a hashmap of runtime IDs (keys) and devices (values)
116 /// that we later connect during deserialization.
117 #[serde(skip_deserializing)]
118 pub runtime_identifier: String,
119
120 /// If this device is not available (see [`DeviceInfo::is_available`]), this
121 /// will contain a (slightly) more detailed explanation for its
122 /// unavailability.
123 #[serde(default, rename = "availabilityError")]
124 pub availability_error: Option<String>,
125
126 /// Contains the path where application data is stored.
127 #[serde(rename = "dataPath")]
128 pub data_path: PathBuf,
129
130 /// Contains the path where logs are written to.
131 #[serde(rename = "logPath")]
132 pub log_path: PathBuf,
133
134 /// Contains a unique identifier for this device.
135 pub udid: String,
136
137 /// Indicates if this device is available. Also see
138 /// [`Runtime::is_available`].
139 #[serde(rename = "isAvailable")]
140 pub is_available: bool,
141
142 /// This corresponds to [`DeviceType::identifier`]. This is missing for
143 /// devices whose device type has since been removed from Xcode.
144 #[serde(default, rename = "deviceTypeIdentifier")]
145 pub device_type_identifier: String,
146
147 /// Contains the state of this device.
148 pub state: DeviceState,
149
150 /// Contains the name of this device.
151 pub name: String,
152}
153
154/// Short summary of a device that is used as part of a device pair.
155#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
156pub struct DeviceSummary {
157 /// Contains the name of this device.
158 pub name: String,
159
160 /// Contains a unique identifier for this device.
161 pub udid: String,
162
163 /// Contains the state of this device.
164 pub state: DeviceState,
165}
166
167/// Information about a device pair.
168#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
169pub struct DevicePair {
170 /// Note: this field is not directly present in JSON. Instead, the JSON
171 /// representation is a hashmap of runtime IDs (keys) and devices (values)
172 /// that we later connect during deserialization.
173 #[serde(skip_deserializing)]
174 pub udid: String,
175
176 /// Contains a summary of the watch device.
177 pub watch: DeviceSummary,
178
179 /// Contains a summary of the phone device.
180 pub phone: DeviceSummary,
181
182 /// Contains the state of this device pair.
183 pub state: DevicePairState,
184}
185
186/// Wrapper around the `simctl list` subcommand's output.
187#[derive(Debug)]
188pub struct List {
189 simctl: Simctl,
190 device_types: Vec<DeviceType>,
191 runtimes: Vec<Runtime>,
192 devices: Vec<Device>,
193 pairs: Vec<DevicePair>,
194}
195
196impl List {
197 /// Refreshes the `simctl list` subcommand's output.
198 pub fn refresh(&mut self) -> Result<()> {
199 let mut command = self.simctl.command("list");
200 command.arg("-j");
201 command.stdout(Stdio::piped());
202 let output = command.output()?;
203 let output: ListOutput = serde_json::from_slice(&output.stdout)?;
204 self.device_types = output.device_types;
205 self.runtimes = output.runtimes;
206 self.devices = output
207 .devices
208 .into_iter()
209 .map(|(runtime, devices)| {
210 let simctl = self.simctl.clone();
211
212 devices.into_iter().map(move |device| {
213 Device::new(
214 simctl.clone(),
215 DeviceInfo {
216 runtime_identifier: runtime.clone(),
217 ..device
218 },
219 )
220 })
221 })
222 .flatten()
223 .collect();
224 self.pairs = output
225 .pairs
226 .into_iter()
227 .map(move |(udid, pair)| DevicePair { udid, ..pair })
228 .collect();
229 Ok(())
230 }
231
232 /// Returns all device types that have been registered with `simctl`.
233 pub fn device_types(&self) -> &[DeviceType] {
234 &self.device_types
235 }
236
237 /// Returns all runtimes that have been registered with `simctl`.
238 pub fn runtimes(&self) -> &[Runtime] {
239 &self.runtimes
240 }
241
242 /// Returns all devices that have been registered with `simctl`.
243 pub fn devices(&self) -> &[Device] {
244 &self.devices
245 }
246
247 /// Returns all device pairs that have been registered with `simctl`.
248 pub fn pairs(&self) -> &[DevicePair] {
249 &self.pairs
250 }
251}
252
253#[derive(Debug, Default, Deserialize)]
254struct ListOutput {
255 #[serde(rename = "devicetypes")]
256 device_types: Vec<DeviceType>,
257 runtimes: Vec<Runtime>,
258 devices: HashMap<String, Vec<DeviceInfo>>,
259 pairs: HashMap<String, DevicePair>,
260}
261
262impl Simctl {
263 /// Returns a list of all device types, runtimes, devices and device pairs
264 /// that have been registered with `simctl`.
265 pub fn list(&self) -> Result<List> {
266 let mut list = List {
267 simctl: self.clone(),
268 device_types: vec![],
269 devices: vec![],
270 pairs: vec![],
271 runtimes: vec![],
272 };
273 list.refresh()?;
274 Ok(list)
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn test_list() -> Result<()> {
284 let simctl = Simctl::new();
285 let _ = simctl.list()?;
286 Ok(())
287 }
288}