# `BB.Unit`
[🔗](https://github.com/beam-bots/bb/blob/main/lib/bb/unit.ex#L5)

Helpers for working with units in BB DSLs.

Wraps `Localize.Unit` and provides a sigil for compact unit literals.
Unit identifiers can be passed as either atoms (`:newton_meter`) or strings
(`"newton-meter"`); the helpers in this module convert atoms with
underscores into the CLDR canonical dash form before passing them through.

# `compare`

```elixir
@spec compare(Localize.Unit.t(), Localize.Unit.t()) ::
  :lt | :eq | :gt | {:error, Exception.t()}
```

Delegates to `Localize.Unit.compare/2`.

# `compatible?`

```elixir
@spec compatible?(
  Localize.Unit.t() | atom() | binary(),
  Localize.Unit.t() | atom() | binary()
) ::
  boolean()
```

Check whether two units belong to the same dimensional category.

Accepts atoms or strings as the second argument and translates them to the
CLDR canonical form. Either argument may also be a `Localize.Unit` struct.

# `describe`

```elixir
@spec describe(Localize.Unit.t()) :: String.t()
```

Render a unit for a developer-facing error message.

`to_string!/2` needs CLDR locale data, which is loaded by a process in
`Localize`'s supervision tree. DSL validators run while the host robot is
being compiled, and `mix compile` doesn't start dependency applications, so
that process isn't there. This renders the magnitude and canonical unit name
directly instead.

    iex> BB.Unit.describe(Localize.Unit.new!(-30, "degree"))
    "-30 degree"

# `sigil_u`

```elixir
@spec sigil_u(
  binary(),
  charlist()
) :: Localize.Unit.t() | no_return()
```

Parse a string input as a unit.

The input should be a magnitude (integer or float) followed by a unit name.
Whitespace between the magnitude and unit is optional.

Units are generally referred to in the singular, even if it doesn't read as
nicely, for example `meter_per_second` rather than `meters_per_second`. For
a full list of supported units, see the
[Localize documentation](https://hexdocs.pm/localize/units.html).

## Examples

Integer magnitudes:

    iex> import BB.Unit
    iex> u = ~u(5 meter)
    iex> {u.name, u.value}
    {"meter", 5}

Float magnitudes:

    iex> import BB.Unit
    iex> u = ~u(0.1 meter)
    iex> {u.name, u.value}
    {"meter", 0.1}

Negative values:

    iex> import BB.Unit
    iex> u = ~u(-90 degree)
    iex> {u.name, u.value}
    {"degree", -90}

Whitespace is optional:

    iex> import BB.Unit
    iex> u = ~u(100centimeter)
    iex> {u.name, u.value}
    {"centimeter", 100}

Compound units use underscores, which are translated to the CLDR dash form:

    iex> import BB.Unit
    iex> u = ~u(10 meter_per_second)
    iex> {u.name, u.value}
    {"meter-per-second", 10}

# `to_string!`

```elixir
@spec to_string!(
  Localize.Unit.t() | [Localize.Unit.t()],
  keyword()
) :: String.t()
```

Delegates to `Localize.Unit.to_string!/2`.

# `unit_name`

```elixir
@spec unit_name(atom() | binary()) :: binary()
```

Convert an atom or underscored string unit identifier to the CLDR canonical
dash form that `Localize.Unit` expects.

    iex> BB.Unit.unit_name(:newton_meter)
    "newton-meter"

    iex> BB.Unit.unit_name("meter_per_second")
    "meter-per-second"

    iex> BB.Unit.unit_name("meter")
    "meter"

# `validate_unit`

```elixir
@spec validate_unit(atom() | binary()) ::
  {:ok, Localize.Unit.t()} | {:error, Exception.t()}
```

Validate that an identifier resolves to a known unit.

Returns `{:ok, unit}` for a known unit, `{:error, exception}` otherwise.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
