Update API documentation
This commit is contained in:
parent
21924315cc
commit
fab63e6a0d
12 changed files with 344 additions and 16 deletions
|
@ -252,10 +252,15 @@ defmodule RDF do
|
|||
|
||||
for term <- ~w[type subject predicate object first rest value]a do
|
||||
defdelegate unquote(term)(), to: RDF.NS.RDF
|
||||
@doc false
|
||||
defdelegate unquote(term)(s, o), to: RDF.NS.RDF
|
||||
@doc false
|
||||
defdelegate unquote(term)(s, o1, o2), to: RDF.NS.RDF
|
||||
@doc false
|
||||
defdelegate unquote(term)(s, o1, o2, o3), to: RDF.NS.RDF
|
||||
@doc false
|
||||
defdelegate unquote(term)(s, o1, o2, o3, o4), to: RDF.NS.RDF
|
||||
@doc false
|
||||
defdelegate unquote(term)(s, o1, o2, o3, o4, o5), to: RDF.NS.RDF
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
defmodule RDF.Literal do
|
||||
@moduledoc """
|
||||
RDF literals are leaf nodes of a RDF graph containing raw data, like strings and numbers.
|
||||
RDF literals are leaf nodes of a RDF graph containing raw data, like strings, numbers etc.
|
||||
|
||||
A literal is a struct consisting of a `literal` field holding either a `RDF.Literal.Datatype` struct
|
||||
for the respective known datatype of the literal or a `RDF.Literal.Generic` struct if the datatype
|
||||
is unknown, i.e. has no `RDF.Literal.Datatype` implementation.
|
||||
"""
|
||||
|
||||
defstruct [:literal]
|
||||
|
@ -238,34 +242,66 @@ defmodule RDF.Literal do
|
|||
############################################################################
|
||||
# functions delegating to the RDF.Literal.Datatype of a RDF.Literal
|
||||
|
||||
@doc """
|
||||
Returns the IRI of datatype of the given `literal`.
|
||||
"""
|
||||
@spec datatype_id(t) :: IRI.t()
|
||||
def datatype_id(%__MODULE__{literal: %datatype{} = literal}), do: datatype.datatype_id(literal)
|
||||
|
||||
@doc """
|
||||
Returns the language of the given `literal` if present.
|
||||
"""
|
||||
@spec language(t) :: String.t() | nil
|
||||
def language(%__MODULE__{literal: %datatype{} = literal}), do: datatype.language(literal)
|
||||
|
||||
@doc """
|
||||
Returns the value of the given `literal`.
|
||||
"""
|
||||
@spec value(t) :: any
|
||||
def value(%__MODULE__{literal: %datatype{} = literal}), do: datatype.value(literal)
|
||||
|
||||
@doc """
|
||||
Returns the lexical form of the given `literal`.
|
||||
"""
|
||||
@spec lexical(t) :: String.t
|
||||
def lexical(%__MODULE__{literal: %datatype{} = literal}), do: datatype.lexical(literal)
|
||||
|
||||
@doc """
|
||||
Transforms the given `literal` into its canonical form.
|
||||
"""
|
||||
@spec canonical(t) :: t
|
||||
def canonical(%__MODULE__{literal: %datatype{} = literal}), do: datatype.canonical(literal)
|
||||
|
||||
@doc """
|
||||
Returns the canonical lexical of the given `literal`.
|
||||
"""
|
||||
@spec canonical_lexical(t) :: String.t | nil
|
||||
def canonical_lexical(%__MODULE__{literal: %datatype{} = literal}), do: datatype.canonical_lexical(literal)
|
||||
|
||||
@doc """
|
||||
Returns if the lexical of the given `literal` has the canonical form.
|
||||
"""
|
||||
@spec canonical?(t) :: boolean
|
||||
def canonical?(%__MODULE__{literal: %datatype{} = literal}), do: datatype.canonical?(literal)
|
||||
|
||||
@doc """
|
||||
Returns if the given `literal` is valid with respect to its datatype.
|
||||
"""
|
||||
@spec valid?(t | any) :: boolean
|
||||
def valid?(%__MODULE__{literal: %datatype{} = literal}), do: datatype.valid?(literal)
|
||||
def valid?(_), do: false
|
||||
|
||||
@doc """
|
||||
Checks if two literals are equal.
|
||||
|
||||
Two literals are equal if they have the same datatype, value and lexical form.
|
||||
"""
|
||||
@spec equal?(any, any) :: boolean
|
||||
def equal?(left, right), do: left == right
|
||||
|
||||
@doc """
|
||||
Checks if two literals have equal values.
|
||||
"""
|
||||
@spec equal_value?(t, t | any) :: boolean
|
||||
def equal_value?(%__MODULE__{literal: %datatype{} = left}, right),
|
||||
do: datatype.equal_value?(left, right)
|
||||
|
@ -314,6 +350,24 @@ defmodule RDF.Literal do
|
|||
def matches?(value, pattern, flags) when is_binary(value) and is_binary(pattern) and is_binary(flags),
|
||||
do: RDF.XSD.Utils.Regex.matches?(value, pattern, flags)
|
||||
|
||||
@doc """
|
||||
Updates the value of a `RDF.Literal` without changing everything else.
|
||||
|
||||
The optional second argument allows to specify what will be passed to `fun` with the `:as` option,
|
||||
eg. with `as: :lexical` the lexical is passed to the function.
|
||||
|
||||
## Example
|
||||
|
||||
iex> RDF.XSD.integer(42) |> RDF.Literal.update(fn value -> value + 1 end)
|
||||
RDF.XSD.integer(43)
|
||||
iex> ~L"foo"de |> RDF.Literal.update(fn _ -> "bar" end)
|
||||
~L"bar"de
|
||||
iex> RDF.literal("foo", datatype: "http://example.com/dt") |> RDF.Literal.update(fn _ -> "bar" end)
|
||||
RDF.literal("bar", datatype: "http://example.com/dt")
|
||||
iex> RDF.XSD.integer(42) |> RDF.XSD.Integer.update(
|
||||
...> fn value -> value <> "1" end, as: :lexical)
|
||||
RDF.XSD.integer(421)
|
||||
"""
|
||||
def update(%__MODULE__{literal: %datatype{} = literal}, fun, opts \\ []) do
|
||||
datatype.update(literal, fun, opts)
|
||||
end
|
||||
|
|
|
@ -1,4 +1,22 @@
|
|||
defmodule RDF.Literal.Datatype do
|
||||
@moduledoc """
|
||||
A behaviour for datatypes for `RDF.Literal`s.
|
||||
|
||||
An implementation of this behaviour defines a struct for a datatype IRI and the semantics of its
|
||||
values via the functions defined by this behaviour.
|
||||
|
||||
There are three important groups of `RDF.Literal.Datatype` implementations:
|
||||
|
||||
- `RDF.XSD.Datatype`: This is another, more specific behaviour for XSD datatypes. RDF.ex comes with
|
||||
builtin implementations of this behaviour for the most important XSD datatypes, but you define
|
||||
your own custom datatypes by deriving from these builtin datatypes and constraining them via
|
||||
`RDF.XSD.Facet`s.
|
||||
- Non-XSD datatypes which implement the `RDF.Literal.Datatype` directly: There's currently only one
|
||||
builtin datatype of this category - `RDF.LangString` for language tagged RDF literals.
|
||||
- `RDF.Literal.Generic`: This is a generic implementation which is used for `RDF.Literal`s with a
|
||||
datatype that has no own `RDF.Literal.Datatype` implementation defining its semantics.
|
||||
"""
|
||||
|
||||
alias RDF.{Literal, IRI}
|
||||
|
||||
@type t :: module
|
||||
|
@ -91,7 +109,7 @@ defmodule RDF.Literal.Datatype do
|
|||
@callback canonical?(Literal.t() | literal | any) :: boolean
|
||||
|
||||
@doc """
|
||||
Determines if the lexical form of a `RDF.Literal` is a member of its lexical value space.
|
||||
Determines if a `RDF.Literal` has a proper value of the value space of its datatype.
|
||||
|
||||
This function also accepts literals of derived datatypes.
|
||||
"""
|
||||
|
@ -154,16 +172,16 @@ defmodule RDF.Literal.Datatype do
|
|||
## Example
|
||||
|
||||
iex> RDF.XSD.integer(42) |> RDF.XSD.Integer.update(fn value -> value + 1 end)
|
||||
RDF.XSD.integer(42)
|
||||
iex> RDF.literal("foo", language: "de") |> RDF.LangString.update(fn _ -> "bar" end)
|
||||
RDF.literal("bar", language: "de")
|
||||
iex> RDF.literal("foo", datatype: "http://example.com/dt") |> RDF.LangString.update(fn _ -> "bar" end)
|
||||
RDF.XSD.integer(43)
|
||||
iex> ~L"foo"de |> RDF.LangString.update(fn _ -> "bar" end)
|
||||
~L"bar"de
|
||||
iex> RDF.literal("foo", datatype: "http://example.com/dt") |> RDF.Literal.Generic.update(fn _ -> "bar" end)
|
||||
RDF.literal("bar", datatype: "http://example.com/dt")
|
||||
"""
|
||||
@callback update(Literal.t() | literal, fun()) :: Literal.t
|
||||
|
||||
@doc """
|
||||
Updates the value of a `RDF.Literal` without changing everything else.
|
||||
Updates the value of a `RDF.Literal` without changing anything else.
|
||||
|
||||
This variant of `c:update/2` allows with the `:as` option to specify what will
|
||||
be passed to `fun`, eg. with `as: :lexical` the lexical is passed to the function.
|
||||
|
@ -171,11 +189,14 @@ defmodule RDF.Literal.Datatype do
|
|||
## Example
|
||||
|
||||
iex> RDF.XSD.integer(42) |> RDF.XSD.Integer.update(
|
||||
...> fn value -> value <> "1" end, as: lexical)
|
||||
...> fn value -> value <> "1" end, as: :lexical)
|
||||
RDF.XSD.integer(421)
|
||||
"""
|
||||
@callback update(Literal.t() | literal, fun(), keyword) :: Literal.t
|
||||
|
||||
@doc """
|
||||
Returns the `RDF.Literal.Datatype` for a datatype IRI.
|
||||
"""
|
||||
defdelegate get(id), to: Literal.Datatype.Registry, as: :datatype
|
||||
|
||||
@doc !"""
|
||||
|
@ -223,6 +244,9 @@ defmodule RDF.Literal.Datatype do
|
|||
# RDF.XSD.Datatypes offers another default implementation, but since it is
|
||||
# still in a macro implementation defoverridable doesn't work
|
||||
unless RDF.XSD.Datatype in @behaviour do
|
||||
@doc """
|
||||
Checks if the given literal has this datatype.
|
||||
"""
|
||||
@impl unquote(__MODULE__)
|
||||
def datatype?(%Literal{literal: literal}), do: datatype?(literal)
|
||||
def datatype?(%datatype{}), do: datatype?(datatype)
|
||||
|
@ -238,6 +262,9 @@ defmodule RDF.Literal.Datatype do
|
|||
def language(%Literal{literal: literal}), do: language(literal)
|
||||
def language(%__MODULE__{}), do: nil
|
||||
|
||||
@doc """
|
||||
Returns the canonical lexical form of a `RDF.Literal` of this datatype.
|
||||
"""
|
||||
@impl unquote(__MODULE__)
|
||||
def canonical_lexical(literal)
|
||||
def canonical_lexical(%Literal{literal: literal}), do: canonical_lexical(literal)
|
||||
|
@ -255,7 +282,7 @@ defmodule RDF.Literal.Datatype do
|
|||
Returns `nil` when the given arguments are not castable into this datatype or when the given argument is an
|
||||
invalid literal.
|
||||
|
||||
Implementations define the casting for a given value with the `c:do_cast/1` callback.
|
||||
Implementations define the casting for a given value with the `c:RDF.Literal.Datatype.do_cast/1` callback.
|
||||
"""
|
||||
@spec cast(Literal.Datatype.literal | RDF.Term.t) :: Literal.t() | nil
|
||||
@dialyzer {:nowarn_function, cast: 1}
|
||||
|
@ -290,8 +317,8 @@ defmodule RDF.Literal.Datatype do
|
|||
Invalid literals are only considered equal in this relation when both have the exact same
|
||||
datatype and the same attributes (lexical form, language etc.).
|
||||
|
||||
Implementations can customize this equivalence relation via the `c:do_equal_value_different_datatypes?/2`
|
||||
and `c:do_equal_value_different_datatypes?/2` callbacks.
|
||||
Implementations can customize this equivalence relation via the `c:RDF.Literal.Datatype.do_equal_value_different_datatypes?/2`
|
||||
and `c:RDF.Literal.Datatype.do_equal_value_different_datatypes?/2` callbacks.
|
||||
"""
|
||||
def equal_value?(left, right)
|
||||
def equal_value?(left, %Literal{literal: right}), do: equal_value?(left, right)
|
||||
|
@ -382,6 +409,9 @@ defmodule RDF.Literal.Datatype do
|
|||
do_compare: 2
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates the value of a `RDF.Literal` without changing everything else.
|
||||
"""
|
||||
@impl unquote(__MODULE__)
|
||||
def update(literal, fun, opts \\ [])
|
||||
def update(%Literal{literal: literal}, fun, opts), do: update(literal, fun, opts)
|
||||
|
@ -420,6 +450,8 @@ defmodule RDF.Literal.Datatype do
|
|||
|
||||
defimpl_ex Registration, unquote(unquoted_id),
|
||||
for: RDF.Literal.Datatype.Registry.Registration do
|
||||
@moduledoc false
|
||||
|
||||
def datatype(id), do: unquote(datatype)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ProtocolEx
|
||||
|
||||
defprotocol_ex RDF.Literal.Datatype.Registry.Registration do
|
||||
@moduledoc false
|
||||
|
||||
def datatype(id), do: nil
|
||||
end
|
||||
|
|
|
@ -19,6 +19,9 @@ defmodule RDF.LangString do
|
|||
language: String.t
|
||||
}
|
||||
|
||||
@doc """
|
||||
Creates a new `RDF.Literal` with this datatype and the given `value` and `language`.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
@spec new(any, String.t | atom | keyword) :: Literal.t
|
||||
def new(value, language_or_opts \\ [])
|
||||
|
|
|
@ -2,7 +2,17 @@ defmodule RDF.XSD do
|
|||
@moduledoc """
|
||||
An implementation of the XML Schema (XSD) datatype system for use within `RDF.Literal.Datatype` system.
|
||||
|
||||
see <https://www.w3.org/TR/xmlschema-2/>
|
||||
It consists of
|
||||
|
||||
- `RDF.XSD.Datatype`: a more specialized `RDF.Literal.Datatype` behaviour for XSD datatypes
|
||||
- `RDF.XSD.Datatype.Primitive`: macros for the definition of `RDF.Literal.Datatype` and
|
||||
`RDF.XSD.Datatype` implementations for primitive XSD datatypes
|
||||
- `RDF.XSD.Datatype.Restriction`: macros for the definition of `RDF.Literal.Datatype` and
|
||||
`RDF.XSD.Datatype` implementations for derived XSD datatypes
|
||||
- `RDF.XSD.Facet`: a behaviour for XSD facets which can be used to constrain values on
|
||||
datatype derivations
|
||||
|
||||
see <https://www.w3.org/TR/xmlschema11-2/>
|
||||
"""
|
||||
|
||||
import RDF.Utils.Guards
|
||||
|
@ -22,6 +32,10 @@ defmodule RDF.XSD do
|
|||
|
||||
@facets_by_name Map.new(@facets, fn facet -> {facet.name(), facet} end)
|
||||
|
||||
@doc """
|
||||
Get a `RDF.XSD.Facet` by its name.
|
||||
"""
|
||||
def facet(name)
|
||||
def facet(name) when is_ordinary_atom(name), do: @facets_by_name[to_string(name)]
|
||||
def facet(name), do: @facets_by_name[name]
|
||||
|
||||
|
|
|
@ -1,6 +1,71 @@
|
|||
defmodule RDF.XSD.Datatype do
|
||||
@moduledoc """
|
||||
The behaviour of all XSD datatypes.
|
||||
A behaviour for XSD datatypes.
|
||||
|
||||
A XSD datatype has three properties:
|
||||
|
||||
- A _value space_, which is a set of values.
|
||||
- A _lexical space_, which is a set of _literals_ used to denote the values.
|
||||
- A collection of functions associated with the datatype.
|
||||
|
||||
|
||||
### Builtin XSD datatypes
|
||||
|
||||
RDF.ex comes with the following builtin implementations of XSD datatypes:
|
||||
|
||||
| `xsd:boolean` | `RDF.XSD.Boolean` |
|
||||
| `xsd:float` | `RDF.XSD.Float` |
|
||||
| `xsd:double` | `RDF.XSD.Double` |
|
||||
| `xsd:decimal` | `RDF.XSD.Decimal` |
|
||||
| `xsd:integer` | `RDF.XSD.Integer` |
|
||||
| `xsd:long` | `RDF.XSD.Long` |
|
||||
| `xsd:int` | `RDF.XSD.Int` |
|
||||
| `xsd:short` | `RDF.XSD.Short` |
|
||||
| `xsd:byte` | `RDF.XSD.Byte` |
|
||||
| `xsd:nonPositiveInteger` | `RDF.XSD.NonPositiveInteger` |
|
||||
| `xsd:negativeInteger` | `RDF.XSD.NegativeInteger` |
|
||||
| `xsd:nonNegativeInteger` | `RDF.XSD.NonNegativeInteger` |
|
||||
| `xsd:positiveInteger` | `RDF.XSD.PositiveInteger` |
|
||||
| `xsd:unsignedLong` | `RDF.XSD.UnsignedLong` |
|
||||
| `xsd:unsignedInt` | `RDF.XSD.UnsignedInt` |
|
||||
| `xsd:unsignedShort` | `RDF.XSD.UnsignedShort` |
|
||||
| `xsd:unsignedByte` | `RDF.XSD.UnsignedByte` |
|
||||
| `xsd:string` | `RDF.XSD.String` |
|
||||
| `xsd:normalizedString` | ❌ |
|
||||
| `xsd:token` | ❌ |
|
||||
| `xsd:language` | ❌ |
|
||||
| `xsd:Name` | ❌ |
|
||||
| `xsd:NCName` | ❌ |
|
||||
| `xsd:ID` | ❌ |
|
||||
| `xsd:IDREF` | ❌ |
|
||||
| `xsd:ENTITY` | ❌ |
|
||||
| `xsd:NMTOKEN` | ❌ |
|
||||
| `xsd:dateTime` | `RDF.XSD.DateTime` |
|
||||
| `xsd:dateTimeStamp` | ❌ |
|
||||
| `xsd:date` | `RDF.XSD.Date` |
|
||||
| `xsd:time` | `RDF.XSD.Time` |
|
||||
| `xsd:duration` | ❌ |
|
||||
| `xsd:dayTimeDuration` | ❌ |
|
||||
| `xsd:yearMonthDuration` | ❌ |
|
||||
| `xsd:gYearMonth` | ❌ |
|
||||
| `xsd:gYear` | ❌ |
|
||||
| `xsd:gMonthDay` | ❌ |
|
||||
| `xsd:gDay` | ❌ |
|
||||
| `xsd:gMonth` | ❌ |
|
||||
| `xsd:base64Binary` | ❌ |
|
||||
| `xsd:hexBinary` | ❌ |
|
||||
| `xsd:anyURI` | `RDF.XSD.AnyURI` |
|
||||
| `xsd:QName` | ❌ |
|
||||
| `xsd:NOTATION` | ❌ |
|
||||
|
||||
There are some notable difference in the implementations of some datatypes compared to
|
||||
the original spec:
|
||||
|
||||
- `RDF.XSD.Integer` is not derived from `RDF.XSD.Decimal`, but implemented as a primitive datatype
|
||||
- `RDF.XSD.Float` is not implemented as a primitive datatype, but derived from `RDF.XSD.Double`
|
||||
without further restrictions instead, since Erlang doesn't have a corresponding datatype
|
||||
|
||||
see <https://www.w3.org/TR/xmlschema11-2/#built-in-datatypes>
|
||||
"""
|
||||
|
||||
@type t :: module
|
||||
|
@ -22,6 +87,10 @@ defmodule RDF.XSD.Datatype do
|
|||
|
||||
@doc """
|
||||
The base datatype from which a `RDF.XSD.Datatype` is derived.
|
||||
|
||||
Note: Since this library focuses on atomic types and the special `xsd:anyAtomicType`
|
||||
specified as the base type of all primitive types in the W3C spec wouldn't serve any
|
||||
purpose here, all primitive datatypes just return `nil` instead.
|
||||
"""
|
||||
@callback base :: t() | nil
|
||||
|
||||
|
@ -52,6 +121,17 @@ defmodule RDF.XSD.Datatype do
|
|||
|
||||
@doc """
|
||||
A mapping from Elixir values into the value space of a `RDF.XSD.Datatype`.
|
||||
|
||||
If the Elixir mapping for the given value can not be mapped into value space of
|
||||
the XSD datatype an implementation should return `@invalid_value`
|
||||
(which is just `nil` at the moment, so `nil` is never a valid value of a value space).
|
||||
|
||||
Otherwise a tuple `{value, lexical}` with `value` being the internal representation
|
||||
of the mapped value from the value space and `lexical` being the lexical representation
|
||||
to be used for the Elixir value or `nil` if `c:init_valid_lexical/3` should be used
|
||||
to determine the lexical form in general (i.e. also when initialized with a string
|
||||
via the `c:lexical_mapping/2`). Since the later case is most often what you want,
|
||||
you can also return `value` directly, as long as it is not a two element tuple.
|
||||
"""
|
||||
@callback elixir_mapping(any, Keyword.t()) :: any | {any, uncanonical_lexical}
|
||||
|
||||
|
@ -61,18 +141,34 @@ defmodule RDF.XSD.Datatype do
|
|||
@callback canonical_mapping(any) :: String.t()
|
||||
|
||||
@doc """
|
||||
Produces the lexical representation to be used as for a `RDF.XSD.Datatype` literal.
|
||||
Produces the lexical representation to be used for a `RDF.XSD.Datatype` literal.
|
||||
|
||||
By default the lexical representation of a `RDF.XSD.Datatype` is either the
|
||||
canonical form in case it is created from a non-string Elixir value or, if it
|
||||
is created from a string, just with that string as the lexical form.
|
||||
|
||||
But there can be various reasons for why this should be different for certain
|
||||
datatypes. For example, for `RDF.XSD.Double`s given as Elixir floats, we want the
|
||||
default lexical representation to be the decimal and not the canonical
|
||||
exponential form. Another reason might be that additional options are given
|
||||
which should be taken into account in the lexical form.
|
||||
|
||||
If the lexical representation for a given `value` and `lexical` should be the
|
||||
canonical one, an implementation should return `nil`.
|
||||
"""
|
||||
@callback init_valid_lexical(any, uncanonical_lexical, Keyword.t()) :: uncanonical_lexical
|
||||
|
||||
@doc """
|
||||
Produces the lexical representation of an invalid value.
|
||||
|
||||
The default implementation of the `_using__` macro just returns `to_string/1`
|
||||
The default implementation of the `_using__` macro just returns the `to_string/1`
|
||||
representation of the value.
|
||||
"""
|
||||
@callback init_invalid_lexical(any, Keyword.t()) :: String.t()
|
||||
|
||||
@doc """
|
||||
Returns the `RDF.XSD.Datatype` for a datatype IRI.
|
||||
"""
|
||||
defdelegate get(id), to: RDF.Literal.Datatype.Registry, as: :xsd_datatype
|
||||
|
||||
@doc false
|
||||
|
@ -111,6 +207,9 @@ defmodule RDF.XSD.Datatype do
|
|||
"""
|
||||
def __xsd_datatype_indicator__, do: true
|
||||
|
||||
@doc """
|
||||
Checks if the given literal has datatype this or a datatype that is derived of it.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def datatype?(%RDF.Literal{literal: literal}), do: datatype?(literal)
|
||||
def datatype?(%datatype{}), do: datatype?(datatype)
|
||||
|
@ -129,6 +228,9 @@ defmodule RDF.XSD.Datatype do
|
|||
def datatype!(value),
|
||||
do: raise RDF.XSD.Datatype.Mismatch, value: value, expected_type: __MODULE__
|
||||
|
||||
@doc """
|
||||
Creates a new `RDF.Literal` with this datatype and the given `value`.
|
||||
"""
|
||||
# Dialyzer causes a warning on all primitives since the facet_conform?/2 call
|
||||
# always returns true there, so the other branch is unnecessary. This could
|
||||
# be fixed by generating a special version for primitives, but it's not worth
|
||||
|
@ -171,6 +273,9 @@ defmodule RDF.XSD.Datatype do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new `RDF.Literal` with this datatype and the given `value` or fails when it is not valid.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def new!(value, opts \\ []) do
|
||||
literal = new(value, opts)
|
||||
|
@ -205,6 +310,10 @@ defmodule RDF.XSD.Datatype do
|
|||
literal(%__MODULE__{uncanonical_lexical: init_invalid_lexical(lexical, opts)})
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Returns the value of a `RDF.Literal` of this or a derived datatype.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def value(%RDF.Literal{literal: literal}), do: value(literal)
|
||||
def value(%__MODULE__{} = literal), do: literal.value
|
||||
|
@ -215,6 +324,9 @@ defmodule RDF.XSD.Datatype do
|
|||
literal.value
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the lexical form of a `RDF.Literal` of this datatype.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def lexical(lexical)
|
||||
|
||||
|
@ -225,11 +337,17 @@ defmodule RDF.XSD.Datatype do
|
|||
|
||||
def lexical(%__MODULE__{uncanonical_lexical: lexical}), do: lexical
|
||||
|
||||
@doc """
|
||||
Returns the canonical lexical form of a `RDF.Literal` of this datatype.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def canonical_lexical(%RDF.Literal{literal: literal}), do: canonical_lexical(literal)
|
||||
def canonical_lexical(%__MODULE__{value: value}) when not is_nil(value), do: canonical_mapping(value)
|
||||
def canonical_lexical(_), do: nil
|
||||
|
||||
@doc """
|
||||
Produces the canonical representation of a `RDF.Literal` of this datatype.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def canonical(literal)
|
||||
|
||||
|
@ -245,12 +363,18 @@ defmodule RDF.XSD.Datatype do
|
|||
def canonical(%__MODULE__{} = literal),
|
||||
do: literal(%__MODULE__{literal | uncanonical_lexical: nil})
|
||||
|
||||
@doc """
|
||||
Determines if the lexical form of a `RDF.Literal` of this datatype is the canonical form.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def canonical?(literal)
|
||||
def canonical?(%RDF.Literal{literal: literal}), do: canonical?(literal)
|
||||
def canonical?(%__MODULE__{uncanonical_lexical: nil}), do: true
|
||||
def canonical?(%__MODULE__{}), do: false
|
||||
|
||||
@doc """
|
||||
Determines if a `RDF.Literal` of this or a derived datatype has a proper value of its value space.
|
||||
"""
|
||||
@impl RDF.Literal.Datatype
|
||||
def valid?(literal)
|
||||
def valid?(%RDF.Literal{literal: literal}), do: valid?(literal)
|
||||
|
@ -275,6 +399,16 @@ defmodule RDF.XSD.Datatype do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Compares two `RDF.Literal`s.
|
||||
|
||||
If the first literal is greater than the second `:gt` is returned, if less than `:lt` is returned.
|
||||
If both literal are equal `:eq` is returned.
|
||||
If the literals can not be compared either `nil` is returned, when they generally can be compared
|
||||
due to their datatype, or `:indeterminate` is returned, when the order of the given values is
|
||||
not defined on only partially ordered datatypes.
|
||||
"""
|
||||
@spec compare(RDF.Literal.t() | any, RDF.Literal.t() | any) :: RDF.Literal.Datatype.comparison_result | :indeterminate | nil
|
||||
def compare(left, right)
|
||||
def compare(left, %RDF.Literal{literal: right}), do: compare(left, right)
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
defmodule RDF.XSD.Datatype.Primitive do
|
||||
@moduledoc """
|
||||
Macros for the definition of primitive XSD datatypes.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Specifies the applicability of the given XSD `facet` on a primitive datatype.
|
||||
|
||||
For a facet with the name `example_facet` this requires a function
|
||||
|
||||
def example_facet_conform?(example_facet_value, literal_value, lexical) do
|
||||
|
||||
end
|
||||
|
||||
to be defined on the primitive datatype.
|
||||
"""
|
||||
defmacro def_applicable_facet(facet) do
|
||||
quote do
|
||||
@applicable_facets unquote(facet)
|
||||
|
|
|
@ -1,4 +1,25 @@
|
|||
defmodule RDF.XSD.Datatype.Restriction do
|
||||
@moduledoc """
|
||||
A `__using__` macro for the derivation of restricted XSD datatypes.
|
||||
|
||||
### Example
|
||||
|
||||
A new custom datatype can be derived by defining a new datatype module and `use` this module,
|
||||
specifying a `name`, `id` and `base` datatype and restricting it by specifying some of the
|
||||
applicable facets of the base datatype:
|
||||
|
||||
defmodule MyApp.PersonAge do
|
||||
use RDF.XSD.Datatype.Restriction,
|
||||
name: "person_age",
|
||||
id: "http://example.com/person_age",
|
||||
base: RDF.XSD.NonNegativeInteger
|
||||
|
||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
|
||||
end
|
||||
|
||||
See `RDF.XSD.Facet` on which facets are available on the existing datatypes.
|
||||
"""
|
||||
|
||||
defmacro __using__(opts) do
|
||||
base = Keyword.fetch!(opts, :base)
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ defmodule RDF.XSD.Date do
|
|||
|
||||
Options:
|
||||
|
||||
- `tz` ... it will also overwrite an eventually already present timezone in an input lexical ...
|
||||
- `tz`: this allows to specify a timezone which is not supported by Elixir's `Date` struct; note,
|
||||
that it will also overwrite an eventually already present timezone in an input lexical
|
||||
"""
|
||||
|
||||
@type valid_value :: Date.t() | {Date.t(), String.t()}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
defmodule RDF.XSD.Time do
|
||||
@moduledoc """
|
||||
`RDF.XSD.Datatype` for XSD times.
|
||||
|
||||
Options:
|
||||
|
||||
- `tz`: this allows to specify a timezone which is not supported by Elixir's `Time` struct; note,
|
||||
that it will also overwrite an eventually already present timezone in an input lexical
|
||||
"""
|
||||
|
||||
@type valid_value :: Time.t() | {Time.t(), true}
|
||||
|
|
|
@ -1,4 +1,46 @@
|
|||
defmodule RDF.XSD.Facet do
|
||||
@moduledoc """
|
||||
A behaviour for XSD restriction facets.
|
||||
|
||||
Here's a list of all the `RDF.XSD.Facet`s RDF.ex implements out-of-the-box:
|
||||
|
||||
| XSD facet | `RDF.XSD.Facet` |
|
||||
| :-------------- | :------------- |
|
||||
| length | `RDF.XSD.Facets.Length` |
|
||||
| minLength | `RDF.XSD.Facets.MinLength` |
|
||||
| maxLength | `RDF.XSD.Facets.MaxLength` |
|
||||
| maxInclusive | `RDF.XSD.Facets.MaxInclusive` |
|
||||
| maxExclusive | `RDF.XSD.Facets.MaxExclusive` |
|
||||
| minInclusive | `RDF.XSD.Facets.MinInclusive` |
|
||||
| minExclusive | `RDF.XSD.Facets.MinExclusive` |
|
||||
| totalDigits | `RDF.XSD.Facets.TotalDigits` |
|
||||
| fractionDigits | `RDF.XSD.Facets.FractionDigits` |
|
||||
| explicitTimezone | `RDF.XSD.Facets.ExplicitTimezone` |
|
||||
| pattern | `RDF.XSD.Facets.Pattern` |
|
||||
| whiteSpace | ❌ |
|
||||
| enumeration | ❌ |
|
||||
| assertions | ❌ |
|
||||
|
||||
Every `RDF.XSD.Datatype.Primitive` defines a set of applicable constraining facets which are can
|
||||
be used on derivations of this primitive or any of its existing derivations:
|
||||
|
||||
| Primitive datatype | Applicable facets |
|
||||
| :----------------- | :---------------- |
|
||||
|string | `RDF.XSD.Facets.Length`, `RDF.XSD.Facets.MaxLength`, `RDF.XSD.Facets.MinLength`, `RDF.XSD.Facets.Pattern` |
|
||||
|boolean | `RDF.XSD.Facets.Pattern` |
|
||||
|float | `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern` |
|
||||
|double | `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern` |
|
||||
|decimal | `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern`, `RDF.XSD.Facets.TotalDigits`, `RDF.XSD.Facets.FractionDigits` |
|
||||
|decimal | `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern`, `RDF.XSD.Facets.TotalDigits` |
|
||||
|duration | `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern` |
|
||||
|dateTime | `RDF.XSD.Facets.ExplicitTimezone`, `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern` |
|
||||
|time | `RDF.XSD.Facets.ExplicitTimezone`, `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern` |
|
||||
|date | `RDF.XSD.Facets.ExplicitTimezone`, `RDF.XSD.Facets.MaxExclusive`, `RDF.XSD.Facets.MaxInclusive`, `RDF.XSD.Facets.MinExclusive`, `RDF.XSD.Facets.MinInclusive`, `RDF.XSD.Facets.Pattern` |
|
||||
|anyURI | `RDF.XSD.Facets.Length`, `RDF.XSD.Facets.MaxLength`, `RDF.XSD.Facets.MinLength`, `RDF.XSD.Facets.Pattern` |
|
||||
|
||||
<https://www.w3.org/TR/xmlschema11-2/datatypes.html#rf-facets>
|
||||
"""
|
||||
|
||||
@type t :: module
|
||||
|
||||
@doc """
|
||||
|
|
Loading…
Reference in a new issue