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
|
||||||
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
|
defmodule RDF.Quad.InvalidGraphContextError do
|
||||||
defexception [:graph_context]
|
defexception [:graph_context]
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,8 @@ defmodule RDF.Literal.Datatype do
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the value of a `RDF.Literal`.
|
Returns the value of a `RDF.Literal`.
|
||||||
|
|
||||||
|
This function also accepts literals of derived datatypes.
|
||||||
"""
|
"""
|
||||||
@callback value(Literal.t | literal) :: any
|
@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.
|
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,
|
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
|
@callback canonical?(Literal.t() | literal | any) :: boolean
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Determines if the lexical form of a `RDF.Literal` is a member of its lexical value space.
|
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
|
@callback valid?(Literal.t() | literal | any) :: boolean
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,15 @@ defmodule RDF.XSD.Datatype do
|
||||||
end
|
end
|
||||||
def datatype?(_), do: false
|
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
|
# 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
|
# 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
|
# 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(%RDF.Literal{literal: literal}), do: value(literal)
|
||||||
def value(%__MODULE__{} = literal), do: literal.value
|
def value(%__MODULE__{} = literal), do: literal.value
|
||||||
|
|
||||||
|
def value(literal) do
|
||||||
|
datatype!(literal)
|
||||||
|
|
||||||
|
literal.value
|
||||||
|
end
|
||||||
|
|
||||||
@impl RDF.Literal.Datatype
|
@impl RDF.Literal.Datatype
|
||||||
def lexical(lexical)
|
def lexical(lexical)
|
||||||
|
|
||||||
|
@ -226,6 +241,8 @@ defmodule RDF.XSD.Datatype do
|
||||||
def valid?(%RDF.Literal{literal: literal}), do: valid?(literal)
|
def valid?(%RDF.Literal{literal: literal}), do: valid?(literal)
|
||||||
def valid?(%__MODULE__{value: @invalid_value}), do: false
|
def valid?(%__MODULE__{value: @invalid_value}), do: false
|
||||||
def valid?(%__MODULE__{}), do: true
|
def valid?(%__MODULE__{}), do: true
|
||||||
|
def valid?((%datatype{} = literal)),
|
||||||
|
do: datatype?(datatype) and datatype.valid?(literal)
|
||||||
def valid?(_), do: false
|
def valid?(_), do: false
|
||||||
|
|
||||||
defimpl Inspect do
|
defimpl Inspect do
|
||||||
|
|
|
@ -36,6 +36,9 @@ defmodule RDF.TestDatatypes do
|
||||||
base: RDF.XSD.PositiveInteger
|
base: RDF.XSD.PositiveInteger
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
|
||||||
|
|
||||||
|
@impl RDF.XSD.Datatype
|
||||||
|
def canonical_mapping(value), do: "#{value} years"
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule DecimalUnitInterval do
|
defmodule DecimalUnitInterval do
|
||||||
|
|
|
@ -23,6 +23,43 @@ defmodule RDF.XSD.IntegerTest do
|
||||||
valid: RDF.XSD.TestData.valid_integers(),
|
valid: RDF.XSD.TestData.valid_integers(),
|
||||||
invalid: RDF.XSD.TestData.invalid_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
|
describe "cast/1" do
|
||||||
test "casting an integer returns the input as it is" do
|
test "casting an integer returns the input as it is" do
|
||||||
assert XSD.integer(0) |> XSD.Integer.cast() == XSD.integer(0)
|
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
|
test "from derived types of the castable datatypes" do
|
||||||
assert XSD.byte(42) |> XSD.String.cast() == XSD.string("42")
|
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 DecimalUnitInterval.new(0.14) |> XSD.String.cast() == XSD.string("0.14")
|
||||||
assert DoubleUnitInterval.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")
|
assert FloatUnitInterval.new(1.0) |> XSD.String.cast() == XSD.string("1")
|
||||||
|
|
Loading…
Reference in a new issue