holodeck_simctl_core/models/
runtime.rs1use std::cmp::Ordering;
2
3use super::platform::Platform;
4use super::semantic_version::SemanticVersion;
5use super::simctl_identifiers::RUNTIME_PREFIX;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct Runtime {
9 pub platform: Platform,
10 pub version: SemanticVersion,
11 pub identifier: String,
12}
13
14impl Runtime {
15 pub fn new(platform: Platform, version: SemanticVersion, identifier: impl Into<String>) -> Self {
16 Self { platform, version, identifier: identifier.into() }
17 }
18
19 pub fn from_identifier(identifier: &str) -> Option<Self> {
20 let suffix = identifier.strip_prefix(RUNTIME_PREFIX)?;
21 let dash = suffix.find('-')?;
22 let platform_name = &suffix[..dash];
23 let version_part = suffix[dash + 1..].replace('-', ".");
24 let platform = Platform::from_simctl_name(platform_name)?;
25 let version = SemanticVersion::parse(&version_part)?;
26 Some(Self { platform, version, identifier: identifier.to_string() })
27 }
28
29 pub fn from_identifier_with_version(identifier: &str, version_string: Option<&str>) -> Option<Self> {
33 let parsed = Self::from_identifier(identifier)?;
34 let resolved_version = version_string.and_then(SemanticVersion::parse).unwrap_or(parsed.version);
35 Some(Self { platform: parsed.platform, version: resolved_version, identifier: identifier.to_string() })
36 }
37
38 pub fn display_name(&self) -> String {
39 format!("{} {}", self.platform.raw_value(), self.version)
40 }
41}
42
43impl PartialOrd for Runtime {
44 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
45 Some(self.cmp(other))
46 }
47}
48
49impl Ord for Runtime {
50 fn cmp(&self, other: &Self) -> Ordering {
51 self.platform.raw_value().cmp(other.platform.raw_value()).then(self.version.cmp(&other.version))
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn parses_identifier() {
61 let runtime = Runtime::from_identifier("com.apple.CoreSimulator.SimRuntime.iOS-18-0").unwrap();
62 assert_eq!(runtime.platform, Platform::IOS);
63 assert_eq!(runtime.version, SemanticVersion::new(18, 0, 0));
64 }
65
66 #[test]
67 fn version_string_overrides_identifier_derived_version() {
68 let runtime =
69 Runtime::from_identifier_with_version("com.apple.CoreSimulator.SimRuntime.iOS-26-4", Some("26.4.1")).unwrap();
70 assert_eq!(runtime.version, SemanticVersion::new(26, 4, 1));
71 }
72
73 #[test]
74 fn falls_back_to_identifier_version_when_string_absent() {
75 let runtime = Runtime::from_identifier_with_version("com.apple.CoreSimulator.SimRuntime.iOS-26-4", None).unwrap();
76 assert_eq!(runtime.version, SemanticVersion::new(26, 4, 0));
77 }
78
79 #[test]
80 fn display_name_combines_platform_and_version() {
81 let runtime = Runtime::from_identifier("com.apple.CoreSimulator.SimRuntime.iOS-18-0").unwrap();
82 assert_eq!(runtime.display_name(), "iOS 18.0");
83 }
84}