defmodule Manganese.CoreKit.Structs.Version do @moduledoc """ A SemVer version. ## Deserialization *See `from_string/1`* ## Serialization The version struct implements the `String.Chars` protocol. ## Manipulation - see `increment/2` """ alias Manganese.CoreKit.Structs alias Manganese.CoreKit.Enumerations @typedoc """ """ @type t :: %Structs.Version{ major: non_neg_integer, minor: non_neg_integer, patch: non_neg_integer } defstruct [ major: 0, minor: 0, patch: 0 ] # Creation @doc """ """ @spec new :: t def new, do: %Structs.Version{} # Deserialization @doc """ """ @spec from_string(String.t) :: t def from_string(string) do components = String.split(string, ".") |> Enum.map(&Integer.parse/1) [ major, minor, patch ] = components %Structs.Version{ major: major, minor: minor, patch: patch } end # Serialization @type t_external :: String.t # Manipulation @doc """ """ @spec increment(t, Enumerations.VersionType.t) :: t def increment(version, version_type \\ :patch) do case version_type do :major -> %Structs.Version{ major: version.major + 1 } :minor -> %Structs.Version{ major: version.major, minor: version.minor + 1 } :patch -> %Structs.Version{ major: version.major, minor: version.minor, patch: version.patch + 1 } end end # Ecto type @behaviour Ecto.Type @impl Ecto.Type def type, do: :string @impl Ecto.Type def cast(%Structs.Version{} = version), do: { :ok, version } def cast(string) when is_binary(string), do: { :ok, from_string string } def cast(nil), do: { :ok, nil } def cast(_), do: :error @impl Ecto.Type def load(string) when is_binary(string), do: { :ok, from_string string } def load(nil), do: { :ok, nil } def load(_), do: :error @impl Ecto.Type def dump(%Structs.Version{} = version), do: { :ok, to_string version } def dump(nil), do: { :ok, nil } def dump(_), do: :error end defimpl String.Chars, for: Manganese.CoreKit.Structs.Version do def to_string(version) do "#{version.major}.#{version.minor}.#{version.patch}" end end