1 use std::cmp::Ordering;
2 use std::{borrow::Cow, collections::HashMap, fmt, io};
3 
4 use super::base_unit::BaseUnit;
5 use crate::num::complex::Complex;
6 use crate::result::FResult;
7 use crate::serialize::{Deserialize, Serialize};
8 use crate::Interrupt;
9 
10 /// A named unit, like kilogram, megabyte or percent.
11 #[derive(Clone)]
12 pub(crate) struct NamedUnit {
13 	prefix: Cow<'static, str>,
14 	pub(super) singular_name: Cow<'static, str>,
15 	plural_name: Cow<'static, str>,
16 	alias: bool,
17 	pub(super) base_units: HashMap<BaseUnit, Complex>,
18 	pub(super) scale: Complex,
19 }
20 
compare_hashmaps<I: Interrupt>( a: &HashMap<BaseUnit, Complex>, b: &HashMap<BaseUnit, Complex>, int: &I, ) -> FResult<bool>21 pub(crate) fn compare_hashmaps<I: Interrupt>(
22 	a: &HashMap<BaseUnit, Complex>,
23 	b: &HashMap<BaseUnit, Complex>,
24 	int: &I,
25 ) -> FResult<bool> {
26 	if a.len() != b.len() {
27 		return Ok(false);
28 	}
29 	for (k, v) in a {
30 		match b.get(k) {
31 			None => return Ok(false),
32 			Some(o) => {
33 				if v.compare(o, int)? != Some(Ordering::Equal) {
34 					return Ok(false);
35 				}
36 			}
37 		}
38 	}
39 	Ok(true)
40 }
41 
42 impl NamedUnit {
new( prefix: Cow<'static, str>, singular_name: Cow<'static, str>, plural_name: Cow<'static, str>, alias: bool, base_units: HashMap<BaseUnit, Complex>, scale: impl Into<Complex>, ) -> Self43 	pub(crate) fn new(
44 		prefix: Cow<'static, str>,
45 		singular_name: Cow<'static, str>,
46 		plural_name: Cow<'static, str>,
47 		alias: bool,
48 		base_units: HashMap<BaseUnit, Complex>,
49 		scale: impl Into<Complex>,
50 	) -> Self {
51 		Self {
52 			prefix,
53 			singular_name,
54 			plural_name,
55 			alias,
56 			base_units,
57 			scale: scale.into(),
58 		}
59 	}
60 
serialize(&self, write: &mut impl io::Write) -> FResult<()>61 	pub(crate) fn serialize(&self, write: &mut impl io::Write) -> FResult<()> {
62 		self.prefix.as_ref().serialize(write)?;
63 		self.singular_name.as_ref().serialize(write)?;
64 		self.plural_name.as_ref().serialize(write)?;
65 		self.alias.serialize(write)?;
66 
67 		self.base_units.len().serialize(write)?;
68 		for (a, b) in &self.base_units {
69 			a.serialize(write)?;
70 			b.serialize(write)?;
71 		}
72 
73 		self.scale.serialize(write)?;
74 		Ok(())
75 	}
76 
deserialize(read: &mut impl io::Read) -> FResult<Self>77 	pub(crate) fn deserialize(read: &mut impl io::Read) -> FResult<Self> {
78 		let prefix = String::deserialize(read)?;
79 		let singular_name = String::deserialize(read)?;
80 		let plural_name = String::deserialize(read)?;
81 		let alias = bool::deserialize(read)?;
82 
83 		let len = usize::deserialize(read)?;
84 		let mut hashmap = HashMap::with_capacity(len);
85 		for _ in 0..len {
86 			let k = BaseUnit::deserialize(read)?;
87 			let v = Complex::deserialize(read)?;
88 			hashmap.insert(k, v);
89 		}
90 		Ok(Self {
91 			prefix: Cow::Owned(prefix),
92 			singular_name: Cow::Owned(singular_name),
93 			plural_name: Cow::Owned(plural_name),
94 			alias,
95 			base_units: hashmap,
96 			scale: Complex::deserialize(read)?,
97 		})
98 	}
99 
compare<I: Interrupt>(&self, other: &Self, int: &I) -> FResult<bool>100 	pub(crate) fn compare<I: Interrupt>(&self, other: &Self, int: &I) -> FResult<bool> {
101 		Ok(self.prefix == other.prefix
102 			&& self.singular_name == other.singular_name
103 			&& self.plural_name == other.plural_name
104 			&& self.alias == other.alias
105 			&& self.scale.compare(&other.scale, int)? == Some(Ordering::Equal)
106 			&& compare_hashmaps(&self.base_units, &other.base_units, int)?)
107 	}
108 
new_from_base(base_unit: BaseUnit) -> Self109 	pub(crate) fn new_from_base(base_unit: BaseUnit) -> Self {
110 		Self {
111 			prefix: "".into(),
112 			singular_name: base_unit.name().to_string().into(),
113 			plural_name: base_unit.name().to_string().into(),
114 			alias: false,
115 			base_units: {
116 				let mut base_units = HashMap::new();
117 				base_units.insert(base_unit, 1.into());
118 				base_units
119 			},
120 			scale: 1.into(),
121 		}
122 	}
123 
prefix_and_name(&self, plural: bool) -> (&str, &str)124 	pub(crate) fn prefix_and_name(&self, plural: bool) -> (&str, &str) {
125 		(
126 			self.prefix.as_ref(),
127 			if plural {
128 				self.plural_name.as_ref()
129 			} else {
130 				self.singular_name.as_ref()
131 			},
132 		)
133 	}
134 
has_no_base_units(&self) -> bool135 	pub(crate) fn has_no_base_units(&self) -> bool {
136 		self.base_units.is_empty()
137 	}
138 
is_alias(&self) -> bool139 	pub(crate) fn is_alias(&self) -> bool {
140 		self.alias && self.has_no_base_units()
141 	}
142 
143 	/// Returns whether or not this unit should be printed with a
144 	/// space (between the number and the unit). This should be true for most
145 	/// units like kg or m, but not for % or °
print_with_space(&self) -> bool146 	pub(crate) fn print_with_space(&self) -> bool {
147 		// Alphabetic names like kg or m should have a space,
148 		// while non-alphabetic names like % or ' shouldn't.
149 		// Empty names shouldn't really exist, but they might as well have a space.
150 
151 		// degree symbol
152 		if self.singular_name == "\u{b0}" {
153 			return false;
154 		}
155 
156 		// if it starts with a quote and is more than one character long, print it with a space
157 		if (self.singular_name.starts_with('\'') || self.singular_name.starts_with('\"'))
158 			&& self.singular_name.len() > 1
159 		{
160 			return true;
161 		}
162 
163 		self.singular_name
164 			.chars()
165 			.next()
166 			.map_or(true, |first_char| {
167 				char::is_alphabetic(first_char) || first_char == '\u{b0}'
168 			})
169 	}
170 }
171 
172 impl fmt::Debug for NamedUnit {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result173 	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 		if self.prefix.is_empty() {
175 			write!(f, "{}", self.singular_name)?;
176 		} else {
177 			write!(f, "{}-{}", self.prefix, self.singular_name)?;
178 		}
179 		write!(f, " (")?;
180 		if self.plural_name != self.singular_name {
181 			if self.prefix.is_empty() {
182 				write!(f, "{}, ", self.plural_name)?;
183 			} else {
184 				write!(f, "{}-{}, ", self.prefix, self.plural_name)?;
185 			}
186 		}
187 		write!(f, "= {:?}", self.scale)?;
188 		let mut it = self.base_units.iter().collect::<Vec<_>>();
189 		it.sort_by_key(|(k, _v)| k.name());
190 		for (base_unit, exponent) in &it {
191 			write!(f, " {base_unit:?}")?;
192 			if !exponent.is_definitely_one() {
193 				write!(f, "^{exponent:?}")?;
194 			}
195 		}
196 		write!(f, ")")?;
197 		Ok(())
198 	}
199 }
200