1use std::fmt;
55
56#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct ParsePlatformError(String);
72
73impl ParsePlatformError {
74 pub(crate) fn new(s: impl fmt::Display) -> Self {
75 Self(s.to_string())
76 }
77}
78
79impl fmt::Display for ParsePlatformError {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 write!(f, "unknown Apple platform: {:?}", self.0)
82 }
83}
84
85impl std::error::Error for ParsePlatformError {}
86
87#[derive(Clone, Copy, PartialEq, Eq)]
95pub struct Platform {
96 pub platform: &'static str,
98 pub id: u32,
100 pub name: &'static str,
102 pub build_name: &'static str,
104 pub target: &'static str,
106 pub tapi_target: &'static str,
108 pub marketing: &'static str,
110 pub sdk: Option<&'static str>,
113}
114
115impl fmt::Debug for Platform {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 f.debug_struct("Platform")
118 .field("platform", &self.platform)
119 .field("id", &self.id)
120 .field("name", &self.name)
121 .field("build_name", &self.build_name)
122 .field("target", &self.target)
123 .field("tapi_target", &self.tapi_target)
124 .field("marketing", &self.marketing)
125 .field("sdk", &self.sdk)
126 .finish()
127 }
128}
129
130impl fmt::Display for Platform {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(f, "{} (id={}, target={})", self.marketing, self.id, self.target)
133 }
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
144pub enum ApplePlatform {
145 Unknown = 0,
146 MacOS = 1,
147 IOS = 2,
148 TvOS = 3,
149 WatchOS = 4,
150 BridgeOS = 5,
151 MacCatalyst = 6,
152 IOSSimulator = 7,
153 TvOSSimulator = 8,
154 WatchOSSimulator = 9,
155 DriverKit = 10,
156 XrOS = 11,
157 XrOSSimulator = 12,
158}
159
160impl Default for ApplePlatform {
161 fn default() -> Self {
162 Self::Unknown
163 }
164}
165
166impl fmt::Display for ApplePlatform {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 write!(f, "{}", self.metadata().marketing)
169 }
170}
171
172impl ApplePlatform {
173 pub const ALL: [ApplePlatform; 13] = [
175 Self::Unknown,
176 Self::MacOS,
177 Self::IOS,
178 Self::TvOS,
179 Self::WatchOS,
180 Self::BridgeOS,
181 Self::MacCatalyst,
182 Self::IOSSimulator,
183 Self::TvOSSimulator,
184 Self::WatchOSSimulator,
185 Self::DriverKit,
186 Self::XrOS,
187 Self::XrOSSimulator,
188 ];
189
190 pub fn iter() -> impl Iterator<Item = ApplePlatform> {
192 Self::ALL.into_iter()
193 }
194
195 #[inline]
197 pub fn metadata(self) -> Platform {
198 PLATFORMS[self as usize]
199 }
200
201 #[inline]
203 pub fn id(self) -> u32 {
204 self.metadata().id
205 }
206
207 pub fn from_id(id: u32) -> Option<Self> {
214 match id {
215 0 => Some(Self::Unknown),
216 1 => Some(Self::MacOS),
217 2 => Some(Self::IOS),
218 3 => Some(Self::TvOS),
219 4 => Some(Self::WatchOS),
220 5 => Some(Self::BridgeOS),
221 6 => Some(Self::MacCatalyst),
222 7 => Some(Self::IOSSimulator),
223 8 => Some(Self::TvOSSimulator),
224 9 => Some(Self::WatchOSSimulator),
225 10 => Some(Self::DriverKit),
226 11 => Some(Self::XrOS),
227 12 => Some(Self::XrOSSimulator),
228 _ => None,
229 }
230 }
231
232 pub fn from_rust_triple(target: &str) -> Result<Self, ParsePlatformError> {
243 match target {
244 "aarch64-apple-darwin" | "x86_64-apple-darwin"
246 => Ok(Self::MacOS),
247 "aarch64-apple-ios" | "armv7-apple-ios" | "armv7s-apple-ios"
249 => Ok(Self::IOS),
250 "aarch64-apple-ios-sim" | "x86_64-apple-ios" | "i386-apple-ios"
252 => Ok(Self::IOSSimulator),
253 "aarch64-apple-ios-macabi" | "x86_64-apple-ios-macabi"
255 => Ok(Self::MacCatalyst),
256 "aarch64-apple-tvos"
258 => Ok(Self::TvOS),
259 "aarch64-apple-tvos-sim" | "x86_64-apple-tvos"
260 => Ok(Self::TvOSSimulator),
261 "aarch64-apple-watchos" | "armv7k-apple-watchos" | "arm64_32-apple-watchos"
263 => Ok(Self::WatchOS),
264 "aarch64-apple-watchos-sim" | "x86_64-apple-watchos-sim"
265 => Ok(Self::WatchOSSimulator),
266 "aarch64-apple-visionos"
268 => Ok(Self::XrOS),
269 "aarch64-apple-visionos-sim"
270 => Ok(Self::XrOSSimulator),
271 "aarch64-apple-driverkit" | "x86_64-apple-driverkit"
273 => Ok(Self::DriverKit),
274 _ => Err(ParsePlatformError::new(target)),
275 }
276 }
277
278 #[inline]
296 pub fn sdk(self) -> Option<&'static str> {
297 self.metadata().sdk
298 }
299
300 pub fn is_simulator(self) -> bool {
304 matches!(
305 self,
306 Self::IOSSimulator | Self::TvOSSimulator | Self::WatchOSSimulator | Self::XrOSSimulator
307 )
308 }
309
310 pub fn is_device(self) -> bool {
312 !self.is_simulator() && self != Self::Unknown
313 }
314
315 pub fn device(self) -> Self {
325 match self {
326 Self::IOSSimulator => Self::IOS,
327 Self::TvOSSimulator => Self::TvOS,
328 Self::WatchOSSimulator => Self::WatchOS,
329 Self::XrOSSimulator => Self::XrOS,
330 other => other,
331 }
332 }
333
334 pub fn simulator(self) -> Option<Self> {
347 match self {
348 Self::IOS => Some(Self::IOSSimulator),
349 Self::TvOS => Some(Self::TvOSSimulator),
350 Self::WatchOS => Some(Self::WatchOSSimulator),
351 Self::XrOS => Some(Self::XrOSSimulator),
352 sim if sim.is_simulator() => Some(sim),
353 _ => None,
354 }
355 }
356}
357
358impl std::str::FromStr for ApplePlatform {
361 type Err = ParsePlatformError;
362
363 fn from_str(s: &str) -> Result<Self, Self::Err> {
364 match s.to_lowercase().as_str() {
365 "macos" | "macosx" | "osx" => Ok(Self::MacOS),
366 "ios" | "iphoneos" => Ok(Self::IOS),
367 "tvos" | "appletvos" => Ok(Self::TvOS),
368 "watchos" => Ok(Self::WatchOS),
369 "bridgeos" => Ok(Self::BridgeOS),
370 "maccatalyst" | "macabi" => Ok(Self::MacCatalyst),
371 "iossimulator" | "iphonesimulator" => Ok(Self::IOSSimulator),
372 "tvossimulator" | "appletvsimulator" => Ok(Self::TvOSSimulator),
373 "watchossimulator" | "watchsimulator" => Ok(Self::WatchOSSimulator),
374 "driverkit" => Ok(Self::DriverKit),
375 "xros" | "visionos" => Ok(Self::XrOS),
376 "xrossimulator" | "xrsimulator" | "visionossimulator" => Ok(Self::XrOSSimulator),
377 "unknown" => Ok(Self::Unknown),
378 _ => Err(ParsePlatformError::new(s)),
379 }
380 }
381}
382
383impl TryFrom<u32> for ApplePlatform {
386 type Error = ParsePlatformError;
387 fn try_from(id: u32) -> Result<Self, Self::Error> {
388 Self::from_id(id).ok_or_else(|| ParsePlatformError::new(id))
389 }
390}
391
392impl TryFrom<usize> for ApplePlatform {
393 type Error = ParsePlatformError;
394 fn try_from(id: usize) -> Result<Self, Self::Error> {
395 u32::try_from(id)
396 .ok()
397 .and_then(Self::from_id)
398 .ok_or_else(|| ParsePlatformError::new(id))
399 }
400}
401
402impl From<ApplePlatform> for Platform {
405 fn from(value: ApplePlatform) -> Self {
406 value.metadata()
407 }
408}
409
410impl TryFrom<Platform> for ApplePlatform {
414 type Error = ParsePlatformError;
415 fn try_from(p: Platform) -> Result<Self, Self::Error> {
416 Self::from_id(p.id).ok_or_else(|| ParsePlatformError::new(p.id))
417 }
418}
419
420const PLATFORMS: [Platform; 13] = [
424 Platform { platform: "UNKNOWN", id: 0, name: "unknown", build_name: "unknown", target: "unknown", tapi_target: "unknown", marketing: "unknown", sdk: None },
425 Platform { platform: "MACOS", id: 1, name: "macos", build_name: "macos", target: "macos", tapi_target: "macos", marketing: "macOS", sdk: Some("macosx") },
426 Platform { platform: "IOS", id: 2, name: "ios", build_name: "ios", target: "ios", tapi_target: "ios", marketing: "iOS", sdk: Some("iphoneos") },
427 Platform { platform: "TVOS", id: 3, name: "tvos", build_name: "tvos", target: "tvos", tapi_target: "tvos", marketing: "tvOS", sdk: Some("appletvos") },
428 Platform { platform: "WATCHOS", id: 4, name: "watchos", build_name: "watchos", target: "watchos", tapi_target: "watchos", marketing: "watchOS", sdk: Some("watchos") },
429 Platform { platform: "BRIDGEOS", id: 5, name: "bridgeos", build_name: "bridgeos", target: "bridgeos", tapi_target: "bridgeos", marketing: "bridgeOS", sdk: Some("bridgeos") },
430 Platform { platform: "MACCATALYST", id: 6, name: "macCatalyst", build_name: "macCatalyst", target: "ios-macabi", tapi_target: "maccatalyst", marketing: "macCatalyst", sdk: Some("macosx") },
431 Platform { platform: "IOSSIMULATOR", id: 7, name: "iossimulator", build_name: "iossimulator", target: "ios-simulator", tapi_target: "ios-simulator", marketing: "iOS Simulator", sdk: Some("iphonesimulator") },
432 Platform { platform: "TVOSSIMULATOR", id: 8, name: "tvossimulator", build_name: "tvossimulator", target: "tvos-simulator", tapi_target: "tvos-simulator", marketing: "tvOS Simulator", sdk: Some("appletvsimulator") },
433 Platform { platform: "WATCHOSSIMULATOR", id: 9, name: "watchossimulator", build_name: "watchossimulator", target: "watchos-simulator", tapi_target: "watchos-simulator", marketing: "watchOS Simulator", sdk: Some("watchsimulator") },
434 Platform { platform: "DRIVERKIT", id: 10, name: "driverkit", build_name: "driverkit", target: "driverkit", tapi_target: "driverkit", marketing: "DriverKit", sdk: Some("driverkit") },
435 Platform { platform: "XROS", id: 11, name: "xros", build_name: "xros", target: "xros", tapi_target: "xros", marketing: "visionOS", sdk: Some("xros") },
436 Platform { platform: "XROS_SIMULATOR", id: 12, name: "xrsimulator", build_name: "xrsimulator", target: "xros-simulator", tapi_target: "xros-simulator", marketing: "visionOS Simulator", sdk: Some("xrsimulator") },
437];
438
439#[cfg(test)]
442mod tests {
443 use super::*;
444
445 #[test]
446 fn all_platforms() {
447 let names: Vec<&str> = ApplePlatform::iter()
448 .map(|p| p.metadata().platform)
449 .collect();
450 assert_eq!(names, vec![
451 "UNKNOWN", "MACOS", "IOS", "TVOS", "WATCHOS", "BRIDGEOS",
452 "MACCATALYST", "IOSSIMULATOR", "TVOSSIMULATOR", "WATCHOSSIMULATOR",
453 "DRIVERKIT", "XROS", "XROS_SIMULATOR",
454 ]);
455 }
456
457 #[test]
458 fn platform_ids() {
459 assert_eq!(ApplePlatform::MacOS.id(), 1);
460 assert_eq!(ApplePlatform::IOS.id(), 2);
461 assert_eq!(ApplePlatform::XrOS.id(), 11);
462 }
463
464 #[test]
465 fn from_id_known() {
466 assert_eq!(ApplePlatform::from_id(1), Some(ApplePlatform::MacOS));
467 assert_eq!(ApplePlatform::from_id(11), Some(ApplePlatform::XrOS));
468 assert_eq!(ApplePlatform::from_id(0), Some(ApplePlatform::Unknown));
469 }
470
471 #[test]
472 fn from_id_unknown_is_none() {
473 assert_eq!(ApplePlatform::from_id(999), None);
474 assert_eq!(ApplePlatform::from_id(13), None);
475 }
476
477 #[test]
478 fn try_from_u32() {
479 assert_eq!(ApplePlatform::try_from(1u32).unwrap(), ApplePlatform::MacOS);
480 assert!(ApplePlatform::try_from(99u32).is_err());
481 }
482
483 #[test]
484 fn try_from_usize() {
485 assert_eq!(ApplePlatform::try_from(2usize).unwrap(), ApplePlatform::IOS);
486 assert!(ApplePlatform::try_from(999usize).is_err());
487 }
488
489 #[test]
490 fn from_rust_triple() {
491 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-darwin").unwrap(), ApplePlatform::MacOS);
492 assert_eq!(ApplePlatform::from_rust_triple("x86_64-apple-darwin").unwrap(), ApplePlatform::MacOS);
493 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-ios").unwrap(), ApplePlatform::IOS);
494 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-ios-sim").unwrap(), ApplePlatform::IOSSimulator);
495 assert_eq!(ApplePlatform::from_rust_triple("x86_64-apple-ios").unwrap(), ApplePlatform::IOSSimulator);
496 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-ios-macabi").unwrap(), ApplePlatform::MacCatalyst);
497 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-tvos").unwrap(), ApplePlatform::TvOS);
498 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-tvos-sim").unwrap(), ApplePlatform::TvOSSimulator);
499 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-watchos").unwrap(), ApplePlatform::WatchOS);
500 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-watchos-sim").unwrap(), ApplePlatform::WatchOSSimulator);
501 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-visionos").unwrap(), ApplePlatform::XrOS);
502 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-visionos-sim").unwrap(), ApplePlatform::XrOSSimulator);
503 assert_eq!(ApplePlatform::from_rust_triple("aarch64-apple-driverkit").unwrap(), ApplePlatform::DriverKit);
504 assert!(ApplePlatform::from_rust_triple("x86_64-unknown-linux-gnu").is_err());
505 }
506
507 #[test]
508 fn from_str() {
509 assert_eq!("macos".parse::<ApplePlatform>().unwrap(), ApplePlatform::MacOS);
510 assert_eq!("visionos".parse::<ApplePlatform>().unwrap(), ApplePlatform::XrOS);
511 assert_eq!("iphonesimulator".parse::<ApplePlatform>().unwrap(), ApplePlatform::IOSSimulator);
512 let err = "nope".parse::<ApplePlatform>().unwrap_err();
513 assert!(err.to_string().contains("nope"));
514 }
515
516 #[test]
517 fn sdk_names() {
518 assert_eq!(ApplePlatform::MacOS.sdk(), Some("macosx"));
519 assert_eq!(ApplePlatform::IOS.sdk(), Some("iphoneos"));
520 assert_eq!(ApplePlatform::IOSSimulator.sdk(), Some("iphonesimulator"));
521 assert_eq!(ApplePlatform::TvOS.sdk(), Some("appletvos"));
522 assert_eq!(ApplePlatform::TvOSSimulator.sdk(), Some("appletvsimulator"));
523 assert_eq!(ApplePlatform::WatchOS.sdk(), Some("watchos"));
524 assert_eq!(ApplePlatform::WatchOSSimulator.sdk(), Some("watchsimulator"));
525 assert_eq!(ApplePlatform::MacCatalyst.sdk(), Some("macosx"));
526 assert_eq!(ApplePlatform::XrOS.sdk(), Some("xros"));
527 assert_eq!(ApplePlatform::XrOSSimulator.sdk(), Some("xrsimulator"));
528 assert_eq!(ApplePlatform::DriverKit.sdk(), Some("driverkit"));
529 assert_eq!(ApplePlatform::Unknown.sdk(), None);
530 }
531
532 #[test]
533 fn tapi_targets_match_llvm() {
534 assert_eq!(ApplePlatform::MacCatalyst.metadata().tapi_target, "maccatalyst");
535 assert_eq!(ApplePlatform::IOSSimulator.metadata().tapi_target, "ios-simulator");
536 assert_eq!(ApplePlatform::XrOSSimulator.metadata().tapi_target, "xros-simulator");
537 }
538
539 #[test]
540 fn simulator_helpers() {
541 assert!(ApplePlatform::IOSSimulator.is_simulator());
542 assert!(!ApplePlatform::IOS.is_simulator());
543 assert!(ApplePlatform::IOS.is_device());
544 assert!(!ApplePlatform::IOSSimulator.is_device());
545 assert!(!ApplePlatform::Unknown.is_device());
546 }
547
548 #[test]
549 fn device_method() {
550 assert_eq!(ApplePlatform::IOSSimulator.device(), ApplePlatform::IOS);
551 assert_eq!(ApplePlatform::XrOSSimulator.device(), ApplePlatform::XrOS);
552 assert_eq!(ApplePlatform::IOS.device(), ApplePlatform::IOS);
553 assert_eq!(ApplePlatform::MacOS.device(), ApplePlatform::MacOS);
554 }
555
556 #[test]
557 fn simulator_method() {
558 assert_eq!(ApplePlatform::IOS.simulator(), Some(ApplePlatform::IOSSimulator));
559 assert_eq!(ApplePlatform::TvOS.simulator(), Some(ApplePlatform::TvOSSimulator));
560 assert_eq!(ApplePlatform::WatchOS.simulator(), Some(ApplePlatform::WatchOSSimulator));
561 assert_eq!(ApplePlatform::XrOS.simulator(), Some(ApplePlatform::XrOSSimulator));
562 assert_eq!(ApplePlatform::IOSSimulator.simulator(), Some(ApplePlatform::IOSSimulator));
564 assert_eq!(ApplePlatform::MacOS.simulator(), None);
566 assert_eq!(ApplePlatform::DriverKit.simulator(), None);
567 assert_eq!(ApplePlatform::BridgeOS.simulator(), None);
568 }
569
570 #[test]
571 fn ordering() {
572 assert!(ApplePlatform::MacOS < ApplePlatform::IOS);
573 assert!(ApplePlatform::IOS < ApplePlatform::IOSSimulator);
574 let mut platforms = vec![ApplePlatform::XrOS, ApplePlatform::MacOS, ApplePlatform::IOS];
575 platforms.sort();
576 assert_eq!(platforms, vec![ApplePlatform::MacOS, ApplePlatform::IOS, ApplePlatform::XrOS]);
577 }
578
579 #[test]
580 fn display() {
581 assert_eq!(format!("{}", ApplePlatform::MacOS), "macOS");
582 assert_eq!(format!("{}", ApplePlatform::XrOS), "visionOS");
583 }
584
585 #[test]
586 fn marketing_names() {
587 assert_eq!(ApplePlatform::XrOS.metadata().marketing, "visionOS");
588 assert_eq!(ApplePlatform::XrOSSimulator.metadata().marketing, "visionOS Simulator");
589 }
590
591 #[test]
592 fn from_into_platform() {
593 let p = Platform::from(ApplePlatform::MacOS);
594 assert_eq!(p.id, 1);
595 let back = ApplePlatform::try_from(p).unwrap();
596 assert_eq!(back, ApplePlatform::MacOS);
597 }
598}