defmodule Money.Subscription.Plan do
@moduledoc """
Defines a standard subscription plan data structure.
"""
@typedoc "A plan interval type."
@type interval :: :day | :week | :month | :year
@typedoc "A integer interval count for a plan."
@type interval_count :: non_neg_integer
@typedoc "A Subscription Plan"
@type t :: %__MODULE__{
price: Money.t() | nil,
interval: interval,
interval_count: interval_count
}
@doc """
Defines the structure of a subscription plan.
"""
defstruct price: nil,
interval: nil,
interval_count: nil
@interval [:day, :week, :month, :year]
@doc """
Returns `{:ok, Money.Subscription.Plan.t}` or an `{:error, reason}`
tuple.
## Arguments
* `:price` is any `Money.t`
* `:interval` is the period of the plan. The valid intervals are
` `:day`, `:week`, `:month` or ':year`.
* `:interval_count` is an integer count of the number of `:interval`s
of the plan. The default is `1`
## Returns
A `Money.Subscription.Plan.t`
## Examples
iex> Money.Subscription.Plan.new Money.new(:USD, 100), :month, 1
{:ok,
%Money.Subscription.Plan{
interval: :month,
interval_count: 1,
price: Money.new(:USD, 100)
}}
iex> Money.Subscription.Plan.new Money.new(:USD, 100), :month
{:ok,
%Money.Subscription.Plan{
interval: :month,
interval_count: 1,
price: Money.new(:USD, 100)
}}
iex> Money.Subscription.Plan.new Money.new(:USD, 100), :day, 30
{:ok,
%Money.Subscription.Plan{
interval: :day,
interval_count: 30,
price: Money.new(:USD, 100)
}}
iex> Money.Subscription.Plan.new 23, :day, 30
{:error, {Money.Invalid, "Invalid subscription plan definition"}}
"""
@spec new(Money.t(), interval(), interval_count()) ::
{:ok, t()} | {:error, {module(), String.t()}}
def new(price, interval, interval_count \\ 1)
def new(%Money{} = price, interval, interval_count)
when interval in @interval and is_integer(interval_count) do
{:ok, %__MODULE__{price: price, interval: interval, interval_count: interval_count}}
end
def new(_price, _interval, _interval_count) do
{:error, {Money.Invalid, "Invalid subscription plan definition"}}
end
@doc """
Returns `{:ok, Money.Subscription.Plan.t}` or raises an
exception.
Takes the same arguments as `Money.Subscription.Plan.new/3`.
## Example
iex> Money.Subscription.Plan.new! Money.new(:USD, 100), :day, 30
%Money.Subscription.Plan{
interval: :day,
interval_count: 30,
price: Money.new(:USD, 100)
}
"""
@spec new!(Money.t(), interval(), interval_count()) :: t() | no_return()
def new!(price, interval, interval_count \\ 1)
def new!(price, interval, interval_count) do
case new(price, interval, interval_count) do
{:ok, plan} -> plan
{:error, {exception, reason}} -> raise exception, reason
end
end
end