rdf_structural_model/
literal.rs1use borrow_or_share::Bos;
4use iri_rfc3987::Iri;
5
6use crate::{
7 BaseDirection,
8 LanguageTag,
9 ModelError,
10};
11
12pub const RDF_LANG_STRING: &str = "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString";
14pub const RDF_DIR_LANG_STRING: &str = "http://www.w3.org/1999/02/22-rdf-syntax-ns#dirLangString";
16pub const XSD_STRING: &str = "http://www.w3.org/2001/XMLSchema#string";
18
19#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
21#[serde(
22 deny_unknown_fields,
23 bound(deserialize = "Text: serde::Deserialize<'de>")
24)]
25pub enum LiteralFacets<Text: AsRef<str> = String> {
26 None,
28 Language(LanguageTag<Text>),
30 Directional {
32 language: LanguageTag<Text>,
34 direction: BaseDirection,
36 },
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
44#[serde(
45 deny_unknown_fields,
46 bound(
47 serialize = "Text: serde::Serialize",
48 deserialize = "Text: serde::Deserialize<'de>, Iri<Text>: serde::Deserialize<'de>"
49 )
50)]
51pub struct LiteralMetadata<Text: Bos<str> + AsRef<str> = String> {
52 datatype: Iri<Text>,
53 facets: LiteralFacets<Text>,
54}
55
56impl<Text: Bos<str> + AsRef<str>> LiteralMetadata<Text> {
57 #[must_use]
60 pub const fn new(datatype: Iri<Text>, facets: LiteralFacets<Text>) -> Self {
61 Self { datatype, facets }
62 }
63
64 #[must_use]
66 pub const fn datatype(&self) -> &Iri<Text> {
67 &self.datatype
68 }
69
70 #[must_use]
72 pub const fn facets(&self) -> &LiteralFacets<Text> {
73 &self.facets
74 }
75
76 fn validate_standard(&self) -> Result<(), ModelError> {
77 let valid = match &self.facets {
78 LiteralFacets::None => {
79 self.datatype.as_str() != RDF_LANG_STRING
80 && self.datatype.as_str() != RDF_DIR_LANG_STRING
81 }
82 LiteralFacets::Language(_) => self.datatype.as_str() == RDF_LANG_STRING,
83 LiteralFacets::Directional { .. } => self.datatype.as_str() == RDF_DIR_LANG_STRING,
84 };
85 if !valid {
86 return Err(ModelError::InconsistentLiteralFacets);
87 }
88 Ok(())
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
98#[serde(
99 try_from = "LiteralInput<Text>",
100 bound(
101 serialize = "Text: serde::Serialize",
102 deserialize = "Text: serde::Deserialize<'de>, Iri<Text>: serde::Deserialize<'de>"
103 )
104)]
105pub struct Literal<Text: Bos<str> + AsRef<str> = String> {
106 lexical: Text,
107 metadata: LiteralMetadata<Text>,
108}
109
110impl<Text: Bos<str> + AsRef<str>> Literal<Text> {
111 pub fn try_new(lexical: Text, metadata: LiteralMetadata<Text>) -> Result<Self, ModelError> {
118 metadata.validate_standard()?;
119 Ok(Self { lexical, metadata })
120 }
121
122 #[must_use]
124 pub fn lexical(&self) -> &str {
125 self.lexical.as_ref()
126 }
127
128 #[must_use]
130 pub const fn metadata(&self) -> &LiteralMetadata<Text> {
131 &self.metadata
132 }
133}
134
135impl Literal<String> {
136 pub fn typed(lexical: impl Into<String>, datatype: Iri<String>) -> Result<Self, ModelError> {
142 Self::try_new(
143 lexical.into(),
144 LiteralMetadata::new(datatype, LiteralFacets::None),
145 )
146 }
147
148 #[must_use]
150 pub fn string(lexical: impl Into<String>) -> Self {
151 Self {
152 lexical: lexical.into(),
153 metadata: LiteralMetadata::new(Self::builtin(XSD_STRING), LiteralFacets::None),
154 }
155 }
156
157 #[must_use]
159 pub fn language_tagged(lexical: impl Into<String>, language: LanguageTag) -> Self {
160 Self {
161 lexical: lexical.into(),
162 metadata: LiteralMetadata::new(
163 Self::builtin(RDF_LANG_STRING),
164 LiteralFacets::Language(language),
165 ),
166 }
167 }
168
169 #[must_use]
171 pub fn directional(
172 lexical: impl Into<String>,
173 language: LanguageTag,
174 direction: BaseDirection,
175 ) -> Self {
176 Self {
177 lexical: lexical.into(),
178 metadata: LiteralMetadata::new(
179 Self::builtin(RDF_DIR_LANG_STRING),
180 LiteralFacets::Directional {
181 language,
182 direction,
183 },
184 ),
185 }
186 }
187
188 fn builtin(value: &'static str) -> Iri<String> {
189 Iri::parse(value)
190 .expect("built-in datatype IRI is valid")
191 .into()
192 }
193
194 #[must_use]
196 pub fn as_ref(&self) -> Literal<&str> {
197 Literal {
198 lexical: &self.lexical,
199 metadata: self.metadata.as_ref(),
200 }
201 }
202}
203
204impl LiteralMetadata<String> {
205 #[must_use]
207 pub fn as_ref(&self) -> LiteralMetadata<&str> {
208 let facets = match &self.facets {
209 LiteralFacets::None => LiteralFacets::None,
210 LiteralFacets::Language(tag) => LiteralFacets::Language(tag.as_ref()),
211 LiteralFacets::Directional {
212 language,
213 direction,
214 } => LiteralFacets::Directional {
215 language: language.as_ref(),
216 direction: *direction,
217 },
218 };
219 LiteralMetadata::new(self.datatype.borrow(), facets)
220 }
221}
222
223#[derive(serde::Deserialize)]
224#[serde(
225 rename = "Literal",
226 deny_unknown_fields,
227 bound(deserialize = "Text: serde::Deserialize<'de>, Iri<Text>: serde::Deserialize<'de>")
228)]
229struct LiteralInput<Text: Bos<str> + AsRef<str>> {
230 lexical: Text,
231 metadata: LiteralMetadata<Text>,
232}
233
234impl<Text: Bos<str> + AsRef<str>> TryFrom<LiteralInput<Text>> for Literal<Text> {
235 type Error = ModelError;
236 fn try_from(input: LiteralInput<Text>) -> Result<Self, Self::Error> {
237 Self::try_new(input.lexical, input.metadata)
238 }
239}