data v0.4.1 Data.Parser.KV
Creates parsers that accept KeyValue-style Enum
s as input.
In particular, KV parsers work with:
- maps (e.g.
%{"hello" => "world"}
) Keyword.t
s (e.g.[hello: "world"]
)- Lists of pairs (e.g.
[{"hello", "world"}]
)
KV parsers are higher-order parsers, and operate in roughly the same way as
Data.Parser.list/1
or Data.Parser.set/1
, but their definition is slightly
more involved. A KV parser is created with a list of field_spec
s, where
each field_spec
defines what fields of the input to look at, and what
parsers to run on them.
Here are some examples of field_spec
s and their parsing behavior:
{:username, Data.Parser.BuiltIn.string()}
This spec says that the input must contain a :username
field, and the value
of that field must satisfy Data.Parser.BuiltIn.string/0
. The output map
will contain the key-value pair username: "some string"
.
If the field cannot be parsed successfully, the entire KV parser will return
{:error, domain_error_with_details_on_parse_failure}
.
If the field is not present, the entire KV parser will return
{:error, domain_error_with_details_about_field_not_found}
{:birthday, Data.Parser.BuiltIn.date(), optional: true}
This spec says that the input may contain a :birthday
field. If the field
does exist, it must satisfy Data.Parser.BuiltIn.date/0
.
If the field exists and parses successfully, the output map will contain the
key-value pair birthday: {:just, ~D[1983-07-18]}
.
If the field does not exist, the output map will contain the key-value pair
birthday: :nothing
.
If the field cannot be parsed successfully, the entire KV parser will return
{:error, domain_error_with_parse_failure_details}
.
{:country, MyApp.country_parser(), default: "Canada"}
This spec says that the input may contain a :country
field, and if so, the
value of that field must parse successfully with MyApp.country_parser/0
.
If the field exists and parses successfully, the output map will contain a
key-value pair such as: country: "Indonesia"
.
If the field cannot be parsed successfully, the entire Constructor will return
{:error, domain_error_with_details_on_parse_failure}
.
If the field does not exist, the default
value will be used. In this
case, the output will contain the key-value pair: country: "Canada"
Link to this section Summary
Types
A structure representing a Data.Parser.t(a,b)
lifted to operate on a KV.
KV parsers accept atom()
s as key names, but will work on inputs where
the keys are String.t()
s as well.
Options to relax requirements on the fields.
A 2-tuple or 3-tuple describing the field to parse and parsing semantics.
KV parsers accept either a map or a Keyword.t
as input.
Link to this section Types
A structure representing a Data.Parser.t(a,b)
lifted to operate on a KV.
KV parsers accept atom()
s as key names, but will work on inputs where
the keys are String.t()
s as well.
Options to relax requirements on the fields.
This is a list that consists of zero or one of the below options:
{:optional, bool()}
{:default, any}
field_spec(a, b)
field_spec(a, b) :: {field_name(), Data.Parser.t(a, b)} | {field_name(), Data.Parser.t(a, b), field_opts(b)}
A 2-tuple or 3-tuple describing the field to parse and parsing semantics.
{field_name, parser}
{field_name, parser, opts}
KV parsers accept either a map or a Keyword.t
as input.
Link to this section Functions
new(field_specs)
new([field_spec(a, b)]) :: FE.Result.t(Data.Parser.t(a, b), Error.t())
Given a list of field_spec
s, verify that all specs are well-formed and
return {:ok, parser}
, where parser
will accept a map
or Keyword
input
and apply the appropriate parsers to their corresponding fields.
If the field_spec
s are not well-formed, return {:error, Error.t}
with details
about the invalid field_spec
s.
Examples
iex> {:ok, p} = Data.Parser.KV.new([{:username, Data.Parser.BuiltIn.string()}])
...> p.(username: "johndoe")
{:ok, %{username: "johndoe"}}
iex> {:ok, p} = Data.Parser.KV.new([{:username, Data.Parser.BuiltIn.string()}])
...> p.(%{"username" => "johndoe"})
{:ok, %{username: "johndoe"}}
iex> {:error, e} = Data.Parser.KV.new(["not a spec"])
...> e.reason
:invalid_field_spec
...> e.details
%{spec: "not a spec"}
iex> {:ok, p} = Data.Parser.KV.new([{:a, Data.Parser.BuiltIn.integer(), optional: true}])
...> p.(a: 1)
{:ok, %{a: {:just, 1}}}
iex> {:ok, p} = Data.Parser.KV.new([{:a, Data.Parser.BuiltIn.integer(), optional: true}])
...> p.([])
{:ok, %{a: :nothing}}
iex> {:ok, p} = Data.Parser.KV.new([{:b, Data.Parser.BuiltIn.integer(), default: 0}])
...> p.([])
{:ok, %{b: 0}}
iex> {:ok, p} = Data.Parser.KV.new([{:b, Data.Parser.BuiltIn.integer(), default: 0}])
...> p.(b: 10)
{:ok, %{b: 10}}
iex> {:ok, p} = Data.Parser.KV.new([{:b, Data.Parser.BuiltIn.integer(), default: 0}])
...> {:error, e} = p.(b: "i am of the wrong type")
...> Error.reason(e)
:failed_to_parse_field
...> {:just, inner_error} = Error.caused_by(e)
...> Error.reason(inner_error)
:not_an_integer