/
generic.ex
131 lines (103 loc) · 3.6 KB
/
generic.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
defmodule RDF.Literal.Generic do
@moduledoc """
A generic `RDF.Literal.Datatype` for literals of an unknown datatype.
"""
defstruct [:value, :datatype]
use RDF.Literal.Datatype,
name: "generic",
id: nil
alias RDF.Literal.Datatype
alias RDF.{Literal, IRI}
import RDF.Guards
@type t :: %__MODULE__{
value: String.t(),
datatype: String.t()
}
@impl Datatype
@spec new(any, String.t() | IRI.t() | keyword) :: Literal.t()
def new(value, datatype_or_opts \\ [])
def new(value, %IRI{} = datatype), do: new(value, datatype: datatype)
def new(value, datatype) when is_binary(datatype) or maybe_ns_term(datatype),
do: new(value, datatype: datatype)
def new(value, opts) do
%Literal{
literal: %__MODULE__{
value: value,
datatype: Keyword.get(opts, :datatype) |> normalize_datatype()
}
}
end
defp normalize_datatype(nil), do: nil
defp normalize_datatype(""), do: nil
defp normalize_datatype(%IRI{} = datatype), do: to_string(datatype)
defp normalize_datatype(datatype) when maybe_ns_term(datatype),
do: datatype |> RDF.iri() |> to_string()
defp normalize_datatype(datatype), do: datatype
@impl Datatype
@spec new!(any, String.t() | IRI.t() | keyword) :: Literal.t()
def new!(value, datatype_or_opts \\ []) do
literal = new(value, datatype_or_opts)
if valid?(literal) do
literal
else
raise ArgumentError,
"#{inspect(value)} with datatype #{inspect(literal.literal.datatype)} is not a valid #{inspect(__MODULE__)}"
end
end
@impl Datatype
def datatype_id(%Literal{literal: literal}), do: datatype_id(literal)
def datatype_id(%__MODULE__{} = literal), do: RDF.iri(literal.datatype)
@impl Datatype
def value(%Literal{literal: literal}), do: value(literal)
def value(%__MODULE__{} = literal), do: literal.value
@impl Datatype
def lexical(%Literal{literal: literal}), do: lexical(literal)
def lexical(%__MODULE__{} = literal), do: literal.value
@impl Datatype
def canonical(%Literal{literal: %__MODULE__{}} = literal), do: literal
def canonical(%__MODULE__{} = literal), do: literal(literal)
@impl Datatype
def canonical?(%Literal{literal: literal}), do: canonical?(literal)
def canonical?(%__MODULE__{}), do: true
@impl Datatype
def valid?(%Literal{literal: %__MODULE__{} = literal}), do: valid?(literal)
def valid?(%__MODULE__{datatype: datatype}) when is_binary(datatype), do: true
def valid?(_), do: false
@doc """
Since generic literals don't support casting, always returns `nil`.
"""
def cast(_), do: nil
@impl Datatype
def do_cast(_), do: nil
@impl Datatype
def do_equal_value_same_or_derived_datatypes?(
%{datatype: datatype} = left,
%{datatype: datatype} = right
),
do: left == right
def do_equal_value_same_or_derived_datatypes?(_, _), do: nil
@impl Datatype
def do_compare(
%__MODULE__{datatype: datatype} = left_literal,
%__MODULE__{datatype: datatype} = right_literal
) do
case {left_literal.value, right_literal.value} do
{left_value, right_value} when left_value < right_value ->
:lt
{left_value, right_value} when left_value > right_value ->
:gt
_ ->
if equal_value?(left_literal, right_literal), do: :eq
end
end
def do_compare(_, _), do: nil
@impl Datatype
def update(literal, fun, opts \\ [])
def update(%Literal{literal: literal}, fun, opts), do: update(literal, fun, opts)
def update(%__MODULE__{} = literal, fun, _opts) do
literal
|> value()
|> fun.()
|> new(datatype: literal.datatype)
end
end