Remove coercion on RDF.Literal.Datatype.cast/1

It's not worth the effort and more explicit to do the coercion step

manually.
This commit is contained in:
Marcel Otto 2020-05-21 01:37:19 +02:00
parent accf66d75f
commit c1a61c58e8
16 changed files with 27 additions and 169 deletions

View file

@ -24,15 +24,17 @@ defmodule RDF.Literal.Datatype do
@callback new!(any, Keyword.t()) :: Literal.t()
@doc """
Casts a datatype literal or coercible value of one type into a datatype literal of another type.
Casts a datatype literal of one type into a datatype literal of another type.
If the given literal or value is invalid or can not be converted into this datatype an
implementation should return `nil`.
This function is called by the auto-generated `cast/1` function on the implementations, which already deals with the basic cases.
So, implementations can assume the passed argument is a valid `RDF.Literal.Datatype` struct.
This function is called by auto-generated `cast/1` function on the implementations,
which already deals with basic cases and coercion.
If the given literal can not be converted into this datatype an implementation should return `nil`.
A final catch-all clause should delegate to `super`. For example `RDF.XSD.Datatype`s will handle casting from derived
datatypes in the default implementation.
"""
@callback do_cast(literal | any) :: Literal.t() | nil
@callback do_cast(literal) :: Literal.t() | nil
@doc """
Checks if the given `RDF.Literal` has the datatype for which the `RDF.Literal.Datatype` is implemented or is derived from it.
@ -222,32 +224,33 @@ defmodule RDF.Literal.Datatype do
def canonical_lexical(_), do: nil
@doc """
Casts a datatype literal or coercible value of one type into a datatype literal of another type.
Casts a datatype literal of one type into a datatype literal of another type.
Returns `nil` when the given arguments are not comparable as literals of this
datatype or when the given argument is an invalid literal.
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.
"""
@spec cast(any) :: Literal.t() | nil
@spec cast(Literal.t | Literal.Datatype.literal) :: Literal.t() | nil
@dialyzer {:nowarn_function, cast: 1}
def cast(literal_or_value)
def cast(%Literal{literal: literal}), do: cast(literal)
def cast(%__MODULE__{} = datatype_literal),
do: if(valid?(datatype_literal), do: literal(datatype_literal))
def cast(nil), do: nil
def cast(value) do
case do_cast(value) do
%__MODULE__{} = literal -> if valid?(literal), do: literal(literal)
%Literal{literal: %__MODULE__{}} = literal -> if valid?(literal), do: literal
_ -> nil
def cast(%struct{} = datatype_literal) do
if Literal.datatype?(struct) and Literal.Datatype.valid?(datatype_literal) do
case do_cast(datatype_literal) do
%__MODULE__{} = literal -> if valid?(literal), do: literal(literal)
%Literal{literal: %__MODULE__{}} = literal -> if valid?(literal), do: literal
_ -> nil
end
end
end
def cast(_), do: nil
@impl unquote(__MODULE__)
def do_cast(value) do
value |> Literal.coerce() |> cast()
end
def do_cast(value), do: nil
@doc """
Checks if two datatype literals are equal in terms of the values of their value space.

View file

@ -186,20 +186,6 @@ defmodule RDF.XSD.Datatype do
literal(%__MODULE__{uncanonical_lexical: init_invalid_lexical(lexical, opts)})
end
def cast(literal_or_value)
def cast(%RDF.Literal{literal: literal}), do: cast(literal)
# Invalid values can not be casted in general
def cast(%{value: @invalid_value}), do: nil
def cast(%__MODULE__{} = datatype_literal), do: literal(datatype_literal)
def cast(nil), do: nil
def cast(value) do
case do_cast(value) do
%__MODULE__{} = literal -> if valid?(literal), do: literal(literal)
%RDF.Literal{literal: %__MODULE__{}} = literal -> if valid?(literal), do: literal
_ -> nil
end
end
@impl RDF.Literal.Datatype
def value(%RDF.Literal{literal: literal}), do: value(literal)
def value(%__MODULE__{} = literal), do: literal.value

View file

@ -43,12 +43,8 @@ defmodule RDF.XSD.Datatype.Primitive do
@impl RDF.Literal.Datatype
def do_cast(value) do
if RDF.Literal.datatype?(value) do
if datatype?(value) do
build_valid(value.value, value.uncanonical_lexical, [])
end
else
value |> RDF.Literal.coerce() |> cast()
if datatype?(value) do # i.e. derived datatype
build_valid(value.value, value.uncanonical_lexical, [])
end
end

View file

