Functional Weave
Code in Rust

units.parse-quantity@1.0.0

README.md

2,421 bytes · view raw

# units.parse-quantity

Turns what a person typed into a value and a canonical unit that
`units.convert` accepts: `"2.5 kg"`, `"1,200 m"`, `"12 ft 6 in"`, `"5'11\""`.
The value is decimal text, never a float, so nothing is lost between parsing
and converting.

## Grammar

One or more parts separated by whitespace; each part is a number, optional
whitespace, then a unit. The unit runs until the next digit, so two-word units
("fl oz", "sq ft", "US gallon") work.

- Numbers are ASCII digits with an optional `.` fraction. Thousands separators
  are allowed only as strict groups of three (`1,200`, `12,345,678`); `1,20`
  is refused rather than guessed at, because in half the world it means 1.20.
  At most 15 significant digits and 15 decimal places, as in `units.convert`.
- A leading `-` is allowed on the first number only, and negates the whole
  quantity: `-5 ft 3 in` is -63 in.
- The value comes back normalised: commas removed, leading zeros and trailing
  fractional zeros trimmed, and `-0` written as `0`.

## Units

A unit is matched first as a `units.convert` symbol (`kg`, `gal_us`, `degC`),
then as an alias from `data/aliases.json` (`kilograms`, `lbs`, `feet`, `'`,
`°C`, `sq ft`), then both again ignoring ASCII letter case (`KG`, `Feet`).
Only ASCII letters fold, so the answer is the same in every language.

Words that mean different things in different places are refused with the
choices spelled out, instead of silently picking one: `gal`, `pint`, `quart`,
`fl oz` (US or imperial), `cup`, `ton` (tonne, short or long) and `Cal`/`calorie`
(a food Calorie is a kilocalorie). The case-insensitive step never hides this:
`CAL` is ambiguous even though `cal` is a symbol. A bare `oz` is the
avoirdupois ounce of mass; say `US fl oz` for volume.

Every alias names a symbol that `units.convert` defines; the implementation
reads `units.convert`'s own table, so the two cannot drift apart.

## Compound quantities

`12 ft 6 in` is returned as `150 in`: the parts are summed exactly into the
last, smallest unit. For that to be exact, the rules are strict:

- every part has the same dimension, and temperatures are never compound;
- units run from largest to smallest and never repeat;
- only the last number may have a fraction (`5 ft 11.5 in`, not `5.5 ft 3 in`);
- each earlier unit must be a whole number of the last one, so `1 ft 2 cm`
  (30.48 cm per foot) is refused rather than approximated.