srdf/
literal.rs

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}