xref: /aosp_15_r20/external/cronet/third_party/rust/chromium_crates_io/vendor/fend-core-1.4.6/src/lib.rs (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 #![forbid(unsafe_code)]
2 #![deny(clippy::all)]
3 #![deny(clippy::pedantic)]
4 #![deny(clippy::use_self)]
5 #![forbid(clippy::needless_borrow)]
6 #![forbid(unreachable_pub)]
7 #![forbid(elided_lifetimes_in_paths)]
8 #![allow(clippy::tabs_in_doc_comments)]
9 
10 //! This library implements most of the features of [fend](https://github.com/printfn/fend).
11 //!
12 //! ## Example
13 //!
14 //! ```rust
15 //! extern crate fend_core;
16 //!
17 //! fn main() {
18 //!     let mut context = fend_core::Context::new();
19 //!     let result = fend_core::evaluate("1 + 1", &mut context).unwrap();
20 //!     assert_eq!(result.get_main_result(), "2");
21 //! }
22 //! ```
23 
24 mod ast;
25 mod date;
26 mod error;
27 mod eval;
28 mod format;
29 mod ident;
30 mod inline_substitutions;
31 mod interrupt;
32 /// This module is not meant to be used by other crates. It may change or be removed at any point.
33 pub mod json;
34 mod lexer;
35 mod num;
36 mod parser;
37 mod result;
38 mod scope;
39 mod serialize;
40 mod units;
41 mod value;
42 
43 use std::sync::Arc;
44 use std::{collections::HashMap, fmt, io};
45 
46 use error::FendError;
47 pub(crate) use eval::Attrs;
48 pub use interrupt::Interrupt;
49 use result::FResult;
50 use serialize::{Deserialize, Serialize};
51 
52 /// This contains the result of a computation.
53 #[derive(PartialEq, Eq, Debug)]
54 pub struct FendResult {
55 	plain_result: String,
56 	span_result: Vec<Span>,
57 	is_unit: bool, // is this the () type
58 	attrs: eval::Attrs,
59 }
60 
61 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
62 #[non_exhaustive]
63 pub enum SpanKind {
64 	Number,
65 	BuiltInFunction,
66 	Keyword,
67 	String,
68 	Date,
69 	Whitespace,
70 	Ident,
71 	Boolean,
72 	Other,
73 }
74 
75 #[derive(Clone, Debug, PartialEq, Eq)]
76 struct Span {
77 	string: String,
78 	kind: SpanKind,
79 }
80 
81 impl Span {
from_string(s: String) -> Self82 	fn from_string(s: String) -> Self {
83 		Self {
84 			string: s,
85 			kind: SpanKind::Other,
86 		}
87 	}
88 }
89 
90 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
91 pub struct SpanRef<'a> {
92 	string: &'a str,
93 	kind: SpanKind,
94 }
95 
96 impl<'a> SpanRef<'a> {
97 	#[must_use]
kind(self) -> SpanKind98 	pub fn kind(self) -> SpanKind {
99 		self.kind
100 	}
101 
102 	#[must_use]
string(self) -> &'a str103 	pub fn string(self) -> &'a str {
104 		self.string
105 	}
106 }
107 
108 impl FendResult {
109 	/// This retrieves the main result of the computation.
110 	#[must_use]
get_main_result(&self) -> &str111 	pub fn get_main_result(&self) -> &str {
112 		self.plain_result.as_str()
113 	}
114 
115 	/// This retrieves the main result as a list of spans, which is useful
116 	/// for colored output.
get_main_result_spans(&self) -> impl Iterator<Item = SpanRef<'_>>117 	pub fn get_main_result_spans(&self) -> impl Iterator<Item = SpanRef<'_>> {
118 		self.span_result.iter().map(|span| SpanRef {
119 			string: &span.string,
120 			kind: span.kind,
121 		})
122 	}
123 
124 	/// Returns whether or not the result is the `()` type. It can sometimes
125 	/// be useful to hide these values.
126 	#[must_use]
is_unit_type(&self) -> bool127 	pub fn is_unit_type(&self) -> bool {
128 		self.is_unit
129 	}
130 
empty() -> Self131 	fn empty() -> Self {
132 		Self {
133 			plain_result: String::new(),
134 			span_result: vec![],
135 			is_unit: true,
136 			attrs: Attrs::default(),
137 		}
138 	}
139 
140 	/// Returns whether or not the result should be outputted with a
141 	/// trailing newline. This is controlled by the `@no_trailing_newline`
142 	/// attribute.
143 	#[must_use]
has_trailing_newline(&self) -> bool144 	pub fn has_trailing_newline(&self) -> bool {
145 		self.attrs.trailing_newline
146 	}
147 }
148 
149 #[derive(Clone, Debug)]
150 struct CurrentTimeInfo {
151 	elapsed_unix_time_ms: u64,
152 	timezone_offset_secs: i64,
153 }
154 
155 #[derive(Clone, Debug, PartialEq, Eq)]
156 enum FCMode {
157 	CelsiusFahrenheit,
158 	CoulombFarad,
159 }
160 
161 #[derive(Clone, Debug, PartialEq, Eq)]
162 enum OutputMode {
163 	SimpleText,
164 	TerminalFixedWidth,
165 }
166 
167 /// An exchange rate handler.
168 pub trait ExchangeRateFn {
169 	/// Returns the value of a currency relative to the base currency.
170 	/// The base currency depends on your implementation. fend-core can work
171 	/// with any base currency as long as it is consistent.
172 	///
173 	/// # Errors
174 	/// This function errors out if the currency was not found or the
175 	/// conversion is impossible for any reason (HTTP request failed, etc.)
relative_to_base_currency( &self, currency: &str, ) -> Result<f64, Box<dyn std::error::Error + Send + Sync + 'static>>176 	fn relative_to_base_currency(
177 		&self,
178 		currency: &str,
179 	) -> Result<f64, Box<dyn std::error::Error + Send + Sync + 'static>>;
180 }
181 
182 impl<T> ExchangeRateFn for T
183 where
184 	T: Fn(&str) -> Result<f64, Box<dyn std::error::Error + Send + Sync + 'static>>,
185 {
relative_to_base_currency( &self, currency: &str, ) -> Result<f64, Box<dyn std::error::Error + Send + Sync + 'static>>186 	fn relative_to_base_currency(
187 		&self,
188 		currency: &str,
189 	) -> Result<f64, Box<dyn std::error::Error + Send + Sync + 'static>> {
190 		self(currency)
191 	}
192 }
193 
194 /// This struct contains fend's current context, including some settings
195 /// as well as stored variables.
196 ///
197 /// If you're writing an interpreter it's recommended to only
198 /// instantiate this struct once so that variables and settings are
199 /// preserved, but you can also manually serialise all variables
200 /// and recreate the context for every calculation, depending on
201 /// which is easier.
202 #[derive(Clone)]
203 pub struct Context {
204 	current_time: Option<CurrentTimeInfo>,
205 	variables: HashMap<String, value::Value>,
206 	fc_mode: FCMode,
207 	random_u32: Option<fn() -> u32>,
208 	output_mode: OutputMode,
209 	get_exchange_rate: Option<Arc<dyn ExchangeRateFn + Send + Sync>>,
210 	custom_units: Vec<(String, String, String)>,
211 }
212 
213 impl fmt::Debug for Context {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result214 	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 		f.debug_struct("Context")
216 			.field("current_time", &self.current_time)
217 			.field("variables", &self.variables)
218 			.field("fc_mode", &self.fc_mode)
219 			.field("random_u32", &self.random_u32)
220 			.field("output_mode", &self.output_mode)
221 			.finish_non_exhaustive()
222 	}
223 }
224 
225 impl Default for Context {
default() -> Self226 	fn default() -> Self {
227 		Self::new()
228 	}
229 }
230 
231 impl Context {
232 	/// Create a new context instance.
233 	#[must_use]
new() -> Self234 	pub fn new() -> Self {
235 		Self {
236 			current_time: None,
237 			variables: HashMap::new(),
238 			fc_mode: FCMode::CelsiusFahrenheit,
239 			random_u32: None,
240 			output_mode: OutputMode::SimpleText,
241 			get_exchange_rate: None,
242 			custom_units: vec![],
243 		}
244 	}
245 
246 	/// This method currently has no effect!
247 	///
248 	/// Set the current time. This API will likely change in the future!
249 	///
250 	/// The first argument (`ms_since_1970`) must be the number of elapsed milliseconds
251 	/// since January 1, 1970 at midnight UTC, ignoring leap seconds in the same way
252 	/// as unix time.
253 	///
254 	/// The second argument (`tz_offset_secs`) is the current time zone
255 	/// offset to UTC, in seconds.
set_current_time_v1(&mut self, _ms_since_1970: u64, _tz_offset_secs: i64)256 	pub fn set_current_time_v1(&mut self, _ms_since_1970: u64, _tz_offset_secs: i64) {
257 		// self.current_time = Some(CurrentTimeInfo {
258 		//     elapsed_unix_time_ms: ms_since_1970,
259 		//     timezone_offset_secs: tz_offset_secs,
260 		// });
261 		self.current_time = None;
262 	}
263 
264 	/// Define the units `C` and `F` as coulomb and farad instead of degrees
265 	/// celsius and degrees fahrenheit.
use_coulomb_and_farad(&mut self)266 	pub fn use_coulomb_and_farad(&mut self) {
267 		self.fc_mode = FCMode::CoulombFarad;
268 	}
269 
270 	/// Set a random number generator
set_random_u32_fn(&mut self, random_u32: fn() -> u32)271 	pub fn set_random_u32_fn(&mut self, random_u32: fn() -> u32) {
272 		self.random_u32 = Some(random_u32);
273 	}
274 
275 	/// Clear the random number generator after setting it with via [`Self::set_random_u32_fn`]
disable_rng(&mut self)276 	pub fn disable_rng(&mut self) {
277 		self.random_u32 = None;
278 	}
279 
280 	/// Change the output mode to fixed-width terminal style. This enables ASCII
281 	/// graphs in the output.
set_output_mode_terminal(&mut self)282 	pub fn set_output_mode_terminal(&mut self) {
283 		self.output_mode = OutputMode::TerminalFixedWidth;
284 	}
285 
serialize_variables_internal(&self, write: &mut impl io::Write) -> FResult<()>286 	fn serialize_variables_internal(&self, write: &mut impl io::Write) -> FResult<()> {
287 		self.variables.len().serialize(write)?;
288 		for (k, v) in &self.variables {
289 			k.as_str().serialize(write)?;
290 			v.serialize(write)?;
291 		}
292 		Ok(())
293 	}
294 
295 	/// Serializes all variables defined in this context to a stream of bytes.
296 	/// Note that the specific format is NOT stable, and can change with any
297 	/// minor update.
298 	///
299 	/// # Errors
300 	/// This function returns an error if the input cannot be serialized.
serialize_variables(&self, write: &mut impl io::Write) -> Result<(), String>301 	pub fn serialize_variables(&self, write: &mut impl io::Write) -> Result<(), String> {
302 		match self.serialize_variables_internal(write) {
303 			Ok(()) => Ok(()),
304 			Err(e) => Err(e.to_string()),
305 		}
306 	}
307 
deserialize_variables_internal(&mut self, read: &mut impl io::Read) -> FResult<()>308 	fn deserialize_variables_internal(&mut self, read: &mut impl io::Read) -> FResult<()> {
309 		let len = usize::deserialize(read)?;
310 		self.variables.clear();
311 		self.variables.reserve(len);
312 		for _ in 0..len {
313 			let s = String::deserialize(read)?;
314 			let v = value::Value::deserialize(read)?;
315 			self.variables.insert(s, v);
316 		}
317 		Ok(())
318 	}
319 
320 	/// Deserializes the given variables, replacing all prior variables in
321 	/// the given context.
322 	///
323 	/// # Errors
324 	/// Returns an error if the input byte stream is invalid and cannot be
325 	/// deserialized.
deserialize_variables(&mut self, read: &mut impl io::Read) -> Result<(), String>326 	pub fn deserialize_variables(&mut self, read: &mut impl io::Read) -> Result<(), String> {
327 		match self.deserialize_variables_internal(read) {
328 			Ok(()) => Ok(()),
329 			Err(e) => Err(e.to_string()),
330 		}
331 	}
332 
333 	/// Set a handler function for loading exchange rates.
set_exchange_rate_handler_v1<T: ExchangeRateFn + 'static + Send + Sync>( &mut self, get_exchange_rate: T, )334 	pub fn set_exchange_rate_handler_v1<T: ExchangeRateFn + 'static + Send + Sync>(
335 		&mut self,
336 		get_exchange_rate: T,
337 	) {
338 		self.get_exchange_rate = Some(Arc::new(get_exchange_rate));
339 	}
340 
define_custom_unit_v1( &mut self, singular: &str, plural: &str, definition: &str, attribute: &CustomUnitAttribute, )341 	pub fn define_custom_unit_v1(
342 		&mut self,
343 		singular: &str,
344 		plural: &str,
345 		definition: &str,
346 		attribute: &CustomUnitAttribute,
347 	) {
348 		let definition_prefix = match attribute {
349 			CustomUnitAttribute::None => "",
350 			CustomUnitAttribute::AllowLongPrefix => "l@",
351 			CustomUnitAttribute::AllowShortPrefix => "s@",
352 			CustomUnitAttribute::IsLongPrefix => "lp@",
353 			CustomUnitAttribute::Alias => "=",
354 		};
355 		self.custom_units.push((
356 			singular.to_string(),
357 			plural.to_string(),
358 			format!("{definition_prefix}{definition}"),
359 		));
360 	}
361 }
362 
363 /// These attributes make is possible to change the behaviour of custom units
364 #[non_exhaustive]
365 pub enum CustomUnitAttribute {
366 	/// Don't allow using prefixes with this custom unit
367 	None,
368 	/// Support long prefixes (e.g. `milli-`, `giga-`) with this unit
369 	AllowLongPrefix,
370 	/// Support short prefixes (e.g. `k` for `kilo`) with this unit
371 	AllowShortPrefix,
372 	/// Allow using this unit as a long prefix with another unit
373 	IsLongPrefix,
374 	/// This unit definition is an alias and will always be replaced with its definition.
375 	Alias,
376 }
377 
378 /// This function evaluates a string using the given context. Any evaluation using this
379 /// function cannot be interrupted.
380 ///
381 /// For example, passing in the string `"1 + 1"` will return a result of `"2"`.
382 ///
383 /// # Errors
384 /// It returns an error if the given string is invalid.
385 /// This may be due to parser or runtime errors.
evaluate(input: &str, context: &mut Context) -> Result<FendResult, String>386 pub fn evaluate(input: &str, context: &mut Context) -> Result<FendResult, String> {
387 	evaluate_with_interrupt(input, context, &interrupt::Never)
388 }
389 
evaluate_with_interrupt_internal( input: &str, context: &mut Context, int: &impl Interrupt, ) -> Result<FendResult, String>390 fn evaluate_with_interrupt_internal(
391 	input: &str,
392 	context: &mut Context,
393 	int: &impl Interrupt,
394 ) -> Result<FendResult, String> {
395 	if input.is_empty() {
396 		// no or blank input: return no output
397 		return Ok(FendResult::empty());
398 	}
399 	let (result, is_unit, attrs) = match eval::evaluate_to_spans(input, None, context, int) {
400 		Ok(value) => value,
401 		Err(e) => return Err(e.to_string()),
402 	};
403 	let mut plain_result = String::new();
404 	for s in &result {
405 		plain_result.push_str(&s.string);
406 	}
407 	Ok(FendResult {
408 		plain_result,
409 		span_result: result,
410 		is_unit,
411 		attrs,
412 	})
413 }
414 
415 /// This function evaluates a string using the given context and the provided
416 /// Interrupt object.
417 ///
418 /// For example, passing in the string `"1 + 1"` will return a result of `"2"`.
419 ///
420 /// # Errors
421 /// It returns an error if the given string is invalid.
422 /// This may be due to parser or runtime errors.
evaluate_with_interrupt( input: &str, context: &mut Context, int: &impl Interrupt, ) -> Result<FendResult, String>423 pub fn evaluate_with_interrupt(
424 	input: &str,
425 	context: &mut Context,
426 	int: &impl Interrupt,
427 ) -> Result<FendResult, String> {
428 	evaluate_with_interrupt_internal(input, context, int)
429 }
430 
431 /// Evaluate the given string to use as a live preview.
432 ///
433 /// Unlike the normal evaluation functions, `evaluate_preview_with_interrupt`
434 /// does not mutate the passed-in context, and only returns results suitable
435 /// for displaying as a live preview: overly long output, multi-line output,
436 /// unit types etc. are all filtered out. RNG functions (e.g. `roll d6`) are
437 /// also disabled. Currency conversions (exchange rates) are disabled.
evaluate_preview_with_interrupt( input: &str, context: &mut Context, int: &impl Interrupt, ) -> FendResult438 pub fn evaluate_preview_with_interrupt(
439 	input: &str,
440 	context: &mut Context,
441 	int: &impl Interrupt,
442 ) -> FendResult {
443 	let empty = FendResult::empty();
444 	// unfortunately making a complete copy of the context is necessary
445 	// because we want variables to still work in multi-statement inputs
446 	// like `a = 2; 5a`.
447 	let context_clone = context.clone();
448 	context.random_u32 = None;
449 	context.get_exchange_rate = None;
450 	let result = evaluate_with_interrupt_internal(input, context, int);
451 	*context = context_clone;
452 	let Ok(result) = result else {
453 		return empty;
454 	};
455 	let s = result.get_main_result();
456 	if s.is_empty()
457 		|| result.is_unit_type()
458 		|| s.len() > 50
459 		|| s.trim() == input.trim()
460 		|| s.contains(|c| c < ' ')
461 	{
462 		return empty;
463 	}
464 	result
465 }
466 
467 #[derive(Debug)]
468 pub struct Completion {
469 	display: String,
470 	insert: String,
471 }
472 
473 impl Completion {
474 	#[must_use]
display(&self) -> &str475 	pub fn display(&self) -> &str {
476 		&self.display
477 	}
478 
479 	#[must_use]
insert(&self) -> &str480 	pub fn insert(&self) -> &str {
481 		&self.insert
482 	}
483 }
484 
485 static GREEK_LOWERCASE_LETTERS: [(&str, &str); 24] = [
486 	("alpha", "α"),
487 	("beta", "β"),
488 	("gamma", "γ"),
489 	("delta", "δ"),
490 	("epsilon", "ε"),
491 	("zeta", "ζ"),
492 	("eta", "η"),
493 	("theta", "θ"),
494 	("iota", "ι"),
495 	("kappa", "κ"),
496 	("lambda", "λ"),
497 	("mu", "μ"),
498 	("nu", "ν"),
499 	("xi", "ξ"),
500 	("omicron", "ο"),
501 	("pi", "π"),
502 	("rho", "ρ"),
503 	("sigma", "σ"),
504 	("tau", "τ"),
505 	("upsilon", "υ"),
506 	("phi", "φ"),
507 	("chi", "χ"),
508 	("psi", "ψ"),
509 	("omega", "ω"),
510 ];
511 static GREEK_UPPERCASE_LETTERS: [(&str, &str); 24] = [
512 	("Alpha", "Α"),
513 	("Beta", "Β"),
514 	("Gamma", "Γ"),
515 	("Delta", "Δ"),
516 	("Epsilon", "Ε"),
517 	("Zeta", "Ζ"),
518 	("Eta", "Η"),
519 	("Theta", "Θ"),
520 	("Iota", "Ι"),
521 	("Kappa", "Κ"),
522 	("Lambda", "Λ"),
523 	("Mu", "Μ"),
524 	("Nu", "Ν"),
525 	("Xi", "Ξ"),
526 	("Omicron", "Ο"),
527 	("Pi", "Π"),
528 	("Rho", "Ρ"),
529 	("Sigma", "Σ"),
530 	("Tau", "Τ"),
531 	("Upsilon", "Υ"),
532 	("Phi", "Φ"),
533 	("Chi", "Χ"),
534 	("Psi", "Ψ"),
535 	("Omega", "Ω"),
536 ];
537 
538 #[must_use]
get_completions_for_prefix(mut prefix: &str) -> (usize, Vec<Completion>)539 pub fn get_completions_for_prefix(mut prefix: &str) -> (usize, Vec<Completion>) {
540 	if let Some((prefix, letter)) = prefix.rsplit_once('\\') {
541 		if letter.starts_with(|c: char| c.is_ascii_alphabetic()) && letter.len() <= 7 {
542 			return if letter.starts_with(|c: char| c.is_ascii_uppercase()) {
543 				GREEK_UPPERCASE_LETTERS
544 			} else {
545 				GREEK_LOWERCASE_LETTERS
546 			}
547 			.iter()
548 			.find(|l| l.0 == letter)
549 			.map_or((0, vec![]), |l| {
550 				(
551 					prefix.len(),
552 					vec![Completion {
553 						display: prefix.to_string(),
554 						insert: l.1.to_string(),
555 					}],
556 				)
557 			});
558 		}
559 	}
560 
561 	let mut prepend = "";
562 	let position = prefix.len();
563 	if let Some((a, b)) = prefix.rsplit_once(' ') {
564 		prepend = a;
565 		prefix = b;
566 	}
567 
568 	if prefix.is_empty() {
569 		return (0, vec![]);
570 	}
571 	let mut res = units::get_completions_for_prefix(prefix);
572 	for c in &mut res {
573 		c.display.insert_str(0, prepend);
574 	}
575 	(position, res)
576 }
577 
578 pub use inline_substitutions::substitute_inline_fend_expressions;
579 
get_version_as_str() -> &'static str580 const fn get_version_as_str() -> &'static str {
581 	env!("CARGO_PKG_VERSION")
582 }
583 
584 /// Returns the current version of `fend-core`.
585 #[must_use]
get_version() -> String586 pub fn get_version() -> String {
587 	get_version_as_str().to_string()
588 }
589 
590 /// Used by unit and integration tests
591 pub mod test_utils {
592 	/// A simple currency handler used in unit and integration tests. Not intended
593 	/// to be used outside of `fend_core`.
594 	///
595 	/// # Panics
596 	/// Panics on unknown currencies
597 	///
598 	/// # Errors
599 	/// Panics on error, so it never needs to return Err(_)
dummy_currency_handler( currency: &str, ) -> Result<f64, Box<dyn std::error::Error + Send + Sync + 'static>>600 	pub fn dummy_currency_handler(
601 		currency: &str,
602 	) -> Result<f64, Box<dyn std::error::Error + Send + Sync + 'static>> {
603 		Ok(match currency {
604 			"EUR" | "USD" => 1.0,
605 			"GBP" => 0.9,
606 			"NZD" => 1.5,
607 			"HKD" => 8.0,
608 			"AUD" => 1.3,
609 			"PLN" => 0.2,
610 			"JPY" => 149.9,
611 			_ => panic!("unknown currency {currency}"),
612 		})
613 	}
614 }
615