apple_clis/shared/identifiers/
device_name.rs

1use crate::prelude::*;
2
3/// [Deserialize]s from a [String] representation.
4#[derive(
5	Debug, Clone, PartialEq, derive_more::Display, derive_more::From, Serialize, Deserialize,
6)]
7#[serde(try_from = "String")]
8#[serde(into = "String")]
9pub enum DeviceName {
10	IPhone(IPhoneVariant),
11
12	IPad(IPadVariant),
13
14	#[from(ignore)]
15	#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/inline/TODO.md"))]
16	UnImplemented(String),
17}
18
19impl From<DeviceName> for String {
20	#[tracing::instrument(level = "trace", skip(variant))]
21	fn from(variant: DeviceName) -> Self {
22		variant.to_string()
23	}
24}
25
26impl TryFrom<&str> for DeviceName {
27	type Error = <Self as FromStr>::Err;
28
29	fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
30		value.parse()
31	}
32}
33
34impl TryFrom<String> for DeviceName {
35	type Error = <Self as FromStr>::Err;
36
37	fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
38		value.parse()
39	}
40}
41
42impl_from_str_nom!(DeviceName);
43
44impl From<&IPhoneVariant> for DeviceName {
45	#[tracing::instrument(level = "trace", skip(variant))]
46	fn from(variant: &IPhoneVariant) -> Self {
47		Self::IPhone(*variant)
48	}
49}
50
51impl From<&IPadVariant> for DeviceName {
52	#[tracing::instrument(level = "trace", skip(variant))]
53	fn from(variant: &IPadVariant) -> Self {
54		Self::IPad(*variant)
55	}
56}
57
58impl From<&DeviceName> for DeviceName {
59	#[tracing::instrument(level = "trace", skip(variant))]
60	fn from(variant: &DeviceName) -> Self {
61		variant.clone()
62	}
63}
64
65pub use iphone::*;
66mod iphone;
67
68pub use ipad::*;
69mod ipad;
70
71impl DeviceName {
72	pub fn parsed_successfully(&self) -> bool {
73		!matches!(self, DeviceName::UnImplemented(_))
74	}
75
76	pub fn is_iphone(&self) -> bool {
77		matches!(self, DeviceName::IPhone(_))
78	}
79
80	pub fn is_ipad(&self) -> bool {
81		matches!(self, DeviceName::IPad(_))
82	}
83
84	pub fn get_ipad(&self) -> Option<&IPadVariant> {
85		match self {
86			DeviceName::IPad(ipad) => Some(ipad),
87			_ => None,
88		}
89	}
90
91	pub fn get_iphone(&self) -> Option<&IPhoneVariant> {
92		match self {
93			DeviceName::IPhone(iphone) => Some(iphone),
94			_ => None,
95		}
96	}
97}
98
99impl NomFromStr for DeviceName {
100	fn nom_from_str(input: &str) -> IResult<&str, Self> {
101		alt((
102			map(ws(IPadVariant::nom_from_str), DeviceName::IPad),
103			map(ws(IPhoneVariant::nom_from_str), DeviceName::IPhone),
104			map(rest, |s: &str| DeviceName::UnImplemented(s.to_owned())),
105		))(input)
106	}
107}
108
109#[cfg(test)]
110mod tests {
111	use crate::shared::assert_nom_parses;
112
113	use super::*;
114
115	#[test]
116	fn hardcoded_parse_device_names() {
117		let examples = [
118			"iPad Air 11-inch (M2)",
119			"iPad Pro (11-inch) (4th generation)",
120		];
121		assert_nom_parses::<DeviceName>(examples, |d| d.parsed_successfully());
122	}
123
124	#[test]
125	fn generated_parse_device_name() {
126		let examples = include!("../../../tests/device-names.json");
127		assert_nom_parses::<DeviceName>(examples, |d| d.parsed_successfully())
128	}
129}