defmodule Stellar.TxBuild.ClaimPredicate do @moduledoc """ `ClaimPredicate` struct definition. """ alias Stellar.TxBuild.{ClaimPredicates, OptionalClaimPredicate} alias StellarBase.XDR.{ClaimPredicate, ClaimPredicateType, Int64, Void} @behaviour Stellar.TxBuild.XDR @type validation :: {:ok, any()} | {:error, any()} @type predicate :: :unconditional | :conditional @type type :: :time | :and | :or | :not @type time_type :: :relative | :absolute @type value :: t() | ClaimPredicates.t() | pos_integer() @type t :: %__MODULE__{ predicate: predicate(), value: value(), type: type(), time_type: time_type() } defstruct [:predicate, :value, :type, :time_type] @impl true def new(args, opts \\ []) def new(:unconditional, _opts), do: %__MODULE__{predicate: :unconditional} def new({value, type}, _opts), do: new(value: value, type: type) def new([value: value, type: type], _opts) when type in ~w(and or not)a do case validate_predicate_value(value, type) do {:ok, value} -> %__MODULE__{predicate: :conditional, type: type, value: value, time_type: nil} {:error, value} -> {:error, value} end end def new({value, type, time_type}, _opts), do: new(value: value, type: type, time_type: time_type) def new([value: value, type: type, time_type: time_type], _opts) do case validate_predicate_value(value, type) do {:ok, value} -> %__MODULE__{predicate: :conditional, type: :time, value: value, time_type: time_type} {:error, value} -> {:error, value} end end def new(_args, _opts), do: {:error, :invalid_claim_predicate} @impl true def to_xdr(%__MODULE__{predicate: :unconditional}) do Void.new() |> ClaimPredicate.new(ClaimPredicateType.new(:CLAIM_PREDICATE_UNCONDITIONAL)) end def to_xdr(%__MODULE__{value: value, type: :and}) do value |> ClaimPredicates.to_xdr() |> ClaimPredicate.new(ClaimPredicateType.new(:CLAIM_PREDICATE_AND)) end def to_xdr(%__MODULE__{value: value, type: :or}) do value |> ClaimPredicates.to_xdr() |> ClaimPredicate.new(ClaimPredicateType.new(:CLAIM_PREDICATE_OR)) end def to_xdr(%__MODULE__{value: value, type: :not}) do value |> OptionalClaimPredicate.new() |> OptionalClaimPredicate.to_xdr() |> ClaimPredicate.new(ClaimPredicateType.new(:CLAIM_PREDICATE_NOT)) end def to_xdr(%__MODULE__{value: value, time_type: :absolute}) do value |> Int64.new() |> ClaimPredicate.new(ClaimPredicateType.new(:CLAIM_PREDICATE_BEFORE_ABSOLUTE_TIME)) end def to_xdr(%__MODULE__{value: value, time_type: :relative}) do value |> Int64.new() |> ClaimPredicate.new(ClaimPredicateType.new(:CLAIM_PREDICATE_BEFORE_RELATIVE_TIME)) end @spec validate_predicate_value(value :: value(), type :: atom()) :: validation() defp validate_predicate_value(%__MODULE__{} = value, type) when type != :time, do: {:ok, value} defp validate_predicate_value(value, :time) when is_integer(value), do: {:ok, value} defp validate_predicate_value(%ClaimPredicates{value: value}, type) when is_list(value) and length(value) == 2 and type in ~w(and or)a, do: ClaimPredicates.validate_predicate_list([], value) defp validate_predicate_value(_value, _type), do: {:error, :invalid_predicate_value} end