1 use std::borrow::Cow;
2
3 #[derive(Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
4 struct UnitDef {
5 singular: &'static str,
6 plural: &'static str,
7 definition: &'static str,
8 }
9
10 // singular, plural (or empty), definition, description
11 type UnitTuple = (&'static str, &'static str, &'static str, &'static str);
12
13 const BASE_UNITS: &[UnitTuple] = &[
14 ("second", "seconds", "l@!", ""),
15 ("meter", "meters", "l@!", ""),
16 ("kilogram", "kilograms", "l@!", ""),
17 ("kelvin", "", "l@!", ""),
18 ("ampere", "amperes", "l@!", ""),
19 ("mole", "moles", "l@!", ""),
20 ("candela", "candelas", "l@!", ""),
21 ("neper", "nepers", "l@!", ""),
22 ];
23
24 const BASE_UNIT_ABBREVIATIONS: &[UnitTuple] = &[
25 ("s", "", "s@second", ""),
26 ("metre", "metres", "l@meter", ""),
27 ("m", "", "s@meter", ""),
28 ("gram", "grams", "l@1/1000 kilogram", ""),
29 ("jin", "jin", "l@1/2 kilogram", ""),
30 ("g", "", "s@gram", ""),
31 ("K", "", "s@kelvin", ""),
32 ("\u{b0}K", "", "=K", ""),
33 ("amp", "amps", "l@ampere", ""),
34 ("A", "", "s@ampere", ""),
35 ("mol", "", "s@mole", ""),
36 ("cd", "", "s@candela", ""),
37 ("Np", "", "s@neper", ""),
38 ];
39
40 // some temperature scales have special support for conversions
41 const TEMPERATURE_SCALES: &[UnitTuple] = &[
42 ("celsius", "", "l@!", ""),
43 ("\u{b0}C", "", "celsius", ""), // degree symbol
44 ("oC", "", "=\u{b0}C", ""),
45 ("rankine", "", "l@5/9 K", ""),
46 ("\u{b0}R", "", "rankine", ""),
47 ("fahrenheit", "", "l@!", ""),
48 ("\u{b0}F", "", "fahrenheit", ""),
49 ("oF", "", "=\u{b0}F", ""),
50 ];
51
52 const BITS_AND_BYTES: &[UnitTuple] = &[
53 ("bit", "bits", "l@!", ""),
54 ("bps", "", "s@bits/second", ""),
55 ("byte", "bytes", "l@8 bits", ""),
56 ("b", "", "s@bit", ""),
57 ("B", "", "s@byte", ""),
58 ("octet", "octets", "l@8 bits", ""),
59 ("nibble", "nibbles", "l@4 bits", ""),
60 ];
61
62 const STANDARD_PREFIXES: &[UnitTuple] = &[
63 ("quecca", "", "lp@1e30", ""),
64 ("ronna", "", "lp@1e27", ""),
65 ("yotta", "", "lp@1e24", ""),
66 ("zetta", "", "lp@1e21", ""),
67 ("exa", "", "lp@1e18", ""),
68 ("peta", "", "lp@1e15", ""),
69 ("tera", "", "lp@1e12", ""),
70 ("giga", "", "lp@1e9", ""),
71 ("mega", "", "lp@1e6", ""),
72 ("myria", "", "lp@1e4", ""),
73 ("kilo", "", "lp@1e3", ""),
74 ("hecto", "", "lp@1e2", ""),
75 ("deca", "", "lp@1e1", ""),
76 ("deka", "", "lp@deca", ""),
77 ("deci", "", "lp@1e-1", ""),
78 ("centi", "", "lp@1e-2", ""),
79 ("milli", "", "lp@1e-3", ""),
80 ("micro", "", "lp@1e-6", ""),
81 ("nano", "", "lp@1e-9", ""),
82 ("pico", "", "lp@1e-12", ""),
83 ("femto", "", "lp@1e-15", ""),
84 ("atto", "", "lp@1e-18", ""),
85 ("zepto", "", "lp@1e-21", ""),
86 ("yocto", "", "lp@1e-24", ""),
87 ("ronto", "", "lp@1e-27", ""),
88 ("quecto", "", "lp@1e-30", ""),
89 ("k", "", "=1000", ""),
90 ("M", "", "=1,000,000", ""),
91 ("G", "", "=1,000,000,000", ""),
92 ("T", "", "=1,000,000,000,000", ""),
93 ];
94
95 const NON_STANDARD_PREFIXES: &[UnitTuple] = &[
96 ("quarter", "", "lp@1/4", ""),
97 ("semi", "", "[email protected]", ""),
98 ("demi", "", "[email protected]", ""),
99 ("hemi", "", "[email protected]", ""),
100 ("half", "", "[email protected]", ""),
101 ("double", "", "lp@2", ""),
102 ("triple", "", "lp@3", ""),
103 ("treble", "", "lp@3", ""),
104 ];
105
106 const BINARY_PREFIXES: &[UnitTuple] = &[
107 ("kibi", "", "lp@2^10", ""),
108 ("mebi", "", "lp@2^20", ""),
109 ("gibi", "", "lp@2^30", ""),
110 ("tebi", "", "lp@2^40", ""),
111 ("pebi", "", "lp@2^50", ""),
112 ("exbi", "", "lp@2^60", ""),
113 ("zebi", "", "lp@2^70", ""),
114 ("yobi", "", "lp@2^80", ""),
115 ("Ki", "", "=2^10", ""),
116 ("Mi", "", "=2^20", ""),
117 ("Gi", "", "=2^30", ""),
118 ("Ti", "", "=2^40", ""),
119 ];
120
121 const NUMBER_WORDS: &[UnitTuple] = &[
122 ("tithe", "", "=1/10", ""),
123 ("one", "", "=1", ""),
124 ("two", "", "=2", ""),
125 ("couple", "", "=2", ""),
126 ("three", "", "=3", ""),
127 ("four", "", "=4", ""),
128 ("quadruple", "", "=4", ""),
129 ("five", "", "=5", ""),
130 ("quintuple", "", "=5", ""),
131 ("six", "", "=6", ""),
132 ("seven", "", "=7", ""),
133 ("eight", "", "=8", ""),
134 ("nine", "", "=9", ""),
135 ("ten", "", "=10", ""),
136 ("eleven", "", "=11", ""),
137 ("twelve", "", "=12", ""),
138 ("dozen", "", "=12", ""),
139 ("thirteen", "", "=13", ""),
140 ("bakersdozen", "", "=13", ""),
141 ("fourteen", "", "=14", ""),
142 ("fifteen", "", "=15", ""),
143 ("sixteen", "", "=16", ""),
144 ("seventeen", "", "=17", ""),
145 ("eighteen", "", "=18", ""),
146 ("nineteen", "", "=19", ""),
147 ("twenty", "", "=20", ""),
148 ("score", "", "=20", ""),
149 ("thirty", "", "=30", ""),
150 ("forty", "", "=40", ""),
151 ("fifty", "", "=50", ""),
152 ("sixty", "", "=60", ""),
153 ("seventy", "", "=70", ""),
154 ("eighty", "", "=80", ""),
155 ("ninety", "", "=90", ""),
156 ("hundred", "", "=100", ""),
157 ("gross", "", "=144", ""),
158 ("greatgross", "", "=12 gross", ""),
159 ("thousand", "", "=1000", ""),
160 ("million", "", "=1e6", ""),
161 ("billion", "", "=1e9", ""),
162 ("trillion", "", "=1e12", ""),
163 ("quadrillion", "", "=1e15", ""),
164 ("quintillion", "", "=1e18", ""),
165 ("sextillion", "", "=1e21", ""),
166 ("septillion", "", "=1e24", ""),
167 ("octillion", "", "=1e27", ""),
168 ("nonillion", "", "=1e30", ""),
169 ("decillion", "", "=1e33", ""),
170 ("undecillion", "", "=1e36", ""),
171 ("duodecillion", "", "=1e39", ""),
172 ("tredecillion", "", "=1e42", ""),
173 ("quattuordecillion", "", "=1e45", ""),
174 ("quindecillion", "", "=1e48", ""),
175 ("sexdecillion", "", "=1e51", ""),
176 ("septendecillion", "", "=1e54", ""),
177 ("octodecillion", "", "=1e57", ""),
178 ("novemdecillion", "", "=1e60", ""),
179 ("vigintillion", "", "=1e63", ""),
180 ("unvigintillion", "", "=1e66", ""),
181 ("duovigintillion", "", "=1e69", ""),
182 ("trevigintillion", "", "=1e72", ""),
183 ("quattuorvigintillion", "", "=1e75", ""),
184 ("quinvigintillion", "", "=1e78", ""),
185 ("sexvigintillion", "", "=1e81", ""),
186 ("septenvigintillion", "", "=1e84", ""),
187 ("octovigintillion", "", "=1e87", ""),
188 ("novemvigintillion", "", "=1e90", ""),
189 ("trigintillion", "", "=1e93", ""),
190 ("untrigintillion", "", "=1e96", ""),
191 ("duotrigintillion", "", "=1e99", ""),
192 ("googol", "", "=1e100", ""),
193 ("tretrigintillion", "", "=1e102", ""),
194 ("quattuortrigintillion", "", "=1e105", ""),
195 ("quintrigintillion", "", "=1e108", ""),
196 ("sextrigintillion", "", "=1e111", ""),
197 ("septentrigintillion", "", "=1e114", ""),
198 ("octotrigintillion", "", "=1e117", ""),
199 ("novemtrigintillion", "", "=1e120", ""),
200 ("centillion", "", "=1e303", ""),
201 ];
202
203 const CONSTANTS: &[UnitTuple] = &[
204 (
205 "c",
206 "",
207 "=299792458 m/s",
208 "speed of light in vacuum (exact)",
209 ),
210 (
211 "planck",
212 "",
213 "=6.62607015e-34 J s",
214 "Planck constant (exact)",
215 ),
216 (
217 "boltzmann",
218 "",
219 "=1.380649e-23 J/K",
220 "Boltzmann constant (exact)",
221 ),
222 (
223 "electron_charge",
224 "",
225 "=1.602176634e-19 coulomb",
226 "electron charge (exact)",
227 ),
228 (
229 "avogadro",
230 "",
231 "=6.02214076e23 / mol",
232 "size of a mole (exact)",
233 ),
234 ("N_A", "", "=avogadro", ""),
235 (
236 "gravitational_constant",
237 "",
238 "=6.67430e-11 N m^2 / kg^2",
239 "gravitational constant",
240 ),
241 ("gravity", "", "=9.80665 m/s^2", ""),
242 ("force", "", "gravity", ""), // used to convert some units
243 ];
244
245 const ANGLES: &[UnitTuple] = &[
246 ("radian", "radians", "l@1", ""),
247 ("rad", "", "radian", ""),
248 ("circle", "circles", "l@2 pi radian", ""),
249 ("degree", "degrees", "l@1/360 circle", ""),
250 ("deg", "degs", "l@degree", ""),
251 ("\u{b0}", "", "degree", ""), // degree symbol
252 ("arcdeg", "arcdegs", "degree", ""),
253 ("arcmin", "arcmins", "l@1/60 degree", ""),
254 ("arcminute", "arcminutes", "l@arcmin", ""),
255 ("arcsec", "arcsecs", "l@1/60 arcmin", ""),
256 ("arcsecond", "arcseconds", "l@arcsec", ""),
257 ("rightangle", "rightangles", "l@90 degrees", ""),
258 ("quadrant", "quadrants", "l@1/4 circle", ""),
259 ("quintant", "quintants", "l@1/5 circle", ""),
260 ("sextant", "sextants", "l@1/6 circle", ""),
261 (
262 "zodiac_sign",
263 "zodiac_signs",
264 "l@1/12 circle",
265 "Angular extent of one sign of the zodiac",
266 ),
267 ("turn", "turns", "l@circle", ""),
268 ("revolution", "revolutions", "l@circle", ""),
269 ("rev", "revs", "l@circle", ""),
270 ("gradian", "gradians", "l@1/100 rightangle", ""),
271 ("gon", "gons", "l@gradian", ""),
272 ("grad", "", "l@gradian", ""),
273 ("mas", "", "milliarcsec", ""),
274 ];
275
276 const SOLID_ANGLES: &[UnitTuple] = &[
277 ("steradian", "steradians", "l@1", ""),
278 ("sr", "sr", "s@steradian", ""),
279 ("sphere", "spheres", "4 pi steradians", ""),
280 (
281 "squaredegree",
282 "squaredegrees",
283 "(1/180)^2 pi^2 steradians",
284 "",
285 ),
286 ("squareminute", "squareminutes", "(1/60)^2 squaredegree", ""),
287 ("squaresecond", "squareseconds", "(1/60)^2 squareminute", ""),
288 ("squarearcmin", "squarearcmins", "squareminute", ""),
289 ("squarearcsec", "squarearcsecs", "squaresecond", ""),
290 (
291 "sphericalrightangle",
292 "sphericalrightangles",
293 "0.5 pi steradians",
294 "",
295 ),
296 ("octant", "octants", "0.5 pi steradians", ""),
297 ];
298
299 const COMMON_SI_DERIVED_UNITS: &[UnitTuple] = &[
300 ("newton", "newtons", "l@kg m / s^2", "force"),
301 ("N", "", "s@newton", ""),
302 ("pascal", "pascals", "l@N/m^2", "pressure or stress"),
303 ("Pa", "", "s@pascal", ""),
304 ("joule", "joules", "l@N m", "energy"),
305 ("J", "", "s@joule", ""),
306 ("watt", "watts", "l@J/s", "power"),
307 ("W", "", "s@watt", ""),
308 (
309 "horsepower",
310 "horsepowers",
311 "[email protected] watts",
312 "",
313 ),
314 ("hp", "", "s@horsepower", ""),
315 ("coulomb", "", "l@A s", "charge"),
316 ("C", "", "s@coulomb", ""),
317 ("volt", "volts", "l@W/A", "potential difference"),
318 ("V", "", "s@volt", ""),
319 ("Ah", "", "s@ampere hour", ""),
320 ("ohm", "ohms", "l@V/A", "electrical resistance"),
321 ("siemens", "", "l@A/V", "electrical conductance"),
322 ("S", "", "s@siemens", ""),
323 ("farad", "", "l@coulomb/V", "capacitance"),
324 ("F", "", "s@farad", ""),
325 ("weber", "", "l@V s", "magnetic flux"),
326 ("Wb", "", "s@weber", ""),
327 ("henry", "", "l@V s / A", "inductance"),
328 ("H", "", "s@henry", ""),
329 ("tesla", "", "l@Wb/m^2", "magnetic flux density"),
330 ("T", "", "s@tesla", ""),
331 ("hertz", "", "l@/s", "frequency"),
332 ("Hz", "", "s@hertz", ""),
333 ("nit", "nits", "l@candela / meter^2", "luminance"),
334 ("nt", "", "nit", ""),
335 ("lumen", "lumens", "l@cd sr", "luminous flux"),
336 ("lm", "", "s@lumen", ""),
337 ("lux", "", "l@lm/m^2", "illuminance"),
338 ("lx", "", "lux", "illuminance"),
339 ("phot", "phots", "l@1e4 lx", ""),
340 ("ph", "", "s@phot", ""),
341 ("becquerel", "becquerels", "l@/s", "radioactivity"),
342 ("Bq", "", "s@becquerel", ""),
343 ("curie", "curies", "[email protected] Bq", ""),
344 ("Ci", "", "s@curie", ""),
345 ("rutherford", "rutherfords", "l@1e6 Bq", ""),
346 ("Rd", "", "s@rutherford", ""),
347 (
348 "gray",
349 "grays",
350 "l@J/kg",
351 "absorbed dose of ionising radiation",
352 ),
353 ("Gy", "", "s@gray", ""),
354 ("rad_radiation", "", "l@1/100 Gy", ""),
355 (
356 "sievert",
357 "sieverts",
358 "l@J / kg",
359 "equivalent dose of ionising radiation",
360 ),
361 ("Sv", "", "s@sievert", ""),
362 ("rem", "", "l@1/100 Sv", ""),
363 ("roentgen", "roentgens", "[email protected] coulomb/kg", ""),
364 ("R", "", "s@roentgen", ""),
365 ];
366
367 const TIME_UNITS: &[UnitTuple] = &[
368 ("sec", "secs", "s@second", ""),
369 ("minute", "minutes", "l@60 seconds", ""),
370 ("min", "mins", "s@minute", ""),
371 ("hour", "hours", "l@60 minutes", ""),
372 ("hr", "hrs", "s@hour", ""),
373 ("h", "h", "s@hour", ""),
374 ("day", "days", "l@24 hours", ""),
375 ("d", "", "s@day", ""),
376 ("da", "", "s@day", ""),
377 ("week", "weeks", "l@7 days", ""),
378 ("wk", "", "s@week", ""),
379 ("fortnight", "fortnights", "l@14 day", ""),
380 (
381 "sidereal_year",
382 "sidereal_years",
383 "365.256363004 days",
384 concat!(
385 "the time taken for the Earth to complete one revolution of its orbit, ",
386 "as measured against a fixed frame of reference (such as the fixed stars, ",
387 "Latin sidera, singular sidus)"
388 ),
389 ),
390 (
391 "tropical_year",
392 "tropical_years",
393 "365.242198781 days",
394 "the period of time for the mean ecliptic longitude of the Sun to increase by 360 degrees",
395 ),
396 (
397 "anomalistic_year",
398 "anomalistic_years",
399 "365.259636 days",
400 "the time taken for the Earth to complete one revolution with respect to its apsides",
401 ),
402 ("year", "years", "l@tropical_year", ""),
403 ("yr", "", "year", ""),
404 ("month", "months", "l@1/12 year", ""),
405 ("mo", "", "month", ""),
406 ("decade", "decades", "10 years", ""),
407 ("century", "centuries", "100 years", ""),
408 ("millennium", "millennia", "1000 years", ""),
409 ("solar_year", "solar_years", "year", ""),
410 ("calendar_year", "calendar_years", "365 days", ""),
411 ("common_year", "common_years", "365 days", ""),
412 ("leap_year", "leap_years", "366 days", ""),
413 ("julian_year", "julian_years", "365.25 days", ""),
414 ("gregorian_year", "gregorian_years", "365.2425 days", ""),
415 // french revolutionary time
416 ("decimal_hour", "decimal_hours", "l@1/10 day", ""),
417 (
418 "decimal_minute",
419 "decimal_minutes",
420 "l@1/100 decimal_hour",
421 "",
422 ),
423 (
424 "decimal_second",
425 "decimal_seconds",
426 "l@1/100 decimal_minute",
427 "",
428 ),
429 ("beat", "beats", "l@decimal_minute", "Swatch Internet Time"),
430 ("scaramucci", "scaramuccis", "11 days", ""),
431 ("mooch", "mooches", "scaramucci", ""),
432 ];
433
434 const RATIOS: &[UnitTuple] = &[
435 ("\u{2030}", "", "=0.001", ""), // per mille
436 ("percent", "", "=0.01", ""),
437 ("%", "", "=percent", ""),
438 ("bel", "bels", "0.5 * ln(10) neper", ""),
439 ("decibel", "decibels", "1/10 bel", ""),
440 ("dB", "", "decibel", ""),
441 ("mill", "mills", "0.001", ""),
442 ("ppm", "", "1e-6", ""),
443 ("parts_per_million", "", "ppm", ""),
444 ("ppb", "", "1e-9", ""),
445 ("parts_per_billion", "", "ppb", ""),
446 ("ppt", "", "1e-12", ""),
447 ("parts_per_trillion", "", "ppt", ""),
448 ("karat", "", "1/24", "measure of gold purity"),
449 ("basispoint", "", "0.01 %", ""),
450 ];
451
452 const COMMON_PHYSICAL_UNITS: &[UnitTuple] = &[
453 ("electron_volt", "electron_volts", "l@electron_charge V", ""),
454 ("eV", "", "s@electron_volt", ""),
455 ("light_year", "light_years", "c julian_year", ""),
456 ("ly", "", "lightyear", ""),
457 ("light_second", "light_seconds", "c second", ""),
458 ("light_minute", "light_minutes", "c minute", ""),
459 ("light_hour", "light_hours", "c hour", ""),
460 ("light_day", "light_days", "c day", ""),
461 ("parsec", "parsecs", "l@au / tan(arcsec)", ""),
462 ("pc", "", "s@parsec", ""),
463 (
464 "astronomical_unit",
465 "astronomical_units",
466 "149597870700 m",
467 "",
468 ),
469 ("au", "", "astronomical_unit", ""),
470 ("AU", "", "astronomical_unit", ""),
471 ("barn", "", "l@1e-28 m^2", ""),
472 ("shed", "", "l@1e-24 barn", ""),
473 ("cc", "", "cm^3", ""),
474 ("are", "ares", "l@100 meter^2", ""),
475 ("liter", "liters", "l@1000 cc", ""),
476 ("litre", "litres", "liter", ""),
477 ("l", "", "s@liter", ""),
478 ("L", "", "s@liter", ""),
479 ("micron", "microns", "l@micrometer", ""),
480 ("bicron", "bicrons", "l@picometer", ""),
481 ("gsm", "", "grams / meter^2", ""),
482 ("hectare", "hectares", "hectoare", ""),
483 ("ha", "", "s@hectare", ""),
484 ("decare", "decares", "l@decaare", ""),
485 ("da", "", "s@decare", ""),
486 ("calorie", "calories", "[email protected] J", ""),
487 ("cal", "", "s@calorie", ""),
488 (
489 "british_thermal_unit",
490 "british_thermal_units",
491 "1055.05585 J",
492 "",
493 ),
494 ("btu", "", "british_thermal_unit", ""),
495 ("Wh", "", "s@W hour", ""),
496 ("atmosphere", "atmospheres", "l@101325 Pa", ""),
497 ("atm", "", "s@atmosphere", ""),
498 ("mmHg", "", "l@1/760 atm", "millimeter of mercury"),
499 ("inHg", "", "[email protected] mmHg", "inch of mercury"),
500 ("bar", "", "l@1e5 Pa", "about 1 atmosphere"),
501 ("diopter", "", "l@/m", "reciprocal of focal length"),
502 ("sqm", "", "=m^2", ""),
503 ("sqmm", "", "=mm^2", ""),
504 ("gongjin", "", "l@1 kilogram", ""),
505 // TODO remove these compatibility units
506 ("lightyear", "lightyears", "light_year", ""),
507 ("light", "", "c", ""),
508 ];
509
510 const CGS_UNITS: &[UnitTuple] = &[
511 ("gal", "gals", "cm/s^2", "acceleration"),
512 ("dyne", "dynes", "g*gal", "force"),
513 ("erg", "ergs", "g*cm^2/s^2", "work, energy"),
514 ("barye", "baryes", "g/(cm*s^2)", "pressure"),
515 ("poise", "poises", "g/(cm*s)", ""),
516 ("stokes", "", "cm^2/s", ""),
517 ("kayser", "kaysers", "cm^-1", ""),
518 ("biot", "biots", "10 amperes", ""),
519 ("emu", "emus", "0.001 A m^2", ""),
520 ("franklin", "franklins", "dyn^1/2*cm", ""),
521 ("gauss", "", "10^-4 tesla", ""),
522 ("maxwell", "maxwells", "10^-8 weber", ""),
523 ("phot", "phots", "10000 lux", ""),
524 ("stilb", "stilbs", "10000 candela/m^2", ""),
525 // abbrevations
526 ("gallileo", "gallileos", "gal", ""),
527 ("dyn", "dyns", "dyne", ""),
528 ("Ba", "", "barye", ""),
529 ("P", "", "poise", ""),
530 ("St", "", "stokes", ""),
531 ("K", "", "kayser", ""),
532 ("Bi", "", "biot", ""),
533 ("Fr", "", "franklin", ""),
534 ("G", "", "gauss", ""),
535 ("Mx", "", "maxwell", ""),
536 ("ph", "", "phot", ""),
537 ];
538
539 const IMPERIAL_UNITS: &[UnitTuple] = &[
540 ("inch", "inches", "2.54 cm", ""),
541 ("mil", "mils", "1/1000 inch", ""),
542 ("\u{2019}", "", "foot", ""), // unicode single quote
543 ("\u{201d}", "", "inch", ""), // unicode double quote
544 ("'", "", "foot", ""),
545 ("\"", "", "inch", ""),
546 ("foot", "feet", "l@12 inch", ""),
547 ("sqft", "", "=ft^2", ""),
548 ("yard", "yards", "l@3 ft", ""),
549 ("mile", "miles", "l@5280 ft", ""),
550 ("line", "lines", "1/12 inch", ""),
551 ("rod", "rods", "5.5 yard", ""),
552 ("pole", "poles", "rod", ""),
553 ("perch", "perches", "rod", ""),
554 ("firkin", "firkins", "90 lb", ""),
555 ("furlong", "furlongs", "40 rod", ""),
556 ("statute_mile", "statute_miles", "mile", ""),
557 ("league", "leagues", "3 mile", ""),
558 ("chain", "chains", "66 feet", ""),
559 ("link", "links", "1/100 chain", ""),
560 ("thou", "", "1/1000 inch", "thousandth of an inch"),
561 ("acre", "acres", "10 chain^2", ""),
562 ("section", "sections", "mile^2", ""),
563 ("township", "townships", "36 sections", ""),
564 ("homestead", "homesteads", "160 acres", ""),
565 ("point", "points", "l@1/72 inch", ""),
566 ("twip", "twips", "l@1/20 point", ""),
567 ("poppyseed", "poppyseeds", "l@line", ""),
568 ("pica", "picas", "l@12 points", ""),
569 ("barleycorn", "barleycorns", "l@4 poppyseed", ""),
570 ("finger", "fingers", "l@63 points", ""),
571 ("stick", "sticks", "l@2 inches", ""),
572 ("palm", "palms", "l@3 inches", ""),
573 ("digit", "digits", "l@1/4 palms", ""),
574 ("nail", "nails", "l@3 digits", ""),
575 ("span", "spans", "l@4 nails", ""),
576 ("hand", "hands", "l@2 sticks", ""),
577 ("shaftment", "shaftments", "l@2 palm", ""),
578 ("cubit", "cubits", "l@2 span", ""),
579 ("ell", "ells", "l@5 span", ""),
580 ("skein", "skeins", "l@96 ell", ""),
581 ("spindle", "spindles", "l@120 skein", ""),
582 ("link", "links", "l@1/25 rod", ""),
583 ("fathom", "fathoms", "l@2 yard", ""),
584 ("shackle", "shackles", "l@15 yard", ""),
585 ("pace", "paces", "l@5 shaftments", ""),
586 ("step", "steps", "l@2 paces", ""),
587 ("grade", "grades", "l@pace", ""),
588 ("rope", "ropes", "l@4 steps", ""),
589 ("ramsdens_chain", "", "l@5 rope", ""),
590 ("roman_mile", "roman_miles", "l@50 ramsdens_chain", ""),
591 ("gunters_chain", "gunters_chains", "l@4 rod", ""),
592 ("rack_unit", "rack_units", "[email protected] inches", ""),
593 ("U", "", "rack_unit", ""),
594 ];
595
596 const LIQUID_UNITS: &[UnitTuple] = &[
597 ("gallon", "gallons", "231 inch^3", ""),
598 ("gal", "", "gallon", ""),
599 ("quart", "quarts", "1/4 gallon", ""),
600 ("pint", "pints", "1/2 quart", ""),
601 ("cup", "cups", "1/2 pint", ""),
602 ("gill", "", "1/4 pint", ""),
603 ("fluid_ounce", "", "1/16 pint", ""),
604 ("tablespoon", "tablespoons", "1/2 floz", ""),
605 ("teaspoon", "teaspoons", "1/3 tablespoon", ""),
606 ("fluid_dram", "", "1/8 floz", ""),
607 ("qt", "", "quart", ""),
608 ("pt", "", "pint", ""),
609 ("floz", "", "fluid_ounce", ""),
610 ("tbsp", "", "tablespoon", ""),
611 ("tbs", "", "tablespoon", ""),
612 ("tsp", "", "teaspoon", ""),
613 ];
614
615 const AVOIRDUPOIS_WEIGHT: &[UnitTuple] = &[
616 ("pound", "pounds", "0.45359237 kg", ""),
617 ("lb", "lbs", "pound", ""),
618 ("grain", "grains", "1/7000 pound", ""),
619 ("ounce", "ounces", "1/16 pound", ""),
620 ("oz", "", "ounce", ""),
621 ("dram", "drams", "1/16 ounce", ""),
622 ("dr", "", "dram", ""),
623 ("hundredweight", "hundredweights", "100 pounds", ""),
624 ("cwt", "", "hundredweight", ""),
625 ("short_ton", "short_tons", "2000 pounds", ""),
626 ("quarterweight", "quarterweights", "1/4 short_ton", ""),
627 ("stone", "stones", "14 pounds", ""),
628 ("st", "", "stone", ""),
629 ];
630
631 const TROY_WEIGHT: &[UnitTuple] = &[
632 ("troy_pound", "troy_pounds", "5760 grains", ""),
633 ("troy_ounce", "troy_ounces", "1/12 troy_pound", ""),
634 ("ozt", "", "troy_ounce", ""),
635 ("pennyweight", "pennyweights", "1/20 troy_ounce", ""),
636 ("dwt", "", "pennyweight", ""),
637 ];
638
639 const OTHER_WEIGHTS: &[UnitTuple] = &[
640 ("metric_grain", "metric_grains", "50 mg", ""),
641 ("carat", "carats", "0.2 grams", ""),
642 ("ct", "", "carat", ""),
643 ("jewellers_point", "jewellers_points", "1/100 carat", ""),
644 ("tonne", "tonnes", "l@1000 kg", ""),
645 ("t", "", "tonne", ""),
646 ];
647
648 const IMPERIAL_ABBREVIATIONS: &[UnitTuple] = &[
649 ("yd", "", "yard", ""),
650 ("ch", "", "chain", ""),
651 ("ft", "", "foot", ""),
652 ("mph", "", "mile/hr", ""),
653 ("mpg", "", "mile/gal", ""),
654 ("kph", "", "km/hr", ""),
655 ("kmh", "", "km/hr", ""),
656 ("fpm", "", "ft/min", ""),
657 ("fps", "", "ft/s", ""),
658 ("rpm", "", "rev/min", ""),
659 ("rps", "", "rev/sec", ""),
660 ("mi", "", "mile", ""),
661 ("smi", "", "mile", ""),
662 ("nmi", "", "nautical_mile", ""),
663 ("mbh", "", "1e3 btu/hour", ""),
664 ("ipy", "", "inch/year", ""),
665 ("ccf", "", "100 ft^3", ""),
666 ("Mcf", "", "1000 ft^3", ""),
667 ("plf", "", "lb / foot", "pounds per linear foot"),
668 ("lbf", "", "lb force", ""),
669 ("psi", "", "pound force / inch^2", ""),
670 ("fur", "furs", "furlong", ""),
671 ("fir", "firs", "firkin", ""),
672 ("ftn", "ftns", "fortnight", ""),
673 ];
674
675 const NAUTICAL_UNITS: &[UnitTuple] = &[
676 ("fathom", "fathoms", "6 ft", ""),
677 ("nautical_mile", "nautical_miles", "1852 m", ""),
678 ("cable", "cables", "1/10 nautical_mile", ""),
679 ("marine_league", "marine_leagues", "3 nautical_mile", ""),
680 ("knot", "knots", "nautical_mile / hr", ""),
681 ("kn", "", "=knots", ""),
682 ("click", "clicks", "km", ""),
683 ("NM", "", "nautical_mile", ""),
684 ];
685
686 const CURRENCIES: &[UnitTuple] = &[
687 ("BASE_CURRENCY", "BASE_CURRENCY", "!", ""),
688 ("dollar", "dollars", "USD", ""),
689 ("cent", "cents", "0.01 USD", ""),
690 ("US$", "US$", "USD", ""),
691 ("$", "$", "USD", ""),
692 ("euro", "euros", "EUR", ""),
693 ("\u{20ac}", "\u{20ac}", "EUR", ""), // Euro symbol
694 ("\u{a3}", "\u{a3}", "GBP", ""), // £
695 ("\u{a5}", "\u{a5}", "JPY", ""), // ¥
696 ("AU$", "AU$", "AUD", ""),
697 ("HK$", "HK$", "HKD", ""),
698 ("NZ$", "NZ$", "NZD", ""),
699 ("zł", "zł", "PLN", ""), // the local abbreviation for PLN, see https://en.wikipedia.org/wiki/Polish_z%C5%82oty
700 ("zl", "zl", "PLN", ""),
701 ];
702
703 // from https://en.wikipedia.org/wiki/ISO_4217
704 const CURRENCY_IDENTIFIERS: &[&str] = &[
705 "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT",
706 "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
707 "CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP",
708 "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP",
709 "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR",
710 "ILS", "INR", "IQD", "IRR", "ISK", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW",
711 "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA",
712 "MKD", "MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", "NAD",
713 "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
714 "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE",
715 "SLL", "SOS", "SRD", "SSP", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP",
716 "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UYW", "UZS", "VED",
717 "VES", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR",
718 "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", "YER", "ZAR", "ZMW", "ZWL",
719 ];
720
721 pub(crate) const ALL_UNIT_DEFS: &[&[UnitTuple]] = &[
722 BASE_UNITS,
723 BASE_UNIT_ABBREVIATIONS,
724 TEMPERATURE_SCALES,
725 BITS_AND_BYTES,
726 STANDARD_PREFIXES,
727 NON_STANDARD_PREFIXES,
728 BINARY_PREFIXES,
729 NUMBER_WORDS,
730 CONSTANTS,
731 ANGLES,
732 SOLID_ANGLES,
733 COMMON_SI_DERIVED_UNITS,
734 TIME_UNITS,
735 RATIOS,
736 COMMON_PHYSICAL_UNITS,
737 IMPERIAL_UNITS,
738 LIQUID_UNITS,
739 AVOIRDUPOIS_WEIGHT,
740 TROY_WEIGHT,
741 OTHER_WEIGHTS,
742 IMPERIAL_ABBREVIATIONS,
743 NAUTICAL_UNITS,
744 CURRENCIES,
745 CGS_UNITS,
746 ];
747
748 const SHORT_PREFIXES: &[(&str, &str)] = &[
749 ("Ki", "sp@kibi"),
750 ("Mi", "sp@mebi"),
751 ("Gi", "sp@gibi"),
752 ("Ti", "sp@tebi"),
753 ("Pi", "sp@pebi"),
754 ("Ei", "sp@exbi"),
755 ("Zi", "sp@zebi"),
756 ("Yi", "sp@yobi"),
757 ("Y", "sp@yotta"),
758 ("Z", "sp@zetta"),
759 ("E", "sp@exa"),
760 ("P", "sp@peta"),
761 ("T", "sp@tera"),
762 ("G", "sp@giga"),
763 ("M", "sp@mega"),
764 ("k", "sp@kilo"),
765 ("h", "sp@hecto"),
766 ("da", "sp@deka"),
767 ("d", "sp@deci"),
768 ("c", "sp@centi"),
769 ("m", "sp@milli"),
770 ("u", "sp@micro"), // alternative to µ
771 ("\u{b5}", "sp@micro"), // U+00B5 (micro sign)
772 ("\u{3bc}", "sp@micro"), // U+03BC (lowercase µ)
773 ("n", "sp@nano"),
774 ("p", "sp@pico"),
775 ("f", "sp@femto"),
776 ("a", "sp@atto"),
777 ("z", "sp@zepto"),
778 ("y", "sp@yocto"),
779 ];
780
781 #[allow(clippy::too_many_lines)]
query_unit( ident: &str, short_prefixes: bool, case_sensitive: bool, ) -> Option<(Cow<'static, str>, Cow<'static, str>, Cow<'static, str>)>782 pub(crate) fn query_unit(
783 ident: &str,
784 short_prefixes: bool,
785 case_sensitive: bool,
786 ) -> Option<(Cow<'static, str>, Cow<'static, str>, Cow<'static, str>)> {
787 if short_prefixes {
788 for (name, def) in SHORT_PREFIXES {
789 if *name == ident || (!case_sensitive && name.eq_ignore_ascii_case(ident)) {
790 return Some((Cow::Borrowed(name), Cow::Borrowed(name), Cow::Borrowed(def)));
791 }
792 }
793 }
794 if let Ok(idx) = CURRENCY_IDENTIFIERS.binary_search(
795 &if case_sensitive {
796 ident.to_string()
797 } else {
798 ident.to_uppercase()
799 }
800 .as_str(),
801 ) {
802 let name = CURRENCY_IDENTIFIERS[idx];
803 return Some((
804 Cow::Borrowed(name),
805 Cow::Borrowed(name),
806 Cow::Borrowed("$CURRENCY"),
807 ));
808 }
809 let mut candidates = vec![];
810 for group in ALL_UNIT_DEFS {
811 for def in *group {
812 let def = UnitDef {
813 singular: def.0,
814 plural: if def.1.is_empty() { def.0 } else { def.1 },
815 definition: def.2,
816 };
817 if def.singular == ident || def.plural == ident {
818 return Some((
819 Cow::Borrowed(def.singular),
820 Cow::Borrowed(def.plural),
821 Cow::Borrowed(def.definition),
822 ));
823 }
824 if !case_sensitive
825 && (def.singular.eq_ignore_ascii_case(ident)
826 || def.plural.eq_ignore_ascii_case(ident))
827 {
828 candidates.push(Some((
829 Cow::Borrowed(def.singular),
830 Cow::Borrowed(def.plural),
831 Cow::Borrowed(def.definition),
832 )));
833 }
834 }
835 }
836 if candidates.len() == 1 {
837 return candidates.into_iter().next().unwrap();
838 }
839 None
840 }
841
842 const DEFAULT_UNITS: &[(&str, &str)] = &[
843 ("hertz", "second^-1"),
844 ("newton", "kilogram^1 meter^1 second^-2"),
845 ("pascal", "kilogram^1 meter^-1 second^-2"),
846 ("joule", "kilogram^1 meter^2 second^-2"),
847 ("watt", "kilogram^1 meter^2 second^-3"),
848 ("ohm", "ampere^-2 kilogram^1 meter^2 second^-3"),
849 ("volt", "ampere^-1 kilogram^1 meter^2 second^-3"),
850 ("liter", "meter^3"),
851 ];
852
lookup_default_unit(base_units: &str) -> Option<&str>853 pub(crate) fn lookup_default_unit(base_units: &str) -> Option<&str> {
854 if let Some((unit_name, _)) = DEFAULT_UNITS.iter().find(|(_, base)| *base == base_units) {
855 return Some(unit_name);
856 }
857 if let Some((singular, _, _, _)) = BASE_UNITS
858 .iter()
859 .find(|(singular, _, _, _)| format!("{singular}^1") == base_units)
860 {
861 return Some(singular);
862 }
863 None
864 }
865
866 /// used for implicit unit addition, e.g. 5'5 -> 5'5"
867 pub(crate) const IMPLICIT_UNIT_MAP: &[(&str, &str)] = &[("'", "\""), ("foot", "inches")];
868
869 #[cfg(test)]
870 mod tests {
871 use super::*;
872
test_str(s: &str, ctx: &mut crate::Context)873 fn test_str(s: &str, ctx: &mut crate::Context) {
874 if s.is_empty() || s == "'" || s == "\"" {
875 return;
876 }
877 //eprintln!("Testing '{s}'");
878 crate::evaluate(s, ctx).unwrap();
879 }
880
test_group(group: &[UnitTuple])881 fn test_group(group: &[UnitTuple]) {
882 let mut ctx = crate::Context::new();
883 ctx.set_exchange_rate_handler_v1(crate::test_utils::dummy_currency_handler);
884 for (s, p, _, _) in group {
885 test_str(s, &mut ctx);
886 test_str(p, &mut ctx);
887 }
888 }
889
890 #[test]
test_all_units()891 fn test_all_units() {
892 for &group in ALL_UNIT_DEFS {
893 test_group(group);
894 }
895 }
896
897 #[test]
currencies_sorted()898 fn currencies_sorted() {
899 let currencies = CURRENCY_IDENTIFIERS.to_vec();
900 let mut sorted = currencies.clone();
901 sorted.sort_unstable();
902 assert_eq!(currencies, sorted, "currencies are not sorted");
903 }
904
905 #[test]
lowercase_currency()906 fn lowercase_currency() {
907 assert!(query_unit("usd", true, true).is_none());
908 assert!(query_unit("usd", true, false).is_some());
909 assert!(query_unit("USD", true, false).is_some());
910 }
911 }
912