Restore ability to cast RDF.IRIs to XSD.Strings and XSD.AnyURIs

This is needed for SPARQL.
This commit is contained in:
Marcel Otto 2020-05-23 00:31:15 +02:00
parent ffebf86505
commit 7daf494fb9
5 changed files with 23 additions and 4 deletions

View file

@ -27,14 +27,15 @@ defmodule RDF.Literal.Datatype do
Casts a datatype literal of one type into a datatype literal of another type.
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.
So, implementations can assume the passed argument is a valid `RDF.Literal.Datatype` struct,
a `RDF.IRI` or a `RDF.BlankNode`.
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) :: Literal.t() | nil
@callback do_cast(literal | RDF.IRI.t | RDF.BlankNode.t) :: 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.
@ -231,14 +232,15 @@ defmodule RDF.Literal.Datatype do
Implementations define the casting for a given value with the `c:do_cast/1` callback.
"""
@spec cast(Literal.t | Literal.Datatype.literal) :: Literal.t() | nil
@spec cast(Literal.Datatype.literal | RDF.Term.t) :: 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(%struct{} = datatype_literal) do
if Literal.datatype?(struct) and Literal.Datatype.valid?(datatype_literal) do
if (Literal.datatype?(struct) and Literal.Datatype.valid?(datatype_literal)) or
struct in [RDF.IRI, RDF.BlankNode] do
case do_cast(datatype_literal) do
%__MODULE__{} = literal -> if valid?(literal), do: literal(literal)
%Literal{literal: %__MODULE__{}} = literal -> if valid?(literal), do: literal

View file

@ -60,6 +60,10 @@ 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
def do_equal_value?(literal1, literal2)

View file

@ -47,6 +47,10 @@ defmodule RDF.XSD.String do
def elixir_mapping(value, _), do: to_string(value)
@impl RDF.Literal.Datatype
def do_cast(literal_or_iri)
def do_cast(%RDF.IRI{value: value}), do: new(value)
def do_cast(%datatype{} = literal) do
cond do
XSD.Decimal.datatype?(literal) ->

View file

@ -34,5 +34,10 @@ 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
end
end

View file

@ -127,6 +127,10 @@ defmodule RDF.XSD.StringTest do
assert FloatUnitInterval.new(1.0) |> XSD.String.cast() == XSD.string("1")
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