Support derived datatypes on RDF.Literal.Datatype.value/1 and valid?/2
This commit is contained in:
parent
7daf494fb9
commit
d247e1bf4f
6 changed files with 71 additions and 2 deletions
|
@ -22,6 +22,14 @@ defmodule RDF.Triple.InvalidPredicateError do
|
|||
end
|
||||
end
|
||||
|
||||
defmodule RDF.XSD.Datatype.Mismatch do
|
||||
defexception [:value, :expected_type]
|
||||
|
||||
def message(%{value: value, expected_type: expected_type}) do
|
||||
"'#{inspect(value)}' is not a #{expected_type}"
|
||||
end
|
||||
end
|
||||
|
||||
defmodule RDF.Quad.InvalidGraphContextError do
|
||||
defexception [:graph_context]
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ defmodule RDF.Literal.Datatype do
|
|||
|
||||
@doc """
|
||||
Returns the value of a `RDF.Literal`.
|
||||
|
||||
This function also accepts literals of derived datatypes.
|
||||
"""
|
||||
@callback value(Literal.t | literal) :: any
|
||||
|
||||
|
@ -84,12 +86,14 @@ defmodule RDF.Literal.Datatype do
|
|||
Determines if the lexical form of a `RDF.Literal` is the canonical form.
|
||||
|
||||
Note: For `RDF.Literal.Generic` literals with the canonical form not defined,
|
||||
this always return `true`.
|
||||
this always returns `true`.
|
||||
"""
|
||||
@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.
|
||||
|
||||
This function also accepts literals of derived datatypes.
|
||||
"""
|
||||
@callback valid?(Literal.t() | literal | any) :: boolean
|
||||
|
||||
|
|
|
@ -110,6 +110,15 @@ defmodule RDF.XSD.Datatype do
|
|||
end
|
||||
def datatype?(_), do: false
|
||||
|
||||
@doc false
|
||||
def datatype!(%__MODULE__{}), do: true
|
||||
def datatype!((%datatype{} = literal)) do
|
||||
datatype?(datatype) ||
|
||||
raise RDF.XSD.Datatype.Mismatch, value: literal, expected_type: __MODULE__
|
||||
end
|
||||
def datatype!(value),
|
||||
do: raise RDF.XSD.Datatype.Mismatch, value: value, expected_type: __MODULE__
|
||||
|
||||
# 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
|
||||
|
@ -190,6 +199,12 @@ defmodule RDF.XSD.Datatype do
|
|||
def value(%RDF.Literal{literal: literal}), do: value(literal)
|
||||
def value(%__MODULE__{} = literal), do: literal.value
|
||||
|
||||
def value(literal) do
|
||||
datatype!(literal)
|
||||
|
||||
literal.value
|
||||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def lexical(lexical)
|
||||
|
||||
|
@ -226,6 +241,8 @@ defmodule RDF.XSD.Datatype do
|
|||
def valid?(%RDF.Literal{literal: literal}), do: valid?(literal)
|
||||
def valid?(%__MODULE__{value: @invalid_value}), do: false
|
||||
def valid?(%__MODULE__{}), do: true
|
||||
def valid?((%datatype{} = literal)),
|
||||
do: datatype?(datatype) and datatype.valid?(literal)
|
||||
def valid?(_), do: false
|
||||
|
||||
defimpl Inspect do
|
||||
|
|
|
@ -36,6 +36,9 @@ defmodule RDF.TestDatatypes do
|
|||
base: RDF.XSD.PositiveInteger
|
||||
|
||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
|
||||
|
||||
@impl RDF.XSD.Datatype
|
||||
def canonical_mapping(value), do: "#{value} years"
|
||||
end
|
||||
|
||||
defmodule DecimalUnitInterval do
|
||||
|
|
|
@ -23,6 +23,43 @@ defmodule RDF.XSD.IntegerTest do
|
|||
valid: RDF.XSD.TestData.valid_integers(),
|
||||
invalid: RDF.XSD.TestData.invalid_integers()
|
||||
|
||||
alias RDF.TestDatatypes.Age
|
||||
|
||||
describe "value/1" do
|
||||
test "with a derived datatype" do
|
||||
assert XSD.byte(42) |> XSD.Integer.value() == 42
|
||||
assert Age.new(42) |> XSD.Integer.value() == 42
|
||||
end
|
||||
|
||||
test "with another datatype" do
|
||||
assert_raise RDF.XSD.Datatype.Mismatch, "'#{inspect(XSD.decimal(42).literal)}' is not a #{XSD.Integer}", fn ->
|
||||
XSD.decimal(42) |> XSD.Integer.value()
|
||||
end
|
||||
end
|
||||
|
||||
test "with a non-literal" do
|
||||
assert_raise RDF.XSD.Datatype.Mismatch, "'42' is not a #{XSD.Integer}", fn ->
|
||||
XSD.Integer.value(42)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "valid?/1" do
|
||||
test "with a derived datatype" do
|
||||
assert XSD.byte(42) |> XSD.Integer.valid?() == true
|
||||
assert Age.new(42) |> XSD.Integer.valid?() == true
|
||||
assert Age.new(200) |> XSD.Integer.valid?() == false
|
||||
end
|
||||
|
||||
test "with another datatype" do
|
||||
assert XSD.decimal(42) |> XSD.Integer.valid?() == false
|
||||
end
|
||||
|
||||
test "with a non-literal" do
|
||||
assert XSD.Integer.valid?(42) == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "cast/1" do
|
||||
test "casting an integer returns the input as it is" do
|
||||
assert XSD.integer(0) |> XSD.Integer.cast() == XSD.integer(0)
|
||||
|
|
|
@ -121,7 +121,7 @@ defmodule RDF.XSD.StringTest do
|
|||
|
||||
test "from derived types of the castable datatypes" do
|
||||
assert XSD.byte(42) |> XSD.String.cast() == XSD.string("42")
|
||||
assert Age.new(42) |> XSD.String.cast() == XSD.string("42")
|
||||
assert Age.new(42) |> XSD.String.cast() == XSD.string("42 years")
|
||||
assert DecimalUnitInterval.new(0.14) |> XSD.String.cast() == XSD.string("0.14")
|
||||
assert DoubleUnitInterval.new(0.14) |> XSD.String.cast() == XSD.string("0.14")
|
||||
assert FloatUnitInterval.new(1.0) |> XSD.String.cast() == XSD.string("1")
|
||||
|
|
Loading…
Reference in a new issue