1use pest::Parser;
2use pest::iterators::Pair;
3use pest_derive::Parser;
4use serde::{Deserialize, Serialize};
5use std::fmt::{Display, Formatter};
6use std::num::{ParseFloatError, ParseIntError};
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9pub enum BaseUnit {
10 Second,
11 Meter,
12 Kilogram,
13 Ampere,
14 Kelvin,
15 Mole,
16 Candela,
17
18 Radian,
19 Steradian,
20 Hertz,
21 Newton,
22 Pascal,
23 Joule,
24 Watt,
25 Coulomb,
26 Volt,
27 Farad,
28 Ohm,
29 Siemens,
30 Weber,
31 Tesla,
32 Henry,
33 DegreeCelsius,
34 Lumen,
35 Lux,
36 Becquerel,
37 Gray,
38 Sievert,
39 Katal,
40 Named {
41 name: String,
42 symbol: String,
43 exp: SIExp,
44 },
45 Unitless,
46 }
52
53#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
54pub struct Unit {
55 pub prefix: Prefix,
56 pub base: BaseUnit,
57 pub exp: i8,
58}
59
60impl Unit {
61 pub fn unit() -> Self {
62 Unit {
63 prefix: Prefix::Unit,
64 base: BaseUnit::Unitless,
65 exp: 1,
66 }
67 }
68
69 pub fn base(base: BaseUnit) -> Self {
70 Unit {
71 prefix: Prefix::Unit,
72 base,
73 exp: 1,
74 }
75 }
76}
77
78impl Display for Unit {
79 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
80 if self.exp == 1 {
81 write!(f, "{}{}", self.prefix, self.base)
82 } else {
83 write!(f, "{}{}^{}", self.prefix, self.base, self.exp)
85 }
86 }
87}
88
89pub const KNOWN_UNITS: [BaseUnit; 7 + 22] = [
90 BaseUnit::Second,
91 BaseUnit::Meter,
92 BaseUnit::Kilogram,
93 BaseUnit::Ampere,
94 BaseUnit::Kelvin,
95 BaseUnit::Mole,
96 BaseUnit::Candela,
97 BaseUnit::Radian,
98 BaseUnit::Steradian,
99 BaseUnit::Hertz,
100 BaseUnit::Newton,
101 BaseUnit::Pascal,
102 BaseUnit::Joule,
103 BaseUnit::Watt,
104 BaseUnit::Coulomb,
105 BaseUnit::Volt,
106 BaseUnit::Farad,
107 BaseUnit::Ohm,
108 BaseUnit::Siemens,
109 BaseUnit::Weber,
110 BaseUnit::Tesla,
111 BaseUnit::Henry,
112 BaseUnit::DegreeCelsius,
113 BaseUnit::Lumen,
114 BaseUnit::Lux,
115 BaseUnit::Becquerel,
116 BaseUnit::Gray,
117 BaseUnit::Sievert,
118 BaseUnit::Katal,
119];
120
121impl BaseUnit {
122 pub fn exp(&self) -> SIExp {
123 match self {
124 BaseUnit::Second => SIExp::new_reduced(0, 0, 1, 0),
125 BaseUnit::Meter => SIExp::new_reduced(1, 0, 0, 0),
126 BaseUnit::Kilogram => SIExp::new_reduced(0, 1, 0, 0),
127 BaseUnit::Ampere => SIExp::new_reduced(0, 0, 0, 1),
128 BaseUnit::Kelvin => SIExp::new(0, 0, 0, 0, 1, 0, 0),
129 BaseUnit::Mole => SIExp::new(0, 0, 0, 0, 0, 1, 0),
130 BaseUnit::Candela => SIExp::new(0, 0, 0, 0, 0, 0, 1),
131 BaseUnit::Radian => SIExp::new_reduced(0, 0, 0, 0),
132 BaseUnit::Steradian => SIExp::new_reduced(0, 0, 0, 0),
133 BaseUnit::Hertz => SIExp::new_reduced(0, 0, -1, 0),
134 BaseUnit::Newton => SIExp::new_reduced(1, 1, -2, 0),
135 BaseUnit::Pascal => SIExp::new_reduced(-1, 1, -2, 0),
136 BaseUnit::Joule => SIExp::new_reduced(2, 1, -2, 0),
137 BaseUnit::Watt => SIExp::new_reduced(2, 1, -3, 0),
138 BaseUnit::Coulomb => SIExp::new_reduced(0, 0, 1, 1),
139 BaseUnit::Volt => SIExp::new_reduced(2, 1, -3, -1),
140 BaseUnit::Farad => SIExp::new_reduced(-2, -1, 4, 2),
141 BaseUnit::Ohm => SIExp::new_reduced(2, 1, -3, -2),
142 BaseUnit::Siemens => SIExp::new_reduced(-2, -1, 3, 2),
143 BaseUnit::Weber => SIExp::new_reduced(2, 1, -2, -1),
144 BaseUnit::Tesla => SIExp::new_reduced(0, 1, -2, -1),
145 BaseUnit::Henry => SIExp::new_reduced(2, 1, -2, -2),
146 BaseUnit::DegreeCelsius => SIExp::new(0, 0, 0, 0, 1, 0, 0),
147 BaseUnit::Lumen => SIExp::new(0, 0, 0, 0, 0, 0, 1),
148 BaseUnit::Lux => SIExp::new(-2, 0, 0, 0, 0, 0, 1),
149 BaseUnit::Becquerel => SIExp::new_reduced(0, 0, -1, 0),
150 BaseUnit::Gray => SIExp::new_reduced(2, 0, -2, 0),
151 BaseUnit::Sievert => SIExp::new_reduced(2, 0, -2, 0),
152 BaseUnit::Katal => SIExp::new(0, 0, -1, 0, 0, 1, 0),
153 BaseUnit::Named { exp, .. } => *exp,
154 BaseUnit::Unitless => SIExp::new_reduced(0, 0, 0, 0),
156 }
157 }
158
159 pub fn name(&self) -> &str {
160 match self {
161 BaseUnit::Second => "Second",
162 BaseUnit::Meter => "Meter",
163 BaseUnit::Kilogram => "Kilogram",
164 BaseUnit::Ampere => "Ampere",
165 BaseUnit::Kelvin => "Kelvin",
166 BaseUnit::Mole => "Mole",
167 BaseUnit::Candela => "Candela",
168 BaseUnit::Radian => "Radian",
169 BaseUnit::Steradian => "Steradian",
170 BaseUnit::Hertz => "Hertz",
171 BaseUnit::Newton => "Newton",
172 BaseUnit::Pascal => "Pascal",
173 BaseUnit::Joule => "Joule",
174 BaseUnit::Watt => "Watt",
175 BaseUnit::Coulomb => "Coulomb",
176 BaseUnit::Volt => "Volt",
177 BaseUnit::Farad => "Farad",
178 BaseUnit::Ohm => "Ohm",
179 BaseUnit::Siemens => "Siemens",
180 BaseUnit::Weber => "Weber",
181 BaseUnit::Tesla => "Tesla",
182 BaseUnit::Henry => "Henry",
183 BaseUnit::DegreeCelsius => "Degree Celsius",
184 BaseUnit::Lumen => "Lumen",
185 BaseUnit::Lux => "Lux",
186 BaseUnit::Becquerel => "Becquerel",
187 BaseUnit::Gray => "Gray",
188 BaseUnit::Sievert => "Sievert",
189 BaseUnit::Katal => "Katal",
190 BaseUnit::Named { name, .. } => name.as_str(),
191 BaseUnit::Unitless => "Unitless",
192 }
193 }
194
195 pub fn symbol(&self) -> &str {
196 match self {
197 BaseUnit::Second => "s",
198 BaseUnit::Meter => "m",
199 BaseUnit::Kilogram => "kg",
200 BaseUnit::Ampere => "A",
201 BaseUnit::Kelvin => "K",
202 BaseUnit::Mole => "mol",
203 BaseUnit::Candela => "cd",
204 BaseUnit::Radian => "rad",
205 BaseUnit::Steradian => "sr",
206 BaseUnit::Hertz => "Hz",
207 BaseUnit::Newton => "N",
208 BaseUnit::Pascal => "Pa",
209 BaseUnit::Joule => "J",
210 BaseUnit::Watt => "W",
211 BaseUnit::Coulomb => "C",
212 BaseUnit::Volt => "V",
213 BaseUnit::Farad => "F",
214 BaseUnit::Ohm => "Ω",
215 BaseUnit::Siemens => "S",
216 BaseUnit::Weber => "Wb",
217 BaseUnit::Tesla => "T",
218 BaseUnit::Henry => "H",
219 BaseUnit::DegreeCelsius => "°C",
220 BaseUnit::Lumen => "lm",
221 BaseUnit::Lux => "lx",
222 BaseUnit::Becquerel => "Bq",
223 BaseUnit::Gray => "Gy",
224 BaseUnit::Sievert => "Sv",
225 BaseUnit::Katal => "kat",
226 BaseUnit::Named { name, .. } => name.as_str(),
227 BaseUnit::Unitless => "()",
229 }
230 }
231}
232
233#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
234pub struct SIExp(pub [i8; 7]);
235
236impl SIExp {
237 pub const fn new(
238 meter: i8,
239 kilogram: i8,
240 second: i8,
241 ampere: i8,
242 kelvin: i8,
243 mole: i8,
244 candela: i8,
245 ) -> Self {
246 SIExp([meter, kilogram, second, ampere, kelvin, mole, candela])
247 }
248
249 pub const fn new_reduced(meter: i8, kilogram: i8, second: i8, ampere: i8) -> Self {
250 SIExp([meter, kilogram, second, ampere, 0, 0, 0])
251 }
252
253 pub fn meter(&self) -> i8 {
254 self.0[0]
255 }
256
257 pub fn kilogram(&self) -> i8 {
258 self.0[1]
259 }
260
261 pub fn second(&self) -> i8 {
262 self.0[2]
263 }
264
265 pub fn ampere(&self) -> i8 {
266 self.0[3]
267 }
268
269 pub fn kelvin(&self) -> i8 {
270 self.0[4]
271 }
272
273 pub fn mole(&self) -> i8 {
274 self.0[5]
275 }
276
277 pub fn candela(&self) -> i8 {
278 self.0[6]
279 }
280}
281
282#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
283pub struct Quantity {
284 pub number: String,
285 pub unit: Unit,
286}
287
288impl Quantity {
289 pub fn parse(s: impl AsRef<str>) -> Result<Quantity, Error> {
290 let mut p = SIParser::parse(Rule::si_number, s.as_ref())?
291 .into_iter()
292 .next()
293 .ok_or(Error::Internal("expected si_number".into()))?
294 .into_inner();
295 let number = p
296 .next()
297 .ok_or(Error::Internal("expected number".into()))?
298 .as_str();
299 let unit = if let Some(unit) = p.next() {
300 parse_unit(unit)?
301 } else {
302 Unit::unit()
303 };
304 Ok(Quantity {
305 number: number.to_string(),
306 unit,
307 })
308 }
309
310 pub fn as_f32(&self) -> Result<f32, ParseFloatError> {
311 let number = self.number.parse::<f32>()?;
312 Ok(number * self.unit.prefix.as_f32())
313 }
314}
315
316fn parse_unit(p: Pair<Rule>) -> Result<Unit, Error> {
317 if !matches!(p.as_rule(), Rule::unit) {
318 return Err(Error::Internal("expected unit".into()));
319 }
320 let mut p = p.into_inner();
321 let prefix_or_raw_unit = p
322 .next()
323 .ok_or(Error::Internal("expected prefix or unit".into()))?;
324 let (prefix, raw_unit) = if prefix_or_raw_unit.as_rule() == Rule::prefix {
325 let raw_unit = p
326 .next()
327 .ok_or(Error::Internal("expected raw_unit".into()))?;
328 (parse_prefix(prefix_or_raw_unit)?, raw_unit)
329 } else {
330 (Prefix::Unit, prefix_or_raw_unit)
331 };
332 let base = parse_raw_unit(raw_unit)?;
333 let exp = if let Some(num) = p.next() {
334 parse_integer_number_i64(num)?
335 } else {
336 1
337 };
338 let exp =
339 i8::try_from(exp).map_err(|_| Error::Internal("unit exponent must fit in i8".into()))?;
340 Ok(Unit { prefix, base, exp })
341}
342
343fn parse_raw_unit(p: Pair<Rule>) -> Result<BaseUnit, Error> {
344 if !matches!(p.as_rule(), Rule::raw_unit) {
345 return Err(Error::Internal("expected raw_unit".into()));
346 }
347 let raw_unit = p.into_inner().next().ok_or(Error::Internal("".into()))?;
348 let unit = match raw_unit.as_rule() {
349 Rule::second => BaseUnit::Second,
350 Rule::meter => BaseUnit::Meter,
351 Rule::gram => BaseUnit::Kilogram, Rule::ampere => BaseUnit::Ampere,
353 Rule::kelvin => BaseUnit::Kelvin,
354 Rule::mole => BaseUnit::Mole,
355 Rule::candela => BaseUnit::Candela,
356
357 Rule::radian => BaseUnit::Radian,
358 Rule::steradian => BaseUnit::Steradian,
359 Rule::hertz => BaseUnit::Hertz,
360 Rule::newton => BaseUnit::Newton,
361 Rule::pascal => BaseUnit::Pascal,
362 Rule::joule => BaseUnit::Joule,
363 Rule::watt => BaseUnit::Watt,
364 Rule::coulomb => BaseUnit::Coulomb,
365 Rule::volt => BaseUnit::Volt,
366 Rule::farad => BaseUnit::Farad,
367 Rule::ohm => BaseUnit::Ohm,
368 Rule::siemens => BaseUnit::Siemens,
369 Rule::weber => BaseUnit::Weber,
370 Rule::tesla => BaseUnit::Tesla,
371 Rule::henry => BaseUnit::Henry,
372 Rule::celsius => BaseUnit::DegreeCelsius,
373 Rule::lumen => BaseUnit::Lumen,
374 Rule::lux => BaseUnit::Lux,
375 Rule::becquerel => BaseUnit::Becquerel,
376 Rule::gray => BaseUnit::Gray,
377 Rule::sievert => BaseUnit::Sievert,
378 Rule::katal => BaseUnit::Katal,
379
380 Rule::ident => BaseUnit::Named {
381 name: raw_unit.as_str().to_string(),
382 symbol: "".to_string(),
383 exp: SIExp::new_reduced(0, 0, 0, 0),
384 },
385 _ => return Err(Error::Internal("unhandled raw_unit".into())),
386 };
387 Ok(unit)
388}
389
390pub enum SIExpr {
391 Num(String),
392 Unit { prefix: Prefix, unit: Unit },
393 Mul((Box<SIExpr>, Box<SIExpr>)),
394 Div((Box<SIExpr>, Box<SIExpr>)),
395}
396
397impl SIExpr {
398 pub fn parse(s: impl AsRef<str>) -> Result<SIExpr, Error> {
399 let p = SIParser::parse(Rule::si_expr, s.as_ref())?;
400 println!("{p:#?}");
401 Ok(SIExpr::Num("".into()))
403 }
404}
405
406#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
407pub enum Prefix {
408 Milli,
409 Micro,
410 Nano,
411 Pico,
412
413 Kilo,
414 Mega,
415 Giga,
416
417 Unit,
418
419 Deca,
420 Hecto,
421 Tera,
422 Peta,
423 Exa,
424 Zetta,
425 Yotta,
426 Ronna,
427 Quetta,
428
429 Deci,
430 Centi,
431 Femto,
432 Atto,
433 Zepto,
434 Yocto,
435 Ronto,
436 Quecto,
437}
438
439pub struct OhmF32(pub f32);
440
441#[derive(Debug)]
442pub enum Error {
443 Pest(pest::error::Error<Rule>),
444 Float(ParseFloatError),
445 Int(ParseIntError),
446 Internal(String),
447}
448
449impl From<pest::error::Error<Rule>> for Error {
450 fn from(e: pest::error::Error<Rule>) -> Self {
451 Error::Pest(e)
452 }
453}
454
455impl From<ParseFloatError> for Error {
456 fn from(e: ParseFloatError) -> Self {
457 Error::Float(e)
458 }
459}
460
461impl From<ParseIntError> for Error {
462 fn from(e: ParseIntError) -> Self {
463 Error::Int(e)
464 }
465}
466
467impl Display for Error {
468 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
469 match self {
470 Error::Pest(e) => write!(f, "{}", e),
471 Error::Float(e) => write!(f, "{}", e),
472 Error::Int(e) => write!(f, "{}", e),
473 Error::Internal(e) => write!(f, "Internal: {}", e),
474 }
475 }
476}
477
478fn expected_rule(name: &'static str) -> Error {
479 Error::Internal(name.to_string())
480}
481
482impl Prefix {
483 pub fn as_f32(&self) -> f32 {
484 match self {
485 Prefix::Milli => 1e-3,
486 Prefix::Micro => 1e-6,
487 Prefix::Nano => 1e-9,
488 Prefix::Pico => 1e-12,
489 Prefix::Kilo => 1e3,
490 Prefix::Mega => 1e6,
491 Prefix::Giga => 1e9,
492 Prefix::Unit => 1e0,
493 Prefix::Deca => 1e1,
494 Prefix::Hecto => 1e2,
495 Prefix::Tera => 1e12,
496 Prefix::Peta => 1e15,
497 Prefix::Exa => 1e18,
498 Prefix::Zetta => 1e21,
499 Prefix::Yotta => 1e24,
500 Prefix::Ronna => 1e27,
501 Prefix::Quetta => 1e30,
502 Prefix::Deci => 1e-1,
503 Prefix::Centi => 1e-2,
504 Prefix::Femto => 1e-15,
505 Prefix::Atto => 1e-18,
506 Prefix::Zepto => 1e-21,
507 Prefix::Yocto => 1e-24,
508 Prefix::Ronto => 1e-27,
509 Prefix::Quecto => 1e-30,
510 }
511 }
512
513 pub fn symbol(&self) -> &'static str {
514 match self {
515 Prefix::Milli => "m",
516 Prefix::Micro => "μ", Prefix::Nano => "n",
518 Prefix::Pico => "p",
519 Prefix::Kilo => "k",
520 Prefix::Mega => "M",
521 Prefix::Giga => "G",
522 Prefix::Unit => "",
523 Prefix::Deca => "da",
524 Prefix::Hecto => "h",
525 Prefix::Tera => "T",
526 Prefix::Peta => "P",
527 Prefix::Exa => "E",
528 Prefix::Zetta => "Z",
529 Prefix::Yotta => "Y",
530 Prefix::Ronna => "R",
531 Prefix::Quetta => "Q",
532 Prefix::Deci => "d",
533 Prefix::Centi => "c",
534 Prefix::Femto => "f",
535 Prefix::Atto => "a",
536 Prefix::Zepto => "z",
537 Prefix::Yocto => "y",
538 Prefix::Ronto => "r",
539 Prefix::Quecto => "q",
540 }
541 }
542}
543
544impl Display for Prefix {
545 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
546 write!(f, "{}", self.symbol())
547 }
548}
549
550#[derive(Parser)]
551#[grammar = "grammar/si.pest"]
552struct SIParser;
553
554impl OhmF32 {
555 pub fn parse(s: impl AsRef<str>) -> Result<OhmF32, Error> {
556 let p = SIParser::parse(Rule::resistance, s.as_ref())?;
557 let mut resistance = p
559 .into_iter()
560 .next()
561 .ok_or(expected_rule("resistance"))?
562 .into_inner();
563 let value = resistance
564 .next()
565 .ok_or(expected_rule("number_prefix_allowed"))?;
566 let value = parse_any_number_f32(value)?;
567
568 if resistance.peek().map(|p| p.as_rule()) == Some(Rule::EOI) {
569 return Ok(OhmF32(value));
570 }
571 let p = resistance
572 .next()
573 .ok_or(expected_rule("ohm_unit"))?
574 .into_inner()
575 .next()
576 .ok_or(expected_rule("prefix or ohm_or_r"))?;
577 let prefix = match p.as_rule() {
578 Rule::r_prefix => parse_prefix(p)?,
579 Rule::ohm_or_r => Prefix::Unit,
580 _ => return Err(Error::Internal("wrong resistance rule".to_owned())),
581 };
582
583 Ok(OhmF32(value * prefix.as_f32()))
584 }
585}
586
587fn parse_any_number_f32(num: Pair<Rule>) -> Result<f32, Error> {
588 if !matches!(num.as_rule(), Rule::number | Rule::r_number) {
589 return Err(Error::Internal(
590 "expected number or number_prefix_allowed".into(),
591 ));
592 }
593 let mut p = num.into_inner();
594 let int = parse_integer_number_i64(p.next().ok_or(expected_rule("integer_number"))?)?;
595 let Some(tail) = p.next() else {
596 return Ok(int as f32);
597 };
598 match tail.as_rule() {
599 Rule::exponent => {
600 let exp = parse_integer_number_i64(p.next().ok_or(expected_rule("simple_number"))?)?;
601 let exp: u32 = exp
602 .try_into()
603 .map_err(|_| Error::Internal("exponent is too big".into()))?;
604 Ok(int as f32 * 10u64.pow(exp) as f32)
605 }
606 Rule::raw_number => {
607 let frac: f32 = format!("0.{}", tail.as_str()).parse()?;
608 let exp = if let Some(p) = p.next() {
609 let exp = parse_integer_number_i64(p)?;
610 let exp: u32 = exp
611 .try_into()
612 .map_err(|_| Error::Internal("exponent is too big".into()))?;
613 exp
614 } else {
615 0
616 };
617 Ok((int as f32 + frac) * 10u64.pow(exp) as f32)
618 }
619 Rule::prefix | Rule::r_prefix => {
620 let prefix = parse_prefix(tail)?;
621 let frac = if let Some(frac) = p.next() {
622 let frac: f32 = format!("0.{}", frac.as_str()).parse()?;
623 frac
624 } else {
625 0.0
626 };
627 Ok((int as f32 + frac) * prefix.as_f32())
628 }
629 _ => Err(Error::Internal("unexpected rule".into())),
630 }
631}
632
633fn parse_integer_number_i64(rule: Pair<Rule>) -> Result<i64, Error> {
634 if rule.as_rule() != Rule::integer_number {
635 return Err(Error::Internal("expected simple_number".into()));
636 }
637 let mut p = rule.into_inner();
638 let minus_or_raw_number = p.next().ok_or(expected_rule("minus or raw_number"))?;
639 let is_minus = minus_or_raw_number.as_rule() == Rule::minus;
640 let int = if is_minus {
641 let int = p.next().ok_or(expected_rule("integer"))?.into_inner();
642 int.as_str()
643 } else {
644 minus_or_raw_number.as_str()
645 };
646 let v: i64 = int.parse()?;
647 if is_minus { Ok(-v) } else { Ok(v) }
648}
649
650fn parse_prefix(rule: Pair<Rule>) -> Result<Prefix, Error> {
651 let rule = rule.into_inner().next().ok_or(expected_rule("prefix"))?;
652 let prefix = match rule.as_rule() {
653 Rule::quetta => Prefix::Quetta,
654 Rule::ronna => Prefix::Ronna,
655 Rule::yotta => Prefix::Yotta,
656 Rule::zetta => Prefix::Zetta,
657 Rule::exa => Prefix::Exa,
658 Rule::peta => Prefix::Peta,
659 Rule::tera => Prefix::Tera,
660 Rule::tera_unconditional => Prefix::Tera,
661 Rule::giga => Prefix::Giga,
662 Rule::giga_unconditional => Prefix::Giga,
663 Rule::mega => Prefix::Mega,
664 Rule::mega_unconditional => Prefix::Mega,
665 Rule::kilo => Prefix::Kilo,
666 Rule::kilo_unconditional => Prefix::Kilo,
667 Rule::hecto => Prefix::Hecto,
668 Rule::deca => Prefix::Deca,
669 Rule::deci => Prefix::Deci,
670 Rule::centi => Prefix::Centi,
671 Rule::milli => Prefix::Milli,
672 Rule::milli_unconditional => Prefix::Milli,
673 Rule::micro => Prefix::Micro,
674 Rule::micro_unconditional => Prefix::Micro,
675 Rule::nano => Prefix::Nano,
676 Rule::nano_unconditional => Prefix::Nano,
677 Rule::pico => Prefix::Pico,
678 Rule::femto => Prefix::Femto,
679 Rule::atto => Prefix::Atto,
680 Rule::zepto => Prefix::Zepto,
681 Rule::yocto => Prefix::Yocto,
682 Rule::ronto => Prefix::Ronto,
683 Rule::quecto => Prefix::Quecto,
684 _ => return Err(Error::Internal("expected prefix rule".into())),
685 };
686 Ok(prefix)
687}
688
689impl Display for BaseUnit {
690 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
691 write!(f, "{}", self.symbol())
692 }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698 use float_cmp::approx_eq;
699
700 #[test]
701 fn expr() {
702 }
718
719 #[test]
720 fn si_number_voltage() {
721 let num = Quantity::parse("10V").unwrap();
722 assert_eq!(num.number, "10");
723 assert_eq!(num.unit.prefix, Prefix::Unit);
724 assert_eq!(num.unit.base, BaseUnit::Volt);
725 assert_eq!(num.unit.exp, 1);
726 assert!(approx_eq!(f32, num.as_f32().unwrap(), 10.0, ulps = 2));
727
728 let num = Quantity::parse("10mV").unwrap();
729 assert_eq!(num.number, "10");
730 assert_eq!(num.unit.prefix, Prefix::Milli);
731 assert_eq!(num.unit.base, BaseUnit::Volt);
732 assert_eq!(num.unit.exp, 1);
733 assert!(approx_eq!(f32, num.as_f32().unwrap(), 0.01, ulps = 2));
734
735 let num = Quantity::parse("10mVDC").unwrap();
736 assert_eq!(num.number, "10");
737 assert_eq!(num.unit.prefix, Prefix::Milli);
738 assert_eq!(num.unit.base.name(), "VDC");
739 }
740
741 #[test]
742 fn si_number_capacitance() {
743 let num = Quantity::parse("4.7 uF").unwrap();
744 assert_eq!(num.number, "4.7");
745 assert_eq!(num.unit.prefix, Prefix::Micro);
746 assert_eq!(num.unit.base, BaseUnit::Farad);
747 assert_eq!(num.unit.exp, 1);
748 assert!(approx_eq!(f32, num.as_f32().unwrap(), 4.7e-6, ulps = 2));
749
750 let num = Quantity::parse("4.7 µF").unwrap();
751 assert_eq!(num.number, "4.7");
752 assert_eq!(num.unit.prefix, Prefix::Micro);
753 assert_eq!(num.unit.base, BaseUnit::Farad);
754 assert_eq!(num.unit.exp, 1);
755 assert!(approx_eq!(f32, num.as_f32().unwrap(), 4.7e-6, ulps = 2));
756 }
757
758 #[test]
759 fn expr_leading_number() {
760 let _expr = SIExpr::parse("10*V/us").unwrap();
761 let _expr = SIExpr::parse("10 V/us").unwrap();
762 let _expr = SIExpr::parse(" 10 V/us").unwrap();
763 let _expr = SIExpr::parse(" 10V ").unwrap();
764 let _expr = SIExpr::parse("10").unwrap();
765 }
766
767 #[test]
768 fn resistance_values() {
769 let v = OhmF32::parse("100").unwrap();
770 assert_eq!(v.0, 100.0);
771
772 let v = OhmF32::parse("5k").unwrap();
773 assert_eq!(v.0, 5000.0);
774
775 let v = OhmF32::parse("5M").unwrap();
776 assert_eq!(v.0, 5_000_000.0);
777
778 let v = OhmF32::parse("5G").unwrap();
779 assert_eq!(v.0, 5_000_000_000.0);
780
781 let v = OhmF32::parse("1R").unwrap();
782 assert_eq!(v.0, 1.0);
783
784 let v = OhmF32::parse("15.5").unwrap();
785 assert_eq!(v.0, 15.5);
786
787 let v = OhmF32::parse("5.55R").unwrap();
788 assert_eq!(v.0, 5.55);
789
790 let v = OhmF32::parse("1.0k").unwrap();
791 assert_eq!(v.0, 1000.0);
792
793 let v = OhmF32::parse("1k2").unwrap();
794 assert_eq!(v.0, 1200.0);
795
796 let v = OhmF32::parse("10 kΩ").unwrap();
797 assert_eq!(v.0, 10_000.0);
798
799 let v = OhmF32::parse("5Ω").unwrap();
800 assert_eq!(v.0, 5.0);
801
802 let v = OhmF32::parse("1 mΩ").unwrap();
803 assert_eq!(v.0, 0.001);
804
805 let v = OhmF32::parse("1μΩ").unwrap();
806 assert_eq!(v.0, 0.000001);
807
808 let v = OhmF32::parse(" 0 ").unwrap();
809 assert_eq!(v.0, 0.0);
810
811 let v = OhmF32::parse(" 0 R ").unwrap();
812 assert_eq!(v.0, 0.0);
813
814 let v = OhmF32::parse("49r").unwrap();
815 assert_eq!(v.0, 49.0);
816
817 let v = OhmF32::parse(" 499kR ").unwrap();
818 assert_eq!(v.0, 499_000.0);
819
820 let v = OhmF32::parse("12 kOhms").unwrap();
821 assert_eq!(v.0, 12000.0);
822
823 let v = OhmF32::parse("2 mOhms").unwrap();
824 assert_eq!(v.0, 0.002);
825 }
826}