defmodule Taly do
@moduledoc """
`Taly` provides a standard API to handle the based of map, keyword or list options
## Example
import Taly
def schema() do
form do
field "name", type: :string, required: true
field "age", type: :integer
field "worth", type: :float, default: 100.1, required: true
field "sex", type: :atom, default: :man
field "other", type: :any
field "marry", type: :boolean
field "like_date", type: :date, format: "{YYYY}/{M}/{D}"
field "birth", type: :datetime
end
end
data = %{
"name" => "john",
"age" => 1,
"marry" => false,
"worth" => "100000.1",
"sex" => :women,
"other" => {"hello"},
# "like_date" => "2021/2/12",
"like_date" => ~D[2021-02-12],
"birth" => "2021-02-12 12:12:12",
}
Taly.Validate.validate(schema(), data)
#=> {:ok, %{
"age" => 1,
"name" => "john",
"other" => {"hello"},
"sex" => :women,
"marry" => false,
"worth" => 100_000.1,
"birth" => ~N[2021-02-12 12:12:12],
"like_date" => ~D[2021-02-12]
}}
"""
alias Taly.Form
defmacro __using__(_opts) do
quote do
import Taly.Field
import Taly
# @before_compile Taly.Field
end
end
@doc """
Pack `field` and `final` into a `Form.t()`.
## Example
form do
field :name, type: :string
field do
{:ok, value}
end
end
"""
defmacro form(opts) do
cells = case opts[:do] do
{:__block__, _, cells_} ->
cells_
_ ->
[opts[:do]]
end
quote do
Enum.reduce(unquote(cells), %Form{}, fn cell, r ->
cond do
is_function(cell) ->
%Form{r | final: cell}
true ->
Form.add_field(r, cell)
end
end)
end
end
@doc """
if you use `do-block`, the parameters is `key`, `value`, `kwargs`. the result must
return `{:ok, value}` or `{:error, "Error"}`.
Detailed usage reference the test file.
## Example
import Taly
form_1 =
form do
field :name, type: :string
end
form_2 =
form do
field :name, requried: true do
{:ok, key}
end
end
data = [name: "john"]
Taly.Validate.validate(form_1, data)
Taly.Validate.validate(form_2, data)
"""
defmacro field(name, opt \\ [], contents \\ []) do
Taly.Field.field(name, opt, contents)
end
@doc """
this defmacro function use to create a option item.
## Example
import Taly
schema_opt = opt type: :integer
list_data = ["123", 456]
Taly.Validate.validate(schema_opt, list_data)
#=> {:ok, [123, 456]}
"""
defmacro opt(option, contents \\ []) do
Taly.Field.opt(option, contents)
end
@doc """
Use `validate/3` to validate data, allows finally to return the result what you want.
The parameter is `result` and `kwargs`.
* `result`: Result that use `validate/3` validate data with schema or `Form`.
* `kwargs`: it's a map.the Key `:org` is input data. the `:path` is the key path.
`:schema` is the schema option.
## Example
import Taly
def final_test() do
schema_1 =
form do
field :name
final do
IO.inspect(kwargs, label: "final kwargs")
result
# the result is {:ok, [name: "john"]}, of course you
# can {:error, "Error"}.
end
end
form do
field :obj, type: :dict, form: schema_1
final do
result
# this result is the real output data, you can return what you
# want. for example [1, 2, 3].
end
end
end
schema = Example.final_test()
data = %{:obj => [name: "john"]}
Taly.Validate.validate(schema, data)
#=> {:ok, %{obj: [name: "john"]}}
"""
defmacro final(contents) do
do_something = contents[:do]
quote do
fn var!(result), var!(kwargs) ->
unquote(do_something)
end
end
end
end