xref: /aosp_15_r20/external/cronet/third_party/rust/chromium_crates_io/vendor/fend-core-1.4.6/src/units.rs (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 use std::borrow::Cow;
2 
3 use crate::error::{FendError, Interrupt};
4 use crate::eval::evaluate_to_value;
5 use crate::num::Number;
6 use crate::result::FResult;
7 use crate::value::Value;
8 use crate::Attrs;
9 
10 mod builtin;
11 
12 pub(crate) use builtin::lookup_default_unit;
13 pub(crate) use builtin::IMPLICIT_UNIT_MAP;
14 
15 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
16 pub(crate) enum PrefixRule {
17 	NoPrefixesAllowed,
18 	LongPrefixAllowed,
19 	LongPrefix,
20 	ShortPrefixAllowed,
21 	ShortPrefix,
22 }
23 
24 #[derive(Debug)]
25 pub(crate) struct UnitDef {
26 	singular: Cow<'static, str>,
27 	plural: Cow<'static, str>,
28 	prefix_rule: PrefixRule,
29 	alias: bool,
30 	value: Value,
31 }
32 
expr_unit<I: Interrupt>( unit_def: (Cow<'static, str>, Cow<'static, str>, Cow<'static, str>), attrs: Attrs, context: &mut crate::Context, int: &I, ) -> FResult<UnitDef>33 fn expr_unit<I: Interrupt>(
34 	unit_def: (Cow<'static, str>, Cow<'static, str>, Cow<'static, str>),
35 	attrs: Attrs,
36 	context: &mut crate::Context,
37 	int: &I,
38 ) -> FResult<UnitDef> {
39 	let (singular, plural, definition) = unit_def;
40 	let mut definition = definition.trim();
41 	if definition == "$CURRENCY" {
42 		let Some(exchange_rate_fn) = &context.get_exchange_rate else {
43 			return Err(FendError::NoExchangeRatesAvailable);
44 		};
45 		let one_base_in_currency = exchange_rate_fn.relative_to_base_currency(&singular)?;
46 		let value = evaluate_to_value(
47 			format!("(1/{one_base_in_currency}) BASE_CURRENCY").as_str(),
48 			None,
49 			attrs,
50 			context,
51 			int,
52 		)?
53 		.expect_num()?;
54 		let value = Number::create_unit_value_from_value(
55 			&value,
56 			Cow::Borrowed(""),
57 			false,
58 			singular.clone(),
59 			plural.clone(),
60 			int,
61 		)?;
62 		return Ok(UnitDef {
63 			singular,
64 			plural,
65 			prefix_rule: PrefixRule::LongPrefixAllowed,
66 			alias: false,
67 			value: Value::Num(Box::new(value)),
68 		});
69 	}
70 	let mut rule = PrefixRule::NoPrefixesAllowed;
71 	if let Some(remaining) = definition.strip_prefix("l@") {
72 		definition = remaining;
73 		rule = PrefixRule::LongPrefixAllowed;
74 	}
75 	if let Some(remaining) = definition.strip_prefix("lp@") {
76 		definition = remaining;
77 		rule = PrefixRule::LongPrefix;
78 	}
79 	if let Some(remaining) = definition.strip_prefix("s@") {
80 		definition = remaining;
81 		rule = PrefixRule::ShortPrefixAllowed;
82 	}
83 	if let Some(remaining) = definition.strip_prefix("sp@") {
84 		definition = remaining;
85 		rule = PrefixRule::ShortPrefix;
86 	}
87 	if definition == "!" {
88 		return Ok(UnitDef {
89 			value: Value::Num(Box::new(Number::new_base_unit(
90 				singular.clone(),
91 				plural.clone(),
92 			))),
93 			prefix_rule: rule,
94 			singular,
95 			plural,
96 			alias: false,
97 		});
98 	}
99 	let (alias, definition) = definition
100 		.strip_prefix('=')
101 		.map_or((false, definition), |remaining| (true, remaining));
102 	// long prefixes like `hecto` are always treated as aliases
103 	let alias = alias || rule == PrefixRule::LongPrefix;
104 	let mut num = evaluate_to_value(definition, None, attrs, context, int)?.expect_num()?;
105 
106 	// There are three cases to consider:
107 	//   1. Unitless aliases (e.g. `million` or `mega`) should be treated as an
108 	//      actual unit, but with the `alias` flag set so it can be simplified
109 	//      when possible.
110 	//   2. Aliases with units (e.g. `sqft`) should be a pure alias (not a unit)
111 	//      so it can always be replaced. We can't convert this like unitless
112 	//      aliases since we would be simplifying it to base units (scaled m^2),
113 	//      so the precise unit we are aliasing to would be lost.
114 	//   3. Units that aren't aliased (e.g. `meter`) should be converted to a
115 	//      normal unit.
116 	//
117 	// One exception to these cases is `unitless`, which should always be
118 	// replaced with `1` even when we aren't simplifying, so it is defined
119 	// manually instead of as a normal unit definition.
120 
121 	#[allow(clippy::nonminimal_bool)]
122 	if !alias || (alias && num.is_unitless(int)?) {
123 		// convert to an actual unit (cases 1 and 3)
124 		num = Number::create_unit_value_from_value(
125 			&num,
126 			Cow::Borrowed(""),
127 			alias,
128 			singular.clone(),
129 			plural.clone(),
130 			int,
131 		)?;
132 	}
133 	Ok(UnitDef {
134 		value: Value::Num(Box::new(num)),
135 		prefix_rule: rule,
136 		singular,
137 		plural,
138 		alias,
139 	})
140 }
141 
construct_prefixed_unit<I: Interrupt>(a: UnitDef, b: UnitDef, int: &I) -> FResult<Value>142 fn construct_prefixed_unit<I: Interrupt>(a: UnitDef, b: UnitDef, int: &I) -> FResult<Value> {
143 	let product = a.value.expect_num()?.mul(b.value.expect_num()?, int)?;
144 	assert_eq!(a.singular, a.plural);
145 	let unit = Number::create_unit_value_from_value(
146 		&product, a.singular, b.alias, b.singular, b.plural, int,
147 	)?;
148 	Ok(Value::Num(Box::new(unit)))
149 }
150 
query_unit<I: Interrupt>( ident: &str, attrs: Attrs, context: &mut crate::Context, int: &I, ) -> FResult<Value>151 pub(crate) fn query_unit<I: Interrupt>(
152 	ident: &str,
153 	attrs: Attrs,
154 	context: &mut crate::Context,
155 	int: &I,
156 ) -> FResult<Value> {
157 	if ident.starts_with('\'') && ident.ends_with('\'') && ident.len() >= 3 {
158 		let ident = ident.split_at(1).1;
159 		let ident = ident.split_at(ident.len() - 1).0;
160 		return Ok(Value::Num(Box::new(Number::new_base_unit(
161 			ident.to_string().into(),
162 			ident.to_string().into(),
163 		))));
164 	}
165 	query_unit_static(ident, attrs, context, int)
166 }
167 
query_unit_static<I: Interrupt>( ident: &str, attrs: Attrs, context: &mut crate::Context, int: &I, ) -> FResult<Value>168 pub(crate) fn query_unit_static<I: Interrupt>(
169 	ident: &str,
170 	attrs: Attrs,
171 	context: &mut crate::Context,
172 	int: &I,
173 ) -> FResult<Value> {
174 	match query_unit_case_sensitive(ident, true, attrs, context, int) {
175 		Err(FendError::IdentifierNotFound(_)) => (),
176 		Err(e) => return Err(e),
177 		Ok(value) => {
178 			return Ok(value);
179 		}
180 	}
181 	query_unit_case_sensitive(ident, false, attrs, context, int)
182 }
183 
query_unit_case_sensitive<I: Interrupt>( ident: &str, case_sensitive: bool, attrs: Attrs, context: &mut crate::Context, int: &I, ) -> FResult<Value>184 fn query_unit_case_sensitive<I: Interrupt>(
185 	ident: &str,
186 	case_sensitive: bool,
187 	attrs: Attrs,
188 	context: &mut crate::Context,
189 	int: &I,
190 ) -> FResult<Value> {
191 	match query_unit_internal(ident, false, case_sensitive, true, context) {
192 		Err(FendError::IdentifierNotFound(_)) => (),
193 		Err(e) => return Err(e),
194 		Ok(unit_def) => {
195 			// Return value without prefix. Note that lone short prefixes
196 			// won't be returned here.
197 			return Ok(expr_unit(unit_def, attrs, context, int)?.value);
198 		}
199 	}
200 	let mut split_idx = ident.chars().next().unwrap().len_utf8();
201 	while split_idx < ident.len() {
202 		let (prefix, remaining_ident) = ident.split_at(split_idx);
203 		split_idx += remaining_ident.chars().next().unwrap().len_utf8();
204 		let a = match query_unit_internal(prefix, true, case_sensitive, false, context) {
205 			Err(FendError::IdentifierNotFound(_)) => continue,
206 			Err(e) => {
207 				return Err(e);
208 			}
209 			Ok(a) => a,
210 		};
211 		match query_unit_internal(remaining_ident, false, case_sensitive, false, context) {
212 			Err(FendError::IdentifierNotFound(_)) => continue,
213 			Err(e) => return Err(e),
214 			Ok(b) => {
215 				let (a, b) = (
216 					expr_unit(a, attrs, context, int)?,
217 					expr_unit(b, attrs, context, int)?,
218 				);
219 				if (a.prefix_rule == PrefixRule::LongPrefix
220 					&& b.prefix_rule == PrefixRule::LongPrefixAllowed)
221 					|| (a.prefix_rule == PrefixRule::ShortPrefix
222 						&& b.prefix_rule == PrefixRule::ShortPrefixAllowed)
223 				{
224 					// now construct a new unit!
225 					return construct_prefixed_unit(a, b, int);
226 				}
227 				return Err(FendError::IdentifierNotFound(ident.to_string().into()));
228 			}
229 		};
230 	}
231 	Err(FendError::IdentifierNotFound(ident.to_string().into()))
232 }
233 
234 #[allow(clippy::type_complexity)]
query_unit_internal( ident: &str, short_prefixes: bool, case_sensitive: bool, whole_unit: bool, context: &crate::Context, ) -> FResult<(Cow<'static, str>, Cow<'static, str>, Cow<'static, str>)>235 fn query_unit_internal(
236 	ident: &str,
237 	short_prefixes: bool,
238 	case_sensitive: bool,
239 	whole_unit: bool,
240 	context: &crate::Context,
241 ) -> FResult<(Cow<'static, str>, Cow<'static, str>, Cow<'static, str>)> {
242 	if !short_prefixes {
243 		for (s, p, d) in &context.custom_units {
244 			let p = if p.is_empty() { s } else { p };
245 			if (ident == s || ident == p)
246 				|| (!case_sensitive
247 					&& (s.eq_ignore_ascii_case(ident) || p.eq_ignore_ascii_case(ident)))
248 			{
249 				return Ok((
250 					s.to_string().into(),
251 					p.to_string().into(),
252 					d.to_string().into(),
253 				));
254 			}
255 		}
256 	}
257 	if whole_unit && context.fc_mode == crate::FCMode::CelsiusFahrenheit {
258 		if ident == "C" {
259 			return Ok((
260 				Cow::Borrowed("C"),
261 				Cow::Borrowed("C"),
262 				Cow::Borrowed("=\u{b0}C"),
263 			));
264 		} else if ident == "F" {
265 			return Ok((
266 				Cow::Borrowed("F"),
267 				Cow::Borrowed("F"),
268 				Cow::Borrowed("=\u{b0}F"),
269 			));
270 		}
271 	}
272 	if let Some(unit_def) = builtin::query_unit(ident, short_prefixes, case_sensitive) {
273 		Ok(unit_def)
274 	} else {
275 		Err(FendError::IdentifierNotFound(ident.to_string().into()))
276 	}
277 }
278 
get_completions_for_prefix(prefix: &str) -> Vec<crate::Completion>279 pub(crate) fn get_completions_for_prefix(prefix: &str) -> Vec<crate::Completion> {
280 	use crate::Completion;
281 
282 	let mut result = vec![];
283 
284 	let mut add = |name: &str| {
285 		if name.starts_with(prefix) && name != prefix {
286 			result.push(Completion {
287 				display: name.to_string(),
288 				insert: name.split_at(prefix.len()).1.to_string(),
289 			});
290 		}
291 	};
292 
293 	for group in builtin::ALL_UNIT_DEFS {
294 		for (s, _, _, _) in *group {
295 			// only add singular name, since plurals
296 			// unnecessarily clutter autocompletions
297 			add(s);
298 		}
299 	}
300 
301 	result.sort_by(|a, b| a.display().cmp(b.display()));
302 
303 	result
304 }
305