Better packaging (#158)

I want rink to adhere slightly better to conventions on Linux.

- Adds man pages for the CLI and for the config file format, using
asciidoc.
- Updates the bundling functionality. It doesn't make sense to bundle
these files when building a distro package, they should go in
`/usr/share`.
- Adds a makefile with good defaults for distro packages.

The asciidoc files will likely become the new source of truth for rink's
documentation. There is no way to make PRs to github wikis, so this
might be for the best anyway.

Currently rink is packaged on arch, nix, and void linux.
This commit is contained in:
Tiffany Bennett 2024-03-29 19:15:53 -07:00 committed by GitHub
parent c33040c746
commit bf3b960307
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1456 additions and 27 deletions

59
Makefile Normal file
View file

@ -0,0 +1,59 @@
SHELL = /bin/sh
CARGO := cargo
FETCHFLAGS := --locked
CARGOFLAGS := --release --locked --offline --no-default-features
ASCIIDOCTOR := asciidoctor
MANFLAGS := -b manpage -D build
HTMLFLAGS := -D build -a toc=left -a toclevels=3 -a sectlinks
INSTALL := install
prefix := /usr/local
DESTDIR := $(prefix)
bindir := $(DESTDIR)/bin
datarootdir := $(DESTDIR)/share
datadir := $(datarootdir)
mandir := $(datarootdir)/man
man1dir := $(mandir)/man1
man5dir := $(mandir)/man5
man7dir := $(mandir)/man7
srcdir := .
RINK_PATH := $(prefix)/share/rink
export RINK_PATH
all: bin man
fetch:
$(CARGO) fetch $(FETCHFLAGS)
bin:
$(CARGO) build $(CARGOFLAGS) -p rink
test:
$(CARGO) test $(CARGOFLAGS) --all
man:
$(ASCIIDOCTOR) $(MANFLAGS) $(srcdir)/docs/rink.1.adoc
$(ASCIIDOCTOR) $(MANFLAGS) $(srcdir)/docs/rink.5.adoc
$(ASCIIDOCTOR) $(MANFLAGS) $(srcdir)/docs/rink.7.adoc
$(ASCIIDOCTOR) $(MANFLAGS) $(srcdir)/docs/rink-defs.5.adoc
$(ASCIIDOCTOR) $(MANFLAGS) $(srcdir)/docs/rink-dates.5.adoc
htmldoc:
$(ASCIIDOCTOR) $(HTMLFLAGS) $(srcdir)/docs/rink.1.adoc
$(ASCIIDOCTOR) $(HTMLFLAGS) $(srcdir)/docs/rink.5.adoc
$(ASCIIDOCTOR) $(HTMLFLAGS) $(srcdir)/docs/rink.7.adoc
$(ASCIIDOCTOR) $(HTMLFLAGS) $(srcdir)/docs/rink-defs.5.adoc
$(ASCIIDOCTOR) $(HTMLFLAGS) $(srcdir)/docs/rink-dates.5.adoc
install: all
$(INSTALL) -Dm 0755 target/release/rink -t $(bindir)
$(INSTALL) -Dm 0644 $(srcdir)/core/definitions.units -t $(datadir)/rink
$(INSTALL) -Dm 0644 $(srcdir)/core/datepatterns.txt -t $(datadir)/rink
$(INSTALL) -Dm 0644 $(srcdir)/core/currency.units -t $(datadir)/rink
$(INSTALL) -Dm 0644 build/rink.1 -t $(man1dir)
$(INSTALL) -Dm 0644 build/rink.5 -t $(man5dir)
$(INSTALL) -Dm 0644 build/rink.7 -t $(man7dir)
$(INSTALL) -Dm 0644 build/rink-defs.5 -t $(man5dir)
$(INSTALL) -Dm 0644 build/rink-dates.5 -t $(man5dir)

46
PACKAGING.md Normal file
View file

@ -0,0 +1,46 @@
# Distro Packaging
Requirements:
- Rust compiler toolchain (stable build)
- `asciidoctor` for building manpages
- `make`
Rink requires several data files in order to work. By default these are
baked into the binary so that `cargo install` will work, but for distro
packaging these should probably go into `/usr/share` instead. The
makefile will do this automatically.
## Makefile-based method
```sh
make fetch
make all prefix=/usr
make install prefix=/usr DESTDIR=$pkgdir
```
A makefile is provided for easier packaging.
Running `make all` will build both the program and the man pages, and
`make install` will install them.
Note that the makefile accepts _both_ the `prefix` and `DESTDIR`
arguments. `prefix` is where Rink should look for its files, this should
generally be `/usr`. `DESTDIR` is where the files will be copied to upon
running `make install`.
## Manual packaging
Build the program using cargo. The RINK_PATH environment variable can be
set to something like `/usr/share/rink` so that rink will look in this
directory for its files. Passing `--no-default-features` will turn off
rink bundling the files into its executable file.
Build the manpages in `docs/` using asciidoctor.
1. Install `target/release/rink` -> `/usr/bin/rink`.
2. Install `core/definitions.units` ->
`/usr/share/rink/definitions.units`.
3. Install `core/currency.units` -> `/usr/share/rink/currency.units`.
4. Install `datepatterns.txt` -> `/usr/share/rink/datepatterns.txt`.
5. Install the manpages into the relevant directories.

View file

@ -10,6 +10,10 @@ keywords = ["unit", "math", "conversion", "cli", "tool"]
categories = ["command-line-utilities", "mathematics", "science"]
edition = "2018"
[features]
default = ["bundle-files"]
bundle-files = ["rink-core/bundle-files"]
[dependencies]
clap = "3"
dirs = "4"

View file

@ -254,6 +254,21 @@ fn load_live_currency(config: &Currency) -> Result<ast::Defs> {
serde_json::from_str(&contents).wrap_err("Invalid JSON")
}
fn try_load_currency(config: &Currency, ctx: &mut Context, search_path: &[PathBuf]) -> Result<()> {
let base = read_from_search_path("currency.units", search_path)
.or_else(|err| CURRENCY_FILE.map(ToOwned::to_owned).ok_or(err)).wrap_err("Rink was not built with a bundled currency.units file, and one was not found in the search path.")?;
let mut base_defs = gnu_units::parse_str(&base);
let mut live_defs = load_live_currency(config)?;
let mut defs = vec![];
defs.append(&mut base_defs.defs);
defs.append(&mut live_defs.defs);
ctx.load(ast::Defs { defs }).map_err(|err| eyre!(err))?;
Ok(())
}
pub fn read_config() -> Result<Config> {
match read_to_string(config_path("config.toml")?) {
// Hard fail if the file has invalid TOML.
@ -271,14 +286,17 @@ pub fn load(config: &Config) -> Result<Context> {
if let Some(config_dir) = dirs::config_dir() {
search_path.push(config_dir);
}
if let Some(prefix) = option_env!("RINK_PATH") {
search_path.push(prefix.into());
}
// Read definitions.units
let units = read_from_search_path("definitions.units", &search_path)
.or_else(|err| DEFAULT_FILE.map(ToOwned::to_owned).ok_or(err).wrap_err("Rink was not built with a bundled definitions file, and one was not found in the search path."))?;
.or_else(|err| DEFAULT_FILE.map(ToOwned::to_owned).ok_or(err).wrap_err("Rink was not built with a bundled definitions.units file, and one was not found in the search path."))?;
// Read datepatterns.txt
let dates = read_from_search_path("datepatterns.txt", &search_path)
.unwrap_or_else(|_| DATES_FILE.to_owned());
.or_else(|err| DATES_FILE.map(ToOwned::to_owned).ok_or(err).wrap_err("Rink was not built with a bundled datepatterns.txt file, and one was not found in the search path."))?;
let mut ctx = Context::new();
ctx.save_previous_result = true;
@ -288,17 +306,8 @@ pub fn load(config: &Config) -> Result<Context> {
// Load currency data.
if config.currency.enabled {
match load_live_currency(&config.currency) {
Ok(mut live_defs) => {
let mut base_defs = gnu_units::parse_str(
&read_from_search_path("currency.units", &search_path)
.unwrap_or_else(|_| CURRENCY_FILE.to_owned()),
);
let mut defs = vec![];
defs.append(&mut base_defs.defs);
defs.append(&mut live_defs.defs);
ctx.load(ast::Defs { defs }).map_err(|err| eyre!(err))?;
}
match try_load_currency(&config.currency, &mut ctx, &search_path) {
Ok(()) => (),
Err(err) => {
println!("{:?}", err.wrap_err("Failed to load currency data"));
}

View file

@ -10,8 +10,8 @@ keywords = ["unit", "math", "conversion"]
edition = "2018"
[features]
default = ["chrono-humanize", "gpl"]
gpl = []
default = ["chrono-humanize"]
bundle-files = []
[dependencies]
num = { version = "0.4.0", features = ["serde"] }

View file

@ -9,13 +9,20 @@ use crate::{
Context,
};
#[cfg(feature = "gpl")]
#[cfg(feature = "bundle-files")]
pub static DEFAULT_FILE: Option<&'static str> = Some(include_str!("../definitions.units"));
#[cfg(not(feature = "gpl"))]
#[cfg(not(feature = "bundle-files"))]
pub static DEFAULT_FILE: Option<&'static str> = None;
pub static DATES_FILE: &str = include_str!("../datepatterns.txt");
pub static CURRENCY_FILE: &str = include_str!("../currency.units");
#[cfg(feature = "bundle-files")]
pub static DATES_FILE: Option<&'static str> = Some(include_str!("../datepatterns.txt"));
#[cfg(not(feature = "bundle-files"))]
pub static DATES_FILE: Option<&'static str> = None;
#[cfg(feature = "bundle-files")]
pub static CURRENCY_FILE: Option<&'static str> = Some(include_str!("../currency.units"));
#[cfg(not(feature = "bundle-files"))]
pub static CURRENCY_FILE: Option<&'static str> = None;
pub fn eval(ctx: &mut Context, line: &str) -> Result<QueryReply, QueryError> {
ctx.update_time();
@ -41,18 +48,17 @@ pub fn one_line(ctx: &mut Context, line: &str) -> Result<String, String> {
}
/// Tries to create a context that has core definitions only (contents
/// of definitions.units), will fail if the GPL feature isn't enabled.
/// of definitions.units), will fail if the bundle-files feature isn't enabled.
/// Mainly intended for unit testing.
pub fn simple_context() -> Result<Context, String> {
let units = match DEFAULT_FILE {
Some(units) => units,
None => return Err("GPL feature not enabled, cannot create simple context.".to_owned()),
};
let message = "bundle-files feature not enabled, cannot create simple context.";
let units = DEFAULT_FILE.ok_or(message.to_owned())?;
let mut iter = gnu_units::TokenIterator::new(&*units).peekable();
let units = gnu_units::parse(&mut iter);
let dates = crate::parsing::datetime::parse_datefile(DATES_FILE);
let dates = DATES_FILE.ok_or(message.to_owned())?;
let dates = crate::parsing::datetime::parse_datefile(dates);
let mut ctx = Context::new();
ctx.load(units)?;

119
docs/rink-dates.5.adoc Normal file
View file

@ -0,0 +1,119 @@
= rink-dates(5)
:manmanual: Rink Manual
:mansource: Rink Manual
Name
----
rink-dates - Rink file format for date patterns
Synposis
--------
Rink allows specifying datetimes using **\#date#** syntax in queries. This
file defines the patterns that are used to try to match the specified
date.
Description
-----------
Blank lines are ignored. Lines starting with # are ignored.
A date pattern is a sequence of keywords which each specify a token to
match. part of the sequence can be wrapped with [] to make it optional.
The valid keywords are:
**fullyear**::
The full 4 digit year, like 2024. Must be exactly 4 digits.
**shortyear**::
Shortened 2-digit year, like 24. Must be exactly 2 digits.
**century**::
The year with the last 2 digits cut off, like 20. Not the same as
the actual century number (which would be 21). Must be exactly 2 digits.
**monthnum**::
The current month number, like 03. Must be exactly 2 digits.
**day**::
The current day of the month, like 3. Can be any number of digits.
**fullday**::
The current day of the month, like 03. Must be exactly 2 digits.
**min**::
The current minute of the hour, like 05. Must be exactly 2 digits.
**ordinal**::
The current day of the year, like 083. Must be exactly 3 digits.
**isoyear**::
The ISO year, like -0001. Must be exactly 4 digits. ISO year unifies
CE and BCE such that 1BC is year 0, and 2BCE is year -1.
**unix**::
Unix timestamp, i.e. the number of 1/86400ths of a day elapsed since
January 1st, 1970. Can be any number of digits.
**year**::
The current year, like 2024. Can be any number of digits.
**adbc**::
Looks for `ad`, `ce`, `bc`, or `bce` (case insensitive). This allows
specifying dates prior to year 1 CE.
**hour12**::
The current hour on a 12-hour clock. Must be exactly 2 digits.
**hour24**::
The current hour on a 24-hour clock. Must be exactly 2 digits.
**meridiem**::
Looks for `am` or `pm` (case insensitive). This allows specifying
12-hour time.
**sec**::
The current second of the current minute. Must be exactly 2 digits.
Can optionally have a decimal point to specify time smaller than a
second.
**offset**::
Matches a timezone offset. This can either be the name of a timezone
like `US/Pacific` or a fixed offset. The fixed offset can either be
in the form +07:00 or +0700.
**monthname**::
Matches English month names, case insensitive. Recognizes 3-letter
names (like jan, feb, mar) and full names.
**weekday**::
Makes English weekday names, case insensitive. Recognizes 3-letter
names (like mon, tue, wed) and full names.
**`-`**::
Matches a literal `-` character.
**`:`**::
Matches a literal `:` character.
` `::
A single space will match any amount of whitespace.
**`'`** <__anything__> **`'`**::
Wrapping text in single quotes will match that text literally.
Files
-----
Rink searches the following locations:
* `./rink/datepatterns.txt`
* `__$XDG_CONFIG_DIR__/rink/datepatterns.txt`
* `/usr/share/rink/datepatterns.txt`
See also
--------
xref:rink.1.adoc[rink(1)], xref:rink.5.adoc[rink(5)],
xref:rink.7.adoc[rink(7)], xref:rink-defs.5.adoc[rink-defs(5)]

278
docs/rink-defs.5.adoc Normal file
View file

@ -0,0 +1,278 @@
= rink-defs(5)
:manmanual: Rink Manual
:mansource: Rink Manual
Name
----
rink-defs - Rink's `definition.units` format
Description
-----------
The units database is a textual document containing the definitions of
units, prefixes, base units, quantities, and substances.
Comments start with `#` and continue to the end of the line. Blank lines
are ignored. Comments can be placed on the same line as units, at the
end.
[listing]
# This line is a comment
# And so is this
Units
~~~~~
Units are defined with their name and their definition separated by
whitespace. A definition can be broken over multiple lines using `\` if
desired.
[listing]
foot 12 inch
Documentation comments are lines starting with `??`. They apply to the
next definition following them. They are shown to end users in Rink when
you query a definition.
[listing]
?? International yard and pound, since July 1, 1959.
?? Documentation comments can be broken over
?? multiple lines.
foot 12 inch
Substances
~~~~~~~~~~
Substances can represent either a specific object (such as the Earth, or
the Sun), a material such as water or concrete, a chemical molecule, an
element of the periodic table, or an elementary particle.
A substance has a name, an optional symbol (used for chemical formulas),
and contains a list of properties inside of a {} block.
Properties can either be "const", which is mainly used for countable
objects such as a particle, or they can be a ratio, such as the density
of a material.
[listing]
neutron {
mass const neutron_mass 1.00866491588 u
wavelength const neutron_wavelength planck_constant / mass c
magnetic_moment const neutron_moment -0.96623650e-26 J/T
}
The two syntax variants for properties are:
[listing]
[name] const [output name] [expression]
[name] [input name] [input expression] / [output name] [output expression]
Categories
~~~~~~~~~~
Units inside the same category will be grouped together in the UI when
doing searches and other operations. A category is started using the
`!category` pragma which takes an identifier (`lower_snake_case`) and a
display name as a string literal.
[listing]
!category us_survey "US survey measures"
Base units
~~~~~~~~~~
Base units are impossible to define in terms of another unit without
creating a cycle. They represent fundamental physical quantities such as
time, length, temperature, etc.
The name is a shorthand form which is used when showing dimensionality
(for example, the dimensionality of `acceleration` is `m / s^2`), while
the long form is used for display purposes.
New base units should only be added when there is a clear value to doing
so. Examples include radians and bits.
[listing]
?? Equal to the mass of the international prototype of the
?? kilogram. 3rd CGPM (1901, CR, 70).
kg !kilogram
Quantities
~~~~~~~~~~
Quantities are shown in parentheses whenever rink displays a unit.
They're very helpful for dimensional analysis, as it can be hard to
remember that `kg m^2 / s^2` means the physical quantity of energy.
Quantities are defined similar to units, but the definition starts with
a ? character. Quantities are in a separate namespace from units, so the
only valid names that can be used are the base units as well as other
quantities.
Unlike units, quantities have no numerical value (not even a value of
1 like SI derived units).
[listing]
length ? meter
area ? length^2
Prefixes
~~~~~~~~
Prefixes allow input of units like `gigabytes`, `megameters`,
`kiloseconds` without needing to explicitly define each one in the units
database. Prefixes are always dimensionless.
These should not be added frequently, only when it's relevant to some
counting system. Examples include power of two byte prefixes (MiB, GiB,
etc.) and Dozenal prefixes.
Prefixes are split into two types, "long" prefixes and "short" prefixes.
Short prefixes should generally only be 1 letter. Typically, short
prefixes are aliases of their corresponding long prefix.
Prefixes are defined similar to other units, but the name ends with a
suffix of `-` (long prefixes) or `--` (short prefixes).
Prefixes use their own namespace. Their definitions can only reference
other prefixes, and not units.
[listing]
kilo- 1e3
k-- kilo
Guidelines
----------
These guidelines mainly apply when contributing to the upstream
`definitions.units`.
The units database should generally be wrapped so that it fits within
80 columns, for ease of reading. Similarly, units definitions should
generally be aligned vertically using spaces.
Non-trivial units should generally have a documentation comment. This
comment can explain what the unit means, as well as providing a citation
for its value.
If there are multiple ways to interpret how a unit should be defined,
then mention which interpretation is being used. Unless it's implied by
the name (e.g. `siderealyear`). Examples where units have multiple
interpretations include:
* Customary units may vary by country, and may even vary depending on
which year in that country. Such as US, British, Australian, and
International definitions of the foot.
* Units that are inherently fuzzy or represent multiple sources. This
may include days (calendar, sidereal), years (calendar, sidereal,
tropical), and others.
Doc comments should be written in US English. They should have sentence
capitalization and full stops, like this guide is written in. Avoid
run-on sentences.
External links should include the protocol part (`https://`, `http://`).
They should be wrapped with angle brackets, like `<https://example.com>`.
Dates should be written in a longhand format such as `July 5th, 1982`.
Do not use ambiguous formats like 2/3/99. It's often sufficient to only
state the year.
Comments in the definitions file are written for the benefit of other
maintainers. They can include explanations for why units are defined a
certain way. They can also state that certain units are part of a group
or set.
All units should be inside of a `!category` / `!endcategory` block.
Category blocks should also enclose comments related to that category,
and the `!category` pragma should immediately follow the last category's
`!endcategory`. This is to allow the file to be easily browsed using Vim
folds. The display name of a category should be in sentence case, and
should be aligned to the 70th column.
NAMING
~~~~~~
English names should be lowercase without separators. Words may be
separated by underscores when it adds clarity. Examples include `foot`,
`olympiccubit`, `usgallon`.
If a shorthand is available, it should be added as an alias of the
longer name. Examples include `ft` for `foot`, `B` for `byte`, and `Ω`
for `ohm`.
[listing]
ft foot
Some units are most commonly written in a non-Latin script. Use the
non-Latin name as the canonical name, and add an ASCII-based one as an
alias. Examples include `золотник`, `分地`.
Some units are typically written with a symbol. Treat these similar to
the non-Latin script names. Examples include `π` (Pi), `τ` (Tau).
Legacy Unicode symbols should only be used as aliases of more standard
names. This includes uncommon symbols such as `㎒` (Unicode symbol for
Megahertz).
If there are multiple names for a unit, then the one that's most typical
should be the "canonical name". The canonical version should have the
full definition, and the other names should be added as aliases pointing
to the canonical version. Avoid duplicating the definition.
DEFINITIONS
~~~~~~~~~~~
Units should be defined in terms of other related units when possible.
The expression you use to define the unit will be visible to the end
user. For example, a foot is defined as `12 inch` rather than as `304.8
mm`. This is because there is already a separate entry for `inch`
defined as `2.54 mm`. When displaying a unit's definition, Rink shows
both the original definition as well as the absolute value. So for
`foot` it shows that it's defined as `12 inch` which equals `304.8
millimeter`.
Rink can represent arbitrary precision rational numbers. The only
limitation is how much memory is available. As a result, irrational
numbers like Pi or Euler's constant should be defined to at least 20
digits.
Universal constants that are measured experimentally should have as many
significant figures as are currently known. For example, if a number is
known to ±0.000003, then it should be listed to 6 digits after the
decimal point.
Files
-----
Rink searches for the definitions file in these locations:
* `./rink/definitions.units`
* `$XDG_CONFIG_DIR/rink/definitions.units`
* `/usr/share/rink/definitions.units`
When live currency fetching is enabled, Rink also looks for a currency
file in these locations:
* `./rink/currency.units`
* `$XDG_CONFIG_DIR/rink/currency.units`
* `/usr/share/rink/currency.units`
History
-------
Rink's units database was originally based on GNU Units and inherits
much of its syntax from there.
Notable differences include:
- Removal of `!locale`, `!set`, `!utf8`, and other pragmas not used by Rink.
- Addition of `!category` and `!endcategory`.
- Addition of documentation comments starting with `??`.
- Addition of substances.
See also
--------
xref:rink.1.adoc[rink(1)], xref:rink.5.adoc[rink(5)],
xref:rink.7.adoc[rink(7)], xref:rink-dates.5.adoc[rink-dates(5)]

70
docs/rink.1.adoc Normal file
View file

@ -0,0 +1,70 @@
= rink(1)
:manmanual: Rink Manual
:mansource: Rink Manual
Name
----
rink - Unit calculator and dimensional analysis tool
Synposis
--------
[verse]
**rink**
**rink** [_EXPR_]...
**rink -f** <__FILE__>
**rink** (**-h** | **-V** | **--config-path**)
Description
-----------
Rink is a unit conversion and calculation tool which can be used for
both small and simple arithmetic and more complex dimensionality
analysis and other tasks.
Options
-------
**--config-path**::
Prints a path to the config file, then exits.
**-f**::
**--file** <__file__>::
Reads expressions from a file, one per line, printing them to stdout
and then exitting.
**-h**::
**--help**::
Print help information and then exits.
**-V**::
**--version**::
Prints the current version and then exits.
Exit status
------------
Returns 0 unless an error prevented rink from starting up.
Environment
-----------
**NO_COLORS**::
If set to a non-empty string, rink will disable colored text
rendering.
Files
-----
Rink looks for a configuration file in
`$XDG_CONFIG_DIR/rink/config.toml`, see rink(5).
Rink relies on some data files, which are found using a search path.
See rink-defs(5) and rink-dates(5).
Bugs
----
<https://github.com/tiffany352/rink-rs/issues>
See also
--------
xref:rink.5.adoc[rink(5)], xref:rink.7.adoc[rink(7)],
xref:rink-defs.5.adoc[rink-defs(5)],
xref:rink-dates.5.adoc[rink-dates(5)]

153
docs/rink.5.adoc Normal file
View file

@ -0,0 +1,153 @@
= rink(5)
:manmanual: Rink Manual
:mansource: Rink Manual
Name
----
rink - TOML configuration file format.
Description
-----------
Rink's configuration file uses the TOML format.
Duration types accept common suffixes like `ms`, `s`, `h`, `d`, `y`.
Size types accept suffixes like `MB`.
Color strings are a set of keywords separated by spaces. The following
keywords are understood:
*black*, *red*, *green*, *yellow*, *blue*, *purple*, *cyan*, *white*::
Sets the color to that ANSI color.
*dim*/*dimmed*::
Uses a dimmed variant of the color instead of the bright variant.
*bold*, *italic*, *underline*/*under*, *strikethrough*::
Formatting.
*hidden*/*none*::
Makes the text invisible.
*on*::
The color keyword after this will apply to the background instead of
the foreground. Example: `black on red`.
*default*/*plain*::
Results in unstyled text.
integers 0 to 255::
Extended terminal color palette codes.
*#* <__6 hex letters__>::
Hex codes can be used to specify truecolor.
Example: `#000000`
*rgb(* <__red__> *,* <__green__> *,* <__blue__> *)*::
RGB values can be used to specify truecolor. No spaces are allowed.
Example: `rgb(10,10,10)`
Rink
~~~~
The `[rink]` section.
*prompt* = <__string__>::
The text that will be displayed before the cursor, to hint
interactivity. Should include the space character.
Default: `"> "`
*long_output* = <__bool__>::
Breaks lists, such as search results, over multiple lines. Requires
a Unicode-capable terminal.
Default: `false`
Currency
~~~~~~~~
The `[currency]` section.
*enabled* = <__bool__>::
Currency fetching can be disabled for those that don't want it.
Default: `true`
*endpoint* = <__url__>::
Allows pointing to alternate Rink-Web instances, or to any other API
that offers a compatible format.
Default: `"https://rinkcalc.app/data/currency.json"`
*timeout* = <__duration__>::
How long to wait for currency fetching before giving up.
Default: `"2s"`
*cache_duration* = <__duration__>::
How long to wait before considering the cached currency data stale.
Default: `"1h"`
Colors
~~~~~~
The `[colors]` section.
*enabled* = <__bool__>::
Set to true to turn on colored output.
Default: `true`, or `false` if the `NO_COLOR` environment variable is set.
*theme* = <__string__>::
Sets the active theme. See the THEMES section.
Default: `"default"`
Themes
~~~~~~
The `[themes]` section. This section is a dictionary, each theme should be
created as `[themes.my_theme]`. These options are specific to each.
*plain* = <__color__>::
Generic text. This will be used often.
Default: `"default"`
*error* = <__color__>::
Error messages.
Default: `"red"`
*unit* = <__color__>::
The names of units, like `kilogram`.
Default: `"cyan"`
*quantity* = <__color__>::
The names of physical quantities, like `length` and `time`. These
are shown in parentheses on every query.
Default: `"dimmed cyan"`
*number* = <__color__>::
Numbers that appear in outputs.
Default: `"default"`
*user_input* = <__color__>::
Used when rink is quoting user input back, such as in unit not found
errors.
Default: `"bold"`
*doc_string* = <__color__>::
Used when rink is showing informational text that's part of the
definition of a unit, like `meter`.
Default: `"italic"`
*pow* = <__color__>::
The `^2` in `m/s^2`.
Default: `"default"`
*prop_name* = <__color__>::
Names of properties in substances, like the `speed` in `speed of
light`.
Default: `"cyan"`
*date_time* = <__color__>::
Date time objects, that can be obtained with the hash notation or
`now`.
Default: `"default"`
Files
-----
Linux::
++__$XDG_CONFIG_DIR__/rink/config.toml++
Windows::
++__{FOLDERID_RoamingAppData}__\rink\config.toml++
macOS::
++__$HOME__/Library/Application Support/rink/config.toml++
See also
--------
xref:rink.1.adoc[rink(1)], xref:rink.7.adoc[rink(7)],
xref:rink-defs.5.adoc[rink-defs(5)],
xref:rink-dates.5.adoc[rink-dates(5)]

685
docs/rink.7.adoc Normal file
View file

@ -0,0 +1,685 @@
= rink(7)
:manmanual: Rink Manual
:mansource: Rink Manual
Name
----
rink - Query language documentation
Synposis
--------
Rink's query language follows a somewhat natural language style for its
syntax. Many expressions like *3.5 meters to feet* will work without any
modification. This manual is meant for those who want to get a deeper
understanding of the query language.
Description
-----------
In order to understand Rink, an understanding of units themselves is
required. Fundamentally, a *unit* is a way of assigning a concrete
value to a specific *quantity* such as length, volume, energy, power,
current, etc. Each *quantity* can be reduced into other quantities
(for example, area is length × length), except for 7 *base units*.
The 7 *base units* (as well as the physical quantities they represent):
* meter (length)
* second (time)
* kilogram (mass)
* ampere (current)
* kelvin (temperature)
* mol (amount of substance)
* candela (human-subjective luminous intensity)
In addition, Rink defines a few non-SI base units:
* euro (money)
* bit (information)
* radian (angle)
* steradian (solid angle)
* wholenote (musical note length)
* IU (biological activity)
Each of these quantities is treated as irreducible. The 7 base units
are the foundations of SI, and customary systems as well. (Customary
systems are defined in terms of SI.)
Every *unit* is composed of two parts: A numerical value, and its
*dimensionality*. The dimensionality is how a unit relates itself to
the *base units*. Each base unit is raised to a certain power to
construct the dimensionality. For example, the dimensionality of the
quantity of acceleration is `length^1` × `time^-2` and then the rest of
the base units are to the 0th power, which is to say that they do not
matter. Two units are considered *conformable* if they have matching
dimensionalities, and they can then be used in conversions.
Because each unit has a numerical part, it is possible to do normal
math on them*.
- Adding two units produces a new unit with matching dimensionality.
- Multiplying two units produces a new unit with its dimensionality as
each base unit multiplied together, e.g. velocity (`length time^-1`) *
hertz (`time^-1`) = acceleration (`length time^-2`).
- Dividing two units is like multiplication, but taking away from the
base units. A unit divided by itself is *dimensionless*, it has no
quantity. Normal numbers are dimensionless.
Because of this, units are essentially a special type of number. As
such, Rink is essentially a calculator which takes dimensionality into
account.
Weight vs Mass
~~~~~~~~~~~~~~
It is important to remember the differences between mass and weight
when working with mass and force units. Here are some tips:
- Mass doesn't change depending on the amount of gravity, and directly
influences momentum.
- Weight is the amount of downward force on an object due to gravity.
- Mass is measured in kilograms or pounds.
- Weight is measured in newtons, kilogram force (kgf), or pound force
(lbf).
- When someone says something weighs some amount of kg or lb, they're
saying it has a weight of that number of kgf or lbf. This includes
things like weight on the moon. (Don't correct anyone using this
common figure of speech.)
- A scale displays an estimate of mass by measuring the force applied
to it divided by its calibrated measurement of the acceleration of
gravity. Its mass estimate would be incorrect on other planets
unless it was recalibrated. You can also think of the displayed
value as being weight in kgf or lbf.
- You can compute weight by multiplying mass by gravity. Both kgf and
lbf have earth gravity as part of their definition, so when you
multiply kg or lb by gravity you get the same numerical values back,
but with kgf or lbf units.
Numbers
~~~~~~~
> 10.1e2
1010 (dimensionless)
> 10
10 (dimensionless)
> 0x10
16 (dimensionless)
> 0o10
8 (dimensionless)
> 0b10
2 (dimensionless)
> 9999999999999
approx. 9.99999e12 (dimensionless)
> 1.001
1.001 (dimensionless)
> 1e100
1.0e100 (dimensionless)
Decimal numbers can be written with an integer component, an
after-decimal-point component, and an exponent. Numbers can optionally
have either `U+2009 THIN SPACE` or an underscore (`_`) for digit place
separators.
The decimal point is always written with a dot (`.`), not a comma or
other marker. If the decimal point is provided, it must be followed by
more digits. (`1.` is not allowed.)
The exponent starts with an `e`, followed by an integer with an
optional sign. The exponent is shorthand for writing out `*
10^exp`. There can be no spaces within the number other than allowed
digit separators. (`10 e10` is not allowed.)
Hexadecimal, octal, and binary integers can be written using `0x`,
`0o`, and `0b` prefixes, respectively. These literals do not currently
support decimal points or exponents.
Numbers can be written with a fraction, and can be written in
scientific notation. `1e24` is short for `1 * 10^24`.
Arithmetic
~~~~~~~~~~
> 3 4 m 5 s
60 m s
> 3 * 4 m 5 s
60 m s
Multiplication can be either by juxtaposition (that is, without any
symbol) or using an explicit * operator.
> 10 km / 5 m
2000 (dimensionless)
> 1|2 m
0.5 m (length)
There are two division operators, for separate purposes. `/` has lower
precedence than multiplication, and is used mainly for separating two
halves of an entire expression. `|` has higher precedence than
multiplication, and is used mainly for fractions of integers.
> 1 * 2 + 1 * 2
4 (dimensionless)
> 12 meters + 5 feet
13.524 m (length)
Addition and subtraction have lower precedence than multiplication and
division.
> 12 ft^2
435483/390625, approx. 1.114836 m^2 (area)
Powers have higher precedence than multiplication. Both `^` and `**`
can be used.
Temperature
~~~~~~~~~~~
> 12 °C
285.15 K (temperature)
Temperature scales are operators with higher precedence than addition,
and lower than multiplication. See the <<temperature-conversions>>
section for more detailed syntax.
Inline Definitions
~~~~~~~~~~~~~~~~~~
> 2000 kcal -> potato = 164 kcal
500/41, approx. 12.19512 potato (energy)
An equals expression is one which simultaneously defines a new unit
with the right-hand side, names it using the left-hand side, and then
produces it as its result. This is useful for customizing the output
of the right-hand side of a conversion or converting into things that
don't currently have units such as the amount of calories in a potato.
Custom base units
~~~~~~~~~~~~~~~~~
> 12 'core' hour / 3 'core' -> minutes
240 minutes (time)
A unit name which is wrapped in quotation marks will not be checked
for whether it exists when it is evaluated. This means you can wrap
anything in quotes to in effect produce a new *base unit* for the
purposes of a single calculation. This can be useful for doing
calculations in terms of things which are otherwise dimensionless.
Previous results
~~~~~~~~~~~~~~~~
> 100 ohm + 50 ohm
150 ohm (resistance)
> ANS * 10 mA
1.5 volt (electrical_potential)
The result of the previous query can be accessed with `_`, `ans` or
`ANS`, which can be convenient for breaking up calculations into
multiple steps. Note that when rink returns an error occurs, the
previous result is kept. Also, currently only the results of
mathematical expressions are stored, the results for conversions aren't.
Prefixes
~~~~~~~~
Units can be prefixed with SI prefixes as well as a number of non-SI
prefixes, such as: quarter, double, kibi, mebi, ⅞.
Rink will accept plural unit names as well.
Conversions
~~~~~~~~~~~
> meter -> feet
3.280839 foot (length)
> 12 °C -> °F
53.6 °F (temperature)
Conversions are done with the `->`, `to`, `in` operators.
The left hand side of the conversion is an arbitrary expression, and
the right hand side is one of:
- An arbitrary expression
- A temperature scale (celsius, fahrenheit, and several historical
scales)
- A unit list (e.g. `hour;min;sec`)
- A timezone (e.g. `"US/Pacific"`)
Unit lists
^^^^^^^^^^
> 2^17 seconds -> hour;min;sec
36 hour, 24 minute, 32 s (time)
> 2 km -> mi;ft
1 mile, 1281.679 foot (length)
> liter -> cup;tbsp;tsp
4 uscup, 3 ustablespoon, 1.884136 usteaspoon (volume)
A unit list is a comma- or semicolon- delimited list of units with the
same dimensionality, which can be used for breaking down numbers into
more familiar quantities.
[#temperature-conversions]
Temperature conversion
^^^^^^^^^^^^^^^^^^^^^^
> 12 °C
285.15 K (temperature)
> 12 degC
285.15 K (temperature)
> 12 celsius
285.15 K (temperature)
Temperature scales in Rink are handled a little specially, because
only Kelvin and Rankine (the absolute zero version of Fahrenheit)
start at absolute zero. As such, they are *operators*, not
units. These operators have looser binding precedence than
multiplication, but tighter than addition.
Available temperature scales:
`degC`, `°C`, `celsius`, `℃`::
**Celsius**, the standard scale in most countries.
`degF`, `°F`, `fahrenheit`, `℉`::
**Fahrenheit**, the scale used in households across the United States.
`degRé`, `°Ré`, `degRe`, `°Re`, `réaumur`, `reaumur`::
**Réaumur**, A historical scale once used throughout Europe.
`degRø`, `°Rø`, `degRo`, `°Ro`, `rømer`, `romer`::
**Romer**, Another historical scale.
`degN`, `°N`, `degnewton`::
**Newton**, A historical scale created by Isaac Newton.
`degDe`, `°De`, `delisle`::
**Delisle**, A historical scale which, alongside the original
Celsius scale, is reversed from the scales we are used to today. Its
zero point is boiling water, and the freezing point of water is
150°De.
Note that these temperature scale measurements are *absolute*
measurements, not *differences*. If you wish to say something like "a
difference of 1°C", then you must use the absolute scale for the scale
you're using. These are:
- For Celsius, kelvin `K`
- For Fahrenheit, Rankine `degR`
- For Réaumur, `reaumur_absolute` (absolute as in the zero point is absolute zero)
- For Rømer, `romer_absolute`
- For Newton, `newton_absolute`
- For Delisle, `delisle_absolute`
Numeric base conversion
^^^^^^^^^^^^^^^^^^^^^^^
> 1000 -> hex
3e8 (dimensionless)
> 10000 -> base 36
7ps (dimensionless)
> pi meter -> hex meter
approx. 3.243f6a meter (length)
Base modifiers are specified with `base` followed by a number,
followed by the rest of your conversion. Allowed bases are currently 2
through 36. There are some special base names which are also
recognized:
`hex`, `hexadecimal`, `base16`::
Base 16.
`oct`, `octal`, `base8`::
Base 8.
`bin`, `binary`, `base2`::
Base 2.
Digits modifier
^^^^^^^^^^^^^^^
> 2^128 -> digits
340282366920938463463374607431768211456 (dimensionless)
> 1/7 -> digits 50
1/7, approx. 0.1428571428571428571428571428571428571428571428571428 (dimensionless)
> googol -> digits
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 (dimensionless)
Digits modifiers are specified with `digits` optionally followed by a
number, before the base modifier and before the rest of the
conversion. Any number of digits are allowed, but large amounts may
not succeed.
The function of this modifier is that it forces the entire integer
part to be printed (i.e., scientific notation will *not* be used), and
then it prints an additional *n* digits, using the default if not
specified.
Trancendental numbers currently cannot be precisely represented, so
asking for many digits of pi or e will produce unsatisfying results.
Trigonometric and logarithmic functions are currently implemented
using a machine-float fallback, because their results cannot be
precisely represented as finite rationals. Because of this, asking for
many digits of such numbers will also produce unsatisfying results.
Units for
^^^^^^^^^
> units for power
Units for kg m^2 / s^3 (power): VA, airwatt, boilerhorsepower, brhorsepower,
donkeypower, electrichorsepower, horsepower, lusec, mbh, metrichorsepower,
poncelet, sccm, sccs, scfh, scfm, slph, slpm, solarluminosity,
tonrefrigeration, waterhorsepower, watt
The *units for*, *units of*, and *units* commands will find more units
which match the dimensionality of the one specified.
Factorization
^^^^^^^^^^^^^
> factorize velocity
Factorizations: velocity; frequency length; area viscosity;
acceleration time; length^2 viscosity
> factorize power
Factorizations: power; force velocity; radiant_intensity solid_angle;
area radiosity; length spectral_flux_wavelength; radiation_dose spectral_exposure_frequency;
spectral_irradiance_wavelength volume; temperature thermal_conductance;
energy frequency; current^2 resistance; ...
Unit factorization is what Rink names the process of finding
quantities which can be multiplied together to produce the original
quantity. This can be useful for discovering new ways to construct a
unit.
Search
^^^^^^
> search milk
Search results: milk (substance), mil (length), mile (length), mill (dimensionless), mi (length)
Allows you to search for units based on their names, returning up to 5
results and showing the associated physical quantity of the unit.
Date and time
~~~~~~~~~~~~~
> #jan 01, 1970#
1970-01-01 00:00:00 +00:00 (46 years ago)
> now - #jan 01, 1970# -> gigaseconds
1.472083 gigaseconds (time)
> #2016-08-24# + 500 weeks
2026-03-25 00:00:00 +00:00 (in 9 years)
In addition to handling units, Rink is also capable of doing some
calculations with dates and times.
Converting to a timezone:
> now
2022-08-08 21:19:56.990897100 -07:00 (now)
> now -> "Europe/London"
2022-08-09 05:20:03.656075600 BST (now)
Converting to a fixed offset:
> now -> +01:00
2022-08-09 05:20:30.080703900 +01:00 (now)
Inputting a time with an offset:
> #apr 1, 2016 12:00:00 +01:00#
2016-04-01 12:00:00 +01:00 (6 years ago)
Substances
~~~~~~~~~~
> milk
milk: density = 242 gram -> approx. 236588.2 millimeter^3
> gallon milk
milk: volume = approx. 3785411.7 millimeter^3; mass = 3.872 kilogram
> egg
egg: USA large egg. mass_shelled = 50 gram; mass_white = 30 gram;
mass_yolk = 18.6 gram; volume = approx. 46824.75 millimeter^3;
volume_white = approx. 29573.52 millimeter^3;
volume_yolk = approx. 17251.22 millimeter^3
> egg_shelled of kg egg
20 (dimensionless)
> gallon gasoline -> btu
gasoline: volume = approx. 3785411.7 millimeter^3; energy_HHV = 125000 btu; energy_LHV = 115000 btu
Substances are how Rink organizes the physical properties of
materials, objects, both countable and uncountable. Each substance has
a name, an associated amount (defaulting to dimensionless 1), and a
set of associated properties.
Each property maps a named input into a named output and vice versa,
and has a name itself. Countable objects often have properties with an
input being dimensionless, so that you do not need to specify an
amount to extract the property.
The properties of a substance are accessed with the *of* operator
(<_property_> *of* <_substance_>), which reads a multiplication
expression following it, so you may have to wrap it in parentheses.
Substances can be used in conversions, and can be added and multiplied
to transform them. Multiplication will change the amount of the
substance you have, so that you can write *kg egg* to specify one
kilogram of eggs. Addition will combine certain properties (currently
only `molar_mass`) to create a new substance entirely. Conversions of
substances allow you to get multiple results simultaneously, for
example if there are multiple different measurements of some property
of the substance available.
If the result of a calculation results in a substance, Rink will show
all of the properties applicable for the given amount.
Reference
---------
The full list of units is specified in the file `definitions.units` (see
xref:rink-defs.5.adoc[rink-defs(5)]), but a small list of the most
helpful ones will be listed here. It is intended that most units should
be easy to guess the names of.
SI derived
~~~~~~~~~~
- Newton `N` (force)
- Pascal `Pa` (pressure)
- Joule `J` (energy)
- Watt `W` (power)
- Coulomb `C` (charge)
- Volt `V` (electrical potential)
- Ohm (electrical resistance)
- Siemens `S` (electrical conductance)
- Farad `F` (capacitance)
- Weber `Wb` (magnetic flux)
- Henry `H` (inductance)
- Tesla `T` (magnetic flux density)
- Hertz `Hz` (frequency)
- Lumen `lm` (luminous flux)
- Lux `lx` (illuminance)
- Gray `Gy` (radiation dose)
- Katal `kat` (catalytic activity)
Substances
~~~~~~~~~~
Compounds and materials::
- Water `water`
- Ammonia `ammonia`
- Freon `freon`
- Tissue `tissue`
- Diamond `diamond`
- Graphite `graphite`
- Water ice `ice`
- Asphalt `asphalt`
- Brick `brick`
- Concrete `cocnrete`
- Silica glass `glass_silica`
- Flint glass `glass_flint`
- Pyrex glass `glass_pyrex`
- Gypsum `gypsum`
- Marble `marble`
- Sand `sand`
- Soil `soil`
- Air `air`
Particles::
- Electron `electron`
- Proton `proton`
- Neutron `neutron`
- Deuterium nucleus `deuteron`
- Muon `muon`
- Helium nucleus `helion`
- Tau `tauparticle`
- Alpha `alphaparticle`
- Tritium nucleus `triton`
Celestial bodies::
- Sun `sun`
- Mercury `mercury_planet`
- Venus `venus`
- Earth `earth`
- Earth's moon `moon`
- Mars `mars`
- Jupiter `jupiter`
- Saturn `saturn`
- Uranus `uranus`
- Neptune `neptune`
- Pluto `pluto`
Fuels::
- Crude oil `oil`
- Coal `coal`
- Natural gas `naturalgas`
- Charcoal `charcoal`
- Wood `wood`
- Ethanol `ethanol`
- Diesel `diesel`
- Gasoline `gasoline`
- Heating oil `heating_oil`
- Fuel oil `fueloil`
- Propane `propane`
- Butane `butane`
Foods::
- Butter `butter`
- Clarified butter `butter_clarified`
- Cocoa butter `cocoa_butter`
- Vegetable shortening `shortening`
- Vegetable oil `vegetable_oil`
- Olive oil `olive_oil`
- Flour `cakeflour`, `flour`, `breadflour`
- Corn starch `cornstarch`
- Cocoa `dutchcocoa`, `cocoa`
- Heavy cream `heavycream`
- Milk `milk`
- Sour cream `sourcream`
- Molasses `molasses`
- Corn syrup `corrnsyrup`
- Honey `honey`
- Sugar `sugar`
- Powdered sugar `powdered_sugar`
- Brown sugar `brownsugar_light`, `brownsugar_dark`
- Baking powder `baking_powder`
- Salt `salt`, `koshersalt`
- Egg `egg`
Elements 1 through 118, by name (e.g. `helium`)
Constants
~~~~~~~~~
- Pi `pi`
- Speed of light `c`
- Planck Constant `planck_constant`
- Gravitational Constant `G`
- Avogadro's number `avogadro`
- Gas Constant `gasconstant`
- Boltzmann Constant `boltzmann`
- Earth Gravity `gravity`, `force`
- Earth Atmosphere Density `atm`
Currencies
~~~~~~~~~~
These are only available if live currency fetching is enabled. (See
rink(5).)
- EU Euro `EUR`, `€`
- US dollar `USD`, `$`, `dollar`
- Japan yen `JPY`, `¥`, `yen`
- Bulgaria lev `BGN`
- Czech koruna `CZK`
- Denmark kroner `DKK`
- UK pound `GBP`, `£`
- Hungary forint `HUF`
- Poland złoty `PLN`
- Romania lei `RON`
- Sweden krona `SEK`
- Switzerland franc `CHF`
- Norway krone `NOK`
- Croatia kuna `HRK`
- Russia ruble `RUB`, `₽`
- Turkey lira `TRY`, `₺`
- Australia dollar `AUD`, `A$`
- Brazil real `BRL`, `R$`
- Canada dollar `CAD`, `C$`
- PRC yuan `CNY`
- Hong Kong dollar `HKD`, `H$`
- Indonesia rupiah `IDR`
- Israel shekel `ILS`, `₪`
- India rupee `INR`, `₹`
- South Korea won `₩`
- Mexico dollar `MXN`, `mex$`
- Malaysia ringgit `MYR`
- New Zealand dollar `NZD`, `NZ$`
- Phillipines piso `PHP`, `₱`
- Singapore dollar `SGD`, `S$`
- Thailand baht `THB`, `฿`
- South Africa rand `ZAR`
Functions
~~~~~~~~~
Currently, all of these result in machine float fallback, because
their results are real numbers that cannot be precisely represented as
rationals.
- `sqrt(x)`: Square root, √x.
- `exp(x)`: The exponential function, e^x.
- `ln(x)`: The natural logarithm, log_e(x).
- `log(x,y)`: Logarithm in base *y*, log_y(x).
- `log2(x)`: Logarithm in base 2, log_2(x).
- `log10(x)`: Logarithm in base 10, log_10(x).
- `hypot(x,y)`: The length of the hypotenuse of a right-angle triangle
given adjacent edges of length x and y.
- `sin(x)`: The sine function.
- `cos(x)`: The cosine function.
- `tan(x)`: The tangent function.
- `asin(x)`: Inverse sine, or arcsine.
- `acos(x)`: Inverse cosine, or arccosine.
- `atan(x)`: Inverse tangent, or arctangent.
- `atan2(x, y)`: Four-quadrant arctangent, which can be used to
reverse sine+cosine back into an angle.
- `sinh(x)`: Hyperbolic sine.
- `cosh(x)`: Hyperbolic cosine.
- `tanh(x)`: Hyperbolic tangent.
- `asinh(x)`: Inverse hyperbolic sine function.
- `acosh(x)`: Inverse hyperbolic cosine function.
- `atanh(x)`: Inverse hyperbolic tangent function.
See also
--------
xref:rink.1.adoc[rink(1)], xref:rink.5.adoc[rink(5)],
xref:rink-defs.5.adoc[rink-defs(5)],
xref:rink-dates.5.adoc[rink-dates(5)]

View file

@ -17,7 +17,7 @@ default = ["console_error_panic_hook"]
[dependencies.rink-core]
path = "../core"
version = "0.7"
features = ["gpl"]
features = ["bundle-files"]
[dependencies]
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }

View file

@ -92,7 +92,7 @@ impl Context {
let mut base_defs = {
use rink_core::loader::gnu_units;
let defs = rink_core::CURRENCY_FILE;
let defs = rink_core::CURRENCY_FILE.unwrap();
let mut iter = gnu_units::TokenIterator::new(defs).peekable();
gnu_units::parse(&mut iter)
};