defmodule Moar.Term do
# @related [test](/test/term_test.exs)
@moduledoc """
Blank/present functions for terms.
A term is considered present when it is not blank.
A term is considered blank when:
* it is `nil`
* it is `false`
* it is a string, and its length after being trimmed is 0
* it is an empty list
* it is an empty map
"""
@doc """
Returns true if the term is blank, nil, or empty.
```elixir
iex> Moar.Term.blank?(nil)
true
iex> Moar.Term.blank?(" ")
true
iex> Moar.Term.blank?([])
true
iex> Moar.Term.blank?(%{})
true
```
"""
@spec blank?(any()) :: boolean()
def blank?(nil), do: true
def blank?(s) when is_binary(s), do: s |> String.trim() == ""
def blank?([]), do: true
def blank?(list) when is_list(list), do: false
def blank?(m) when is_map(m), do: map_size(m) == 0
def blank?(true), do: false
def blank?(false), do: true
def blank?(_), do: false
@doc """
Returns true if the term is not blank, nil, or empty.
```elixir
iex> Moar.Term.present?(1)
true
iex> Moar.Term.present?([1])
true
iex> Moar.Term.present?(%{a: 1})
true
iex> Moar.Term.present?("1")
true
```
"""
@spec present?(any()) :: boolean()
def present?(term), do: !blank?(term)
@doc """
Returns the value if it is present (via `present?`), or else returns the default value.
```elixir
iex> Moar.Term.presence(20, 100)
20
iex> Moar.Term.presence(nil, 100)
100
```
"""
@spec presence(any(), any()) :: any()
def presence(term, default \\ nil), do: when_present(term, term, default)
@doc """
Returns `present_value` when `term` is present (via `present?`), and `blank_value` when `term` is blank.
If `present_value` and/or `blank_value` are functions, they are called with `term` as their argument.
```elixir
iex> Moar.Term.when_present(20, "continue", "value missing")
"continue"
iex> Moar.Term.when_present(nil, "continue", "value missing")
"value missing"
iex> Moar.Term.when_present(20, fn value -> value * 2 end, "value missing")
40
iex> Moar.Term.when_present(nil, "continue", fn value -> "expected a number, got: \#{inspect(value)}" end)
"expected a number, got: nil"
```
"""
@spec when_present(any(), any(), any()) :: any()
def when_present(term, present_value, blank_value) do
value_fn = fn
term, fun when is_function(fun) -> fun.(term)
_term, value -> value
end
if present?(term),
do: value_fn.(term, present_value),
else: value_fn.(term, blank_value)
end
end