1use std::{fmt::Display, result};
2
3use iri_s::IriS;
4use rust_decimal::{prelude::ToPrimitive, Decimal};
5use serde::{Deserialize, Serialize, Serializer};
6
7use crate::{lang::Lang, numeric_literal::NumericLiteral};
8use prefixmap::{Deref, DerefError, IriRef};
9
10#[derive(PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Clone)]
11pub enum Literal {
12 StringLiteral {
13 lexical_form: String,
14 lang: Option<Lang>,
15 },
16 DatatypeLiteral {
17 lexical_form: String,
18 datatype: IriRef,
19 },
20 NumericLiteral(NumericLiteral),
21
22 #[serde(serialize_with = "serialize_boolean_literal")]
23 BooleanLiteral(bool),
24}
25
26impl Literal {
27 pub fn integer(n: isize) -> Literal {
28 Literal::NumericLiteral(NumericLiteral::integer(n))
29 }
30
31 pub fn double(d: f64) -> Literal {
32 Literal::NumericLiteral(NumericLiteral::double(d))
33 }
34
35 pub fn decimal(d: Decimal) -> Literal {
36 Literal::NumericLiteral(NumericLiteral::decimal(d))
37 }
38
39 pub fn lit_datatype(lexical_form: &str, datatype: &IriRef) -> Literal {
40 Literal::DatatypeLiteral {
41 lexical_form: lexical_form.to_owned(),
42 datatype: datatype.clone(),
43 }
44 }
45
46 pub fn boolean(b: bool) -> Literal {
47 Literal::BooleanLiteral(b)
48 }
49
50 pub fn str(lexical_form: &str) -> Literal {
51 Literal::StringLiteral {
52 lexical_form: lexical_form.to_owned(),
53 lang: None,
54 }
55 }
56
57 pub fn lang_str(lexical_form: &str, lang: Lang) -> Literal {
58 Literal::StringLiteral {
59 lexical_form: lexical_form.to_owned(),
60 lang: Some(lang),
61 }
62 }
63
64 pub fn lexical_form(&self) -> String {
65 match self {
66 Literal::StringLiteral { lexical_form, .. } => lexical_form.clone(),
67 Literal::DatatypeLiteral { lexical_form, .. } => lexical_form.clone(),
68 Literal::NumericLiteral(nl) => nl.lexical_form(),
69 Literal::BooleanLiteral(true) => "true".to_string(),
70 Literal::BooleanLiteral(false) => "false".to_string(),
71 }
72 }
73
74 pub fn datatype(&self) -> IriRef {
75 match self {
76 Literal::DatatypeLiteral { datatype, .. } => datatype.clone(),
77 Literal::StringLiteral {
78 lexical_form: _,
79 lang: None,
80 } => IriRef::iri(IriS::new_unchecked(
81 "http://www.w3.org/2001/XMLSchema#string",
82 )),
83 Literal::StringLiteral {
84 lexical_form: _,
85 lang: Some(_),
86 } => IriRef::iri(IriS::new_unchecked(
87 "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString",
88 )),
89 Literal::NumericLiteral(NumericLiteral::Integer(_)) => IriRef::iri(
90 IriS::new_unchecked("http://www.w3.org/2001/XMLSchema#integer"),
91 ),
92 Literal::NumericLiteral(NumericLiteral::Decimal(_)) => IriRef::iri(
93 IriS::new_unchecked("http://www.w3.org/2001/XMLSchema#decimal"),
94 ),
95 Literal::NumericLiteral(NumericLiteral::Double(_)) => IriRef::iri(IriS::new_unchecked(
96 "http://www.w3.org/2001/XMLSchema#double",
97 )),
98 Literal::BooleanLiteral(_) => IriRef::iri(IriS::new_unchecked(
99 "http://www.w3.org/2001/XMLSchema#boolean",
100 )),
101 }
102 }
103
104 pub fn numeric_value(&self) -> Option<NumericLiteral> {
105 match self {
106 Literal::NumericLiteral(nl) => Some(nl.clone()),
107 Literal::StringLiteral { .. }
108 | Literal::DatatypeLiteral { .. }
109 | Literal::BooleanLiteral(true)
110 | Literal::BooleanLiteral(false) => None,
111 }
112 }
113}
114
115impl Default for Literal {
116 fn default() -> Self {
117 Literal::StringLiteral {
118 lexical_form: String::default(),
119 lang: None,
120 }
121 }
122}
123
124impl Display for Literal {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 match self {
127 Literal::StringLiteral {
128 lexical_form,
129 lang: None,
130 } => write!(f, "\"{lexical_form}\""),
131 Literal::StringLiteral {
132 lexical_form,
133 lang: Some(lang),
134 } => write!(f, "\"{lexical_form}\"{lang}"),
135 Literal::DatatypeLiteral {
136 lexical_form,
137 datatype,
138 } => write!(f, "\"{lexical_form}\"^^<{datatype}>"),
139 Literal::NumericLiteral(n) => write!(f, "{}", n),
140 Literal::BooleanLiteral(true) => write!(f, "true"),
141 Literal::BooleanLiteral(false) => write!(f, "false"),
142 }
143 }
144}
145
146fn serialize_boolean_literal<S>(value: &bool, serializer: S) -> result::Result<S::Ok, S::Error>
147where
148 S: Serializer,
149{
150 match value {
151 false => serializer.serialize_str("false"),
152 true => serializer.serialize_str("true"),
153 }
154}
155
156impl Deref for Literal {
157 fn deref(
158 &self,
159 base: &Option<iri_s::IriS>,
160 prefixmap: &Option<prefixmap::PrefixMap>,
161 ) -> Result<Self, DerefError> {
162 match self {
163 Literal::NumericLiteral(n) => Ok(Literal::NumericLiteral(n.clone())),
164 Literal::BooleanLiteral(b) => Ok(Literal::BooleanLiteral(*b)),
165 Literal::StringLiteral { lexical_form, lang } => Ok(Literal::StringLiteral {
166 lexical_form: lexical_form.clone(),
167 lang: lang.clone(),
168 }),
169 Literal::DatatypeLiteral {
170 lexical_form,
171 datatype,
172 } => {
173 let dt = datatype.deref(base, prefixmap)?;
174 Ok(Literal::DatatypeLiteral {
175 lexical_form: lexical_form.clone(),
176 datatype: dt,
177 })
178 }
179 }
180 }
181}
182
183impl From<oxrdf::Literal> for Literal {
184 fn from(value: oxrdf::Literal) -> Self {
185 match value.destruct() {
186 (s, None, None) => Literal::str(&s),
187 (s, None, Some(language)) => Literal::lang_str(&s, Lang::new_unchecked(&language)),
188 (value, Some(dtype), None) => {
189 let datatype = IriRef::iri(IriS::new_unchecked(dtype.as_str()));
190 Literal::lit_datatype(&value, &datatype)
191 }
192 _ => todo!(),
193 }
194 }
195}
196
197impl From<Literal> for oxrdf::Literal {
198 fn from(value: Literal) -> Self {
199 match value {
200 Literal::StringLiteral { lexical_form, lang } => match lang {
201 Some(lang) => oxrdf::Literal::new_language_tagged_literal_unchecked(
202 lexical_form,
203 lang.to_string(),
204 ),
205 None => lexical_form.into(),
206 },
207 Literal::DatatypeLiteral {
208 lexical_form,
209 datatype,
210 } => match datatype.get_iri() {
211 Ok(datatype) => oxrdf::Literal::new_typed_literal(
212 lexical_form,
213 datatype.as_named_node().to_owned(),
214 ),
215 Err(_) => lexical_form.into(),
216 },
217 Literal::NumericLiteral(number) => match number {
218 NumericLiteral::Integer(int) => (int as i64).into(),
219 NumericLiteral::Decimal(decimal) => match decimal.to_f64() {
220 Some(decimal) => decimal.into(),
221 None => decimal.to_string().into(),
222 },
223 NumericLiteral::Double(double) => double.into(),
224 },
225 Literal::BooleanLiteral(bool) => bool.into(),
226 }
227 }
228}