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