apple_clis/xcrun/simctl/boot/
output.rs

1use crate::prelude::*;
2
3#[derive(Debug, Serialize)]
4#[non_exhaustive]
5#[must_use = include_doc!(must_use_cmd_output)]
6pub enum BootOutput {
7	/// NOT considered an error case, since the simulator is *already* booted.
8	AlreadyBooted,
9
10	#[doc = include_doc!(cmd_success)]
11	SuccessUnImplemented {
12		stdout: String,
13	},
14
15	#[doc = include_doc!(cmd_error)]
16	ErrorUnImplemented {
17		stderr: String,
18	},
19
20	/// Added so a special recommendation of running a fixing command can be displayed:
21	/// ```sh
22	/// sudo rm -rf ~/Library/Developer/CoreSimulator/Caches
23	/// ```
24	/// 
25	/// An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=60):
26	/// Unable to boot the Simulator.
27	/// launchd failed to respond.
28	/// Underlying error (domain=com.apple.SimLaunchHostService.RequestError, code=4):
29	///         Failed to start launchd_sim: could not bind to session, launchd_sim may have crashed or quit responding
30	ErrorLaunchDFailed {
31		stderr: String,
32	}
33}
34
35impl CommandNomParsable for BootOutput {
36	fn success_unimplemented(stdout: String) -> Self {
37		Self::SuccessUnImplemented { stdout }
38	}
39
40	fn error_unimplemented(stderr: String) -> Self {
41		Self::ErrorUnImplemented { stderr }
42	}
43
44	fn errored_nom_from_str(input: &str) -> IResult<&str, Self> {
45		alt((parse_already_booted, parse_launchd_failed))(input)
46	}
47}
48
49impl PublicCommandOutput for BootOutput {
50	/// If successful, the simulator is booted successfully
51	type PrimarySuccess = ();
52
53	fn success(&self) -> Result<&Self::PrimarySuccess> {
54		match self {
55			BootOutput::SuccessUnImplemented { .. } | BootOutput::AlreadyBooted => Ok(&()),
56			BootOutput::ErrorLaunchDFailed { .. } => {
57				Err(Error::output_errored_with_hint(self, "Try running `sudo rm -rf ~/Library/Developer/CoreSimulator/Caches` to fix this issue."))
58			}
59			BootOutput::ErrorUnImplemented { .. } => Err(Error::output_errored(self)),
60		}
61	}
62}
63
64/// Parses [BootOutput::AlreadyBooted]
65fn parse_already_booted(input: &str) -> IResult<&str, BootOutput> {
66	let (remaining, _preamble) = ws(tag("An error was encountered processing the command"))(input)?;
67	let (remaining, domain) =
68		delimited(tag("(domain="), take_till(|c| c == ','), tag(","))(remaining)?;
69	let (remaining, error_code) = delimited(ws(tag("code=")), digit1, ws(tag("):")))(remaining)?;
70	let (_, msg) =
71		all_consuming(ws(tag("Unable to boot device in current state: Booted")))(remaining)?;
72
73	warn!(?domain, ?error_code, ?msg, "Parsed xcrun simctl boot error");
74
75	Ok(("", BootOutput::AlreadyBooted))
76}
77
78/// Parses [BootOutput::ErrorLaunchDFailed]
79/// An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=60):
80/// Unable to boot the Simulator.
81/// launchd failed to respond.
82fn parse_launchd_failed(input: &str) -> IResult<&str, BootOutput> {
83	let (remaining, _preamble) = ws(tag("An error was encountered processing the command"))(input)?;
84	let (remaining, domain) =
85		delimited(tag("(domain="), take_till(|c| c == ','), tag(","))(remaining)?;
86	let (remaining, error_code) = delimited(ws(tag("code=")), digit1, ws(tag("):")))(remaining)?;
87	let (underlying_error, msg) = ws(tag("launchd failed to respond."))(remaining)?;
88
89	warn!(?domain, ?error_code, ?msg, ?underlying_error, "Parsed xcrun simctl boot error");
90
91	Ok(("", BootOutput::ErrorLaunchDFailed { stderr: input.into() }))
92}