apple_clis/shared/identifiers/
screen_size.rs

1use std::hash::Hash;
2
3use crate::prelude::*;
4
5pub use maybe::MaybeScreenSize;
6
7mod maybe {
8	use crate::prelude::*;
9
10	/// Wrapper around Option<[ScreenSize]> for easy of [Display] impl.
11	/// The display impl *includes a prefix space*.
12	#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13	pub struct MaybeScreenSize(Option<ScreenSize>);
14
15	impl MaybeScreenSize {
16		pub fn as_ref(&self) -> Option<&ScreenSize> {
17			self.0.as_ref()
18		}
19
20		pub fn get(&self) -> Option<&ScreenSize> {
21			self.as_ref()
22		}
23	}
24
25	impl Display for MaybeScreenSize {
26		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27			if let Some(screen_size) = self.0.as_ref() {
28				write!(f, " {}", screen_size)
29			} else {
30				Ok(())
31			}
32		}
33	}
34
35	impl NomFromStr for MaybeScreenSize {
36		fn nom_from_str(input: &str) -> IResult<&str, Self> {
37			map(opt(ScreenSize::nom_from_str), MaybeScreenSize)(input)
38		}
39	}
40
41	#[cfg(test)]
42	mod test {
43	}
44}
45
46#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord)]
47pub struct ScreenSize {
48	/// divide by 10 to get number of actual inches
49	ten_inches: u16,
50	/// whether to show \" or -inch
51	/// used to differentiate "11\"" and "11-inch"
52	short: bool,
53	/// whether to show brackets
54	/// used to differentiate "(11-inch)" and "11-inch"
55	brackets: bool,
56}
57
58impl PartialEq for ScreenSize {
59	fn eq(&self, other: &Self) -> bool {
60		self.ten_inches == other.ten_inches
61	}
62}
63
64impl Hash for ScreenSize {
65	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
66		self.ten_inches.hash(state);
67	}
68}
69
70impl ScreenSize {
71	/// Long format and brackets
72	/// ```rust
73	/// use apple_clis::prelude::*;
74	///
75	/// #[cfg(feature = "unstable-construction")]
76	/// {
77	/// 	let example = "(11-inch)";
78	/// 	let screen_size = ScreenSize::long_brackets(11.0);
79	/// 	assert_eq!(format!("{}", screen_size), example);
80	/// }
81	/// ```
82	#[stability::unstable(feature = "construction")]
83	pub fn long_brackets(inches: f32) -> Self {
84		let inches = inches * 10.0;
85
86		Self {
87			ten_inches: inches as u16,
88			short: false,
89			brackets: true,
90		}
91	}
92
93	/// Long format and no brackets
94	/// ```rust
95	/// use apple_clis::prelude::*;
96	///
97	/// #[cfg(feature = "unstable-construction")]
98	/// {
99	/// 	let example = "11-inch";
100	/// 	let screen_size = ScreenSize::long_bracketless(11.0);
101	/// 	assert_eq!(format!("{}", screen_size), example);
102	/// }
103	/// ```
104	#[stability::unstable(feature = "construction")]
105	pub fn long_bracketless(inches: f32) -> Self {
106		let inches = inches * 10.0;
107
108		Self {
109			ten_inches: inches as u16,
110			short: false,
111			brackets: false,
112		}
113	}
114
115	/// Short format and brackets
116	/// ```rust
117	/// use apple_clis::prelude::*;
118	///
119	/// #[cfg(feature = "unstable-construction")]
120	/// {
121	/// 	let example = "(13\")";
122	/// 	let screen_size = ScreenSize::short_brackets(13.0);
123	/// 	assert_eq!(format!("{}", screen_size), example);
124	/// }
125	/// ```
126	#[stability::unstable(feature = "construction")]
127	pub fn short_brackets(inches: f32) -> Self {
128		let inches = inches * 10.0;
129
130		Self {
131			ten_inches: inches as u16,
132			short: true,
133			brackets: true,
134		}
135	}
136
137	/// Short format and no brackets
138	/// ```rust
139	/// use apple_clis::prelude::*;
140	///
141	/// #[cfg(feature = "unstable-construction")]
142	/// {
143	/// 	let example = "13\"";
144	/// 	let screen_size = ScreenSize::short_bracketless(13.0);
145	/// 	assert_eq!(format!("{}", screen_size), example);
146	/// }
147	/// ```
148	#[stability::unstable(feature = "construction")]
149	pub fn short_bracketless(inches: f32) -> Self {
150		let inches = inches * 10.0;
151
152		Self {
153			ten_inches: inches as u16,
154			short: true,
155			brackets: false,
156		}
157	}
158
159	pub fn inches(&self) -> f32 {
160		self.ten_inches as f32 / 10.0
161	}
162}
163
164fn brackets_long(input: &str) -> IResult<&str, ScreenSize> {
165	delimited(
166		ws(tag("(")),
167		map(float, ScreenSize::long_brackets),
168		ws(tag("-inch)")),
169	)(input)
170}
171
172fn bracketless_long(input: &str) -> IResult<&str, ScreenSize> {
173	terminated(map(float, ScreenSize::long_bracketless), ws(tag("-inch")))(input)
174}
175
176fn brackets_short(input: &str) -> IResult<&str, ScreenSize> {
177	delimited(
178		ws(tag("(")),
179		map(float, ScreenSize::short_brackets),
180		ws(tag("\")")),
181	)(input)
182}
183
184fn bracketless_short(input: &str) -> IResult<&str, ScreenSize> {
185	terminated(map(float, ScreenSize::short_bracketless), ws(tag("\"")))(input)
186}
187
188impl NomFromStr for ScreenSize {
189	#[tracing::instrument(level = "trace", skip(input))]
190	fn nom_from_str(input: &str) -> IResult<&str, Self> {
191		alt((
192			brackets_long,
193			brackets_short,
194			bracketless_long,
195			bracketless_short,
196		))(input)
197	}
198}
199
200impl Display for ScreenSize {
201	#[tracing::instrument(level = "trace", skip(self, f))]
202	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203		match (self.brackets, self.short) {
204			(true, false) => write!(f, "({}-inch)", self.inches()),
205			(true, true) => write!(f, "({}\")", self.inches()),
206			(false, true) => write!(f, "{}\"", self.inches()),
207			(false, false) => write!(f, "{}-inch", self.inches()),
208		}
209	}
210}
211
212#[cfg(test)]
213mod tests {
214	use crate::shared::assert_nom_parses;
215
216	use super::*;
217
218	#[test]
219	fn screen_size_hardcoded() {
220		let examples = ["(5.5-inch)", "(11-inch)", "(6.1\")", "13-inch", "11-inch"];
221		assert_nom_parses::<ScreenSize>(examples, |_| true)
222	}
223
224	#[test]
225	fn screen_size_ordering() {
226		let small = ScreenSize::long_brackets(5.0);
227		let large = ScreenSize::short_brackets(6.0);
228		assert!(large > small);
229
230		let small = ScreenSize::long_brackets(5.0);
231		let large = ScreenSize::short_brackets(6.0);
232		assert!(large > small);
233	}
234}