@ -22,6 +22,7 @@ defmodule RDF.XSD.AnyURI do
@impl RDF.XSD.Datatype
@spec elixir_mapping(any, Keyword.t()) :: value
def elixir_mapping(%URI{} = uri, _), do: uri
def elixir_mapping(%IRI{} = iri, _), do: IRI.parse(iri)
def elixir_mapping(value, _) when maybe_ns_term(value) do
case RDF.Namespace.resolve_term(value) do
@ -33,7 +34,6 @@ defmodule RDF.XSD.AnyURI do
def elixir_mapping(_, _), do: @invalid_value
@impl RDF.Literal.Datatype
def do_cast(%IRI{} = iri), do: new(iri.value)
def do_cast(value), do: super(value)
@impl RDF.Literal.Datatype

View file

@ -39,8 +39,6 @@ defmodule RDF.XSD.String do
@impl RDF.Literal.Datatype
def do_cast(value)
def do_cast(%RDF.IRI{value: value}), do: new(value)
def do_cast(%RDF.XSD.Decimal{} = xsd_decimal) do
try do
xsd_decimal.value

View file

@ -9,6 +9,8 @@ defmodule RDF.XSD.AnyURITest do
{URI.parse("http://example.com/foo"), nil, "http://example.com/foo"},
URI.parse("http://example.com/foo") =>
{URI.parse("http://example.com/foo"), nil, "http://example.com/foo"},
RDF.iri("http://example.com/foo") =>
{URI.parse("http://example.com/foo"), nil, "http://example.com/foo"},
RDF.List =>
{URI.parse("http://www.w3.org/1999/02/22-rdf-syntax-ns#List"), nil, "http://www.w3.org/1999/02/22-rdf-syntax-ns#List"},
},
@ -20,20 +22,5 @@ defmodule RDF.XSD.AnyURITest do
assert XSD.anyURI("http://example.com/") |> XSD.AnyURI.cast() ==
XSD.anyURI("http://example.com/")
end
test "casting an RDF.IRI" do
assert RDF.iri("http://example.com/") |> XSD.AnyURI.cast() ==
XSD.anyURI("http://example.com/")
end
test "with coercible value" do
assert URI.parse("http://example.com/") |> XSD.AnyURI.cast() ==
XSD.anyURI("http://example.com/")
end
test "with non-coercible value" do
assert XSD.string("http://example.com/") |> XSD.AnyURI.cast() == nil
assert XSD.AnyURI.cast(make_ref()) == nil
end
end
end

View file

@ -91,20 +91,6 @@ defmodule RDF.XSD.BooleanTest do
test "with values of unsupported datatypes" do
assert XSD.date("2020-01-01") |> XSD.Boolean.cast() == nil
end
test "with coercible value" do
assert XSD.Boolean.cast(0) == XSD.false()
assert XSD.Boolean.cast(0.0) == XSD.false()
assert XSD.Boolean.cast(42) == XSD.true()
assert XSD.Boolean.cast(3.14) == XSD.true()
assert XSD.Boolean.cast("true") == XSD.true()
assert XSD.Boolean.cast("false") == XSD.false()
end
test "with non-coercible value" do
assert XSD.Boolean.cast(:foo) == nil
assert XSD.Boolean.cast(make_ref()) == nil
end
end
describe "ebv/1" do

View file

@ -127,14 +127,5 @@ defmodule RDF.XSD.DateTest do
assert XSD.integer(1) |> XSD.Date.cast() == nil
assert XSD.decimal(3.14) |> XSD.Date.cast() == nil
end
test "with coercible value" do
assert XSD.Date.cast("2010-01-01") == XSD.date("2010-01-01")
end
test "with non-coercible value" do
assert XSD.Date.cast(:foo) == nil
assert XSD.Date.cast(make_ref()) == nil
end
end
end

View file

@ -113,15 +113,6 @@ defmodule RDF.XSD.DateTimeTest do
assert XSD.integer(1) |> XSD.DateTime.cast() == nil
assert XSD.decimal(3.14) |> XSD.DateTime.cast() == nil
end
test "with coercible value" do
assert XSD.DateTime.cast("2010-01-01T12:34:56") == XSD.datetime("2010-01-01T12:34:56")
end
test "with non-coercible value" do
assert XSD.DateTime.cast(:foo) == nil
assert XSD.DateTime.cast(make_ref()) == nil
end
end
test "now/0" do

View file

@ -127,16 +127,6 @@ defmodule RDF.XSD.DecimalTest do
test "with literals of unsupported datatypes" do
assert XSD.date("2020-01-01") |> XSD.Decimal.cast() == nil
end
test "with coercible value" do
assert XSD.Decimal.cast("3.14") == XSD.decimal(3.14)
assert XSD.Decimal.cast("42") == XSD.decimal(42.0)
end
test "with non-coercible value" do
assert XSD.Decimal.cast(:foo) == nil
assert XSD.Decimal.cast(make_ref()) == nil
end
end
test "digit_count/1" do

View file

@ -68,14 +68,5 @@ defmodule RDF.XSD.DoubleTest do
test "with literals of unsupported datatypes" do
assert XSD.date("2020-01-01") |> XSD.Double.cast() == nil
end
test "with coercible value" do
assert XSD.Double.cast("3.14") == XSD.double(3.14) |> XSD.Double.canonical()
end
test "with non-coercible value" do
assert XSD.Double.cast(:foo) == nil
assert XSD.Double.cast(make_ref()) == nil
end
end
end

View file

@ -85,18 +85,6 @@ defmodule RDF.XSD.IntegerTest do
test "with literals of unsupported datatypes" do
assert XSD.date("2020-01-01") |> XSD.Integer.cast() == nil
end
test "with coercible value" do
assert XSD.Integer.cast("42") == XSD.integer(42)
assert XSD.Integer.cast(3.14) == XSD.integer(3)
assert XSD.Integer.cast(true) == XSD.integer(1)
assert XSD.Integer.cast(false) == XSD.integer(0)
end
test "with non-coercible value" do
assert XSD.Integer.cast(:foo) == nil
assert XSD.Integer.cast(make_ref()) == nil
end
end
test "digit_count/1" do

View file

@ -107,17 +107,5 @@ defmodule RDF.XSD.NonNegativeIntegerTest do
test "with literals of unsupported datatypes" do
assert XSD.date("2020-01-01") |> XSD.NonNegativeInteger.cast() == nil
end
test "with coercible value" do
assert XSD.NonNegativeInteger.cast("42") == XSD.non_negative_integer(42)
assert XSD.NonNegativeInteger.cast(3.14) == XSD.non_negative_integer(3)
assert XSD.NonNegativeInteger.cast(true) == XSD.non_negative_integer(1)
assert XSD.NonNegativeInteger.cast(false) == XSD.non_negative_integer(0)
end
test "with non-coercible value" do
assert XSD.NonNegativeInteger.cast(:foo) == nil
assert XSD.NonNegativeInteger.cast(make_ref()) == nil
end
end
end

View file

@ -117,17 +117,5 @@ defmodule RDF.XSD.PositiveIntegerTest do
test "with literals of unsupported datatypes" do
assert XSD.date("2020-01-01") |> XSD.PositiveInteger.cast() == nil
end
test "with coercible value" do
assert XSD.PositiveInteger.cast("42") == XSD.positive_integer(42)
assert XSD.PositiveInteger.cast(3.14) == XSD.positive_integer(3)
assert XSD.PositiveInteger.cast(true) == XSD.positive_integer(1)
assert XSD.PositiveInteger.cast(false) == nil
end
test "with non-coercible value" do
assert XSD.PositiveInteger.cast(:foo) == nil
assert XSD.PositiveInteger.cast(make_ref()) == nil
end
end
end

View file

@ -117,26 +117,10 @@ defmodule RDF.XSD.StringTest do
assert XSD.time("00:00:00+01:00") |> XSD.String.cast() == XSD.string("00:00:00+01:00")
end
test "casting an IRI" do
assert RDF.iri("http://example.com") |> XSD.String.cast() == XSD.string("http://example.com")
end
test "with invalid literals" do
assert XSD.integer(3.14) |> XSD.String.cast() == nil
assert XSD.decimal("NAN") |> XSD.String.cast() == nil
assert XSD.double(true) |> XSD.String.cast() == nil
end
test "with coercible value" do
assert XSD.String.cast(42) == XSD.string("42")
assert XSD.String.cast(3.14) == XSD.string("3.14")
assert XSD.String.cast(true) == XSD.string("true")
assert XSD.String.cast(false) == XSD.string("false")
end
test "with non-coercible value" do
assert XSD.String.cast(:foo) == nil
assert XSD.String.cast(make_ref()) == nil
end
end
end

View file

@ -138,14 +138,5 @@ defmodule RDF.XSD.TimeTest do
assert XSD.integer(1) |> XSD.Time.cast() == nil
assert XSD.decimal(3.14) |> XSD.Time.cast() == nil
end
test "with coercible value" do
assert XSD.Time.cast("01:00:00") == XSD.time("01:00:00")
end
test "with non-coercible value" do
assert XSD.Time.cast(:foo) == nil
assert XSD.Time.cast(make_ref()) == nil
end
end
end