defmodule Order do
@enforce_keys [:id]
defstruct [:id, status: :pending, items: []]
# A union of literal atoms, written with `|`.
@type status :: :pending | :paid | :shipped
# A map type: `required(...)` keys must be present,
# `optional(...)` keys may be. `String.t()` is a
# UTF-8 string; `pos_integer()` is 1, 2, 3...
@type item :: %{
required(:sku) => String.t(),
required(:qty) => pos_integer(),
optional(:gift) => boolean()
}
# `%__MODULE__{...}` is this struct, with a type for
# each field. `[item()]` is shorthand for
# `list(item())`.
@type t :: %__MODULE__{
id: pos_integer(),
status: status(),
items: [item()]
}
# `@typep` is private: usable only inside this
# module, and hidden from the docs.
@typep count :: non_neg_integer()
# `keyword()` is a keyword list, like the options
# here.
@spec new(pos_integer(), [item()], keyword()) :: t()
def new(id, items, opts \\ []) do
struct!(__MODULE__, [id: id, items: items] ++ opts)
end
@spec quantity(t()) :: count()
def quantity(order), do: order.items |> Enum.map(& &1.qty) |> Enum.sum()
@spec half(integer()) :: integer()
def half(n), do: n / 2
end
order = Order.new(1, [%{sku: "TEA", qty: 2}, %{sku: "MUG", qty: 1, gift: true}])
IO.inspect(order)
IO.inspect(Order.quantity(order), label: "quantity")