1use crate::{BaseDirection, CowTerm, DEFAULT_GRAPH_URN, Datatype, DefaultGraph, Term, TermKind};
4use alloc::{
5 borrow::Cow,
6 string::{String, ToString},
7};
8use xsd::{DecimalValue, PrimitiveValue, Value, primitive::Boolean};
9
10type Language = String; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub enum HeapTerm {
20 Iri(String),
22
23 BNode(String),
25
26 String(String),
29
30 TaggedString(String, Language, Option<BaseDirection>),
33
34 TypedValue(Value),
37
38 TypedLiteral(String, Datatype),
42
43 DefaultGraph,
45}
46
47impl HeapTerm {
48 pub fn iri(value: impl Into<String>) -> Self {
49 Self::Iri(value.into())
50 }
51
52 pub fn bnode(id: impl Into<String>) -> Self {
53 Self::BNode(id.into())
54 }
55
56 pub fn string(value: impl Into<String>) -> Self {
57 Self::String(value.into())
58 }
59
60 pub fn tagged_string(value: impl Into<String>, tag: impl Into<Language>) -> Self {
61 Self::TaggedString(value.into(), tag.into(), None)
62 }
63
64 pub fn tagged_string_with_dir(
65 value: impl Into<String>,
66 tag: impl Into<Language>,
67 dir: impl Into<BaseDirection>,
68 ) -> Self {
69 Self::TaggedString(value.into(), tag.into(), Some(dir.into()))
70 }
71
72 pub fn typed_value(value: impl Into<Value>) -> Self {
76 Self::TypedValue(value.into())
77 }
78
79 pub fn typed_literal(literal: impl Into<String>, datatype: impl Into<Datatype>) -> Self {
92 Self::TypedLiteral(literal.into(), datatype.into())
93 }
94
95 pub fn kind(&self) -> TermKind {
96 match self {
97 Self::DefaultGraph => TermKind::DefaultGraph,
98 Self::Iri(_) => TermKind::Iri,
99 Self::BNode(_) => TermKind::BNode,
100 Self::String(_)
101 | Self::TaggedString(_, _, _)
102 | Self::TypedValue(_)
103 | Self::TypedLiteral(_, _) => TermKind::Literal,
104 }
105 }
106
107 pub fn value_str(&self) -> Cow<'_, str> {
108 Cow::Borrowed(match self {
109 Self::DefaultGraph => DEFAULT_GRAPH_URN,
110 Self::Iri(s) => s.as_str(),
111 Self::BNode(s) => s.as_str(),
112 Self::String(s) | Self::TaggedString(s, _, _) | Self::TypedLiteral(s, _) => s.as_str(),
113 Self::TypedValue(Value::Primitive(PrimitiveValue::Boolean(Boolean::FALSE))) => "false",
114 Self::TypedValue(Value::Primitive(PrimitiveValue::Boolean(Boolean::TRUE))) => "true",
115 Self::TypedValue(Value::Primitive(
116 PrimitiveValue::String(s) | PrimitiveValue::AnyUri(s),
117 )) => s.as_str(),
118 Self::TypedValue(v) => return Cow::Owned(v.to_string()),
119 })
120 }
121
122 #[cfg(feature = "serde")]
123 pub fn to_json(&self) -> Option<serde_json::Value> {
124 Some(self.clone().into_json())
125 }
126
127 #[cfg(feature = "serde")]
128 pub fn into_json(self) -> serde_json::Value {
132 use serde_json::{Value as JsonValue, json};
133 match self {
134 Self::DefaultGraph => JsonValue::String(DEFAULT_GRAPH_URN.into()),
135 Self::Iri(str) => JsonValue::String(str),
136 Self::BNode(id) => JsonValue::String(id),
137 Self::String(val) => json!({ "@value": val }),
138 Self::TaggedString(val, lang, _) => json!({ "@value": val, "@language": lang }), Self::TypedLiteral(val, r#type) => {
140 json!({ "@value": val, "@type": r#type.to_string() })
141 },
142 Self::TypedValue(Value::Primitive(val)) => match val {
143 PrimitiveValue::String(val) => json!({ "@value": val }),
144 PrimitiveValue::Boolean(val) => val.into_json(),
145 PrimitiveValue::Double(val) => val.into_json(),
146 val => {
147 let r#type = val.r#type();
148 json!({ "@value": val.into_json(), "@type": r#type.curie() })
149 },
150 },
151 Self::TypedValue(Value::Decimal(val)) => match val {
152 DecimalValue::Integer(val) => json!({ "@value": val, "@type": "xsd:integer" }),
153 val => {
154 let r#type = val.r#type();
155 json!({ "@value": val.into_json(), "@type": r#type.curie() })
156 },
157 },
158 }
159 }
160
161 #[cfg(feature = "bson")]
162 pub fn to_bson(&self) -> Option<bson::Bson> {
163 Some(self.clone().into_bson())
164 }
165
166 #[cfg(feature = "bson")]
167 pub fn into_bson(self) -> bson::Bson {
169 use bson::{Bson, Document};
170 match self {
171 Self::DefaultGraph => Bson::String(DEFAULT_GRAPH_URN.into()),
172 Self::Iri(str) => Bson::String(str),
173 Self::BNode(id) => Bson::String(alloc::format!("_:{}", id)),
174 Self::String(val) => {
175 let mut doc = Document::new();
176 doc.insert("@value", val);
177 Bson::Document(doc)
178 },
179 Self::TaggedString(val, lang, _) => {
180 let mut doc = Document::new();
181 doc.insert("@value", val);
182 doc.insert("@language", lang);
183 Bson::Document(doc)
184 },
185 Self::TypedLiteral(lit, r#type) => {
186 let mut doc = Document::new();
187 doc.insert("@value", lit);
188 doc.insert("@type", r#type.to_string());
189 Bson::Document(doc)
190 },
191 Self::TypedValue(val) => {
192 let r#type = val.r#type();
193 let mut doc = Document::new();
194 doc.insert("@value", val.into_bson());
195 doc.insert("@type", r#type.curie());
196 Bson::Document(doc)
197 },
198 }
199 }
200}
201
202impl Term for HeapTerm {
203 fn kind(&self) -> TermKind {
204 self.kind()
205 }
206
207 fn value_str(&self) -> Cow<'_, str> {
208 self.value_str()
209 }
210}
211
212impl Term for &HeapTerm {
213 fn kind(&self) -> TermKind {
214 (*self).kind()
215 }
216
217 fn value_str(&self) -> Cow<'_, str> {
218 (*self).value_str()
219 }
220}
221
222impl From<DefaultGraph> for HeapTerm {
223 fn from(_: DefaultGraph) -> Self {
224 Self::DefaultGraph
225 }
226}
227
228impl From<DefaultGraph> for &HeapTerm {
229 fn from(_: DefaultGraph) -> Self {
230 &HeapTerm::DefaultGraph
231 }
232}
233
234impl<T> From<&T> for HeapTerm
235where
236 T: Clone + Into<Self>,
237{
238 fn from(t: &T) -> Self {
239 t.clone().into()
240 }
241}
242
243impl From<&dyn Term> for HeapTerm {
244 fn from(term: &dyn Term) -> Self {
245 match term.kind() {
246 TermKind::DefaultGraph => Self::DefaultGraph,
247 TermKind::Iri => Self::iri(term.value_str()),
248 TermKind::BNode => Self::bnode(term.value_str()),
249 TermKind::Literal => Self::string(term.value_str()), }
251 }
252}
253
254impl From<CowTerm<'_>> for HeapTerm {
255 fn from(input: CowTerm<'_>) -> Self {
256 match input {
257 CowTerm::DefaultGraph => Self::DefaultGraph,
258 CowTerm::Iri(s) => Self::Iri(s.to_string()),
259 CowTerm::BNode(s) => Self::BNode(s.to_string()),
260 CowTerm::String(s) => Self::String(s.to_string()),
261 CowTerm::TaggedString(s, lang, dir) => {
262 Self::TaggedString(s.to_string(), lang.clone(), dir.clone())
263 },
264 CowTerm::TypedValue(v) => Self::TypedValue(v.clone()),
265 CowTerm::TypedLiteral(s, dt) => Self::TypedLiteral(s.to_string(), dt.clone()),
266 }
267 }
268}
269
270impl From<&str> for HeapTerm {
271 fn from(value: &str) -> Self {
272 Self::string(value)
273 }
274}
275
276impl From<String> for HeapTerm {
277 fn from(value: String) -> Self {
278 Self::String(value)
279 }
280}
281
282impl From<Boolean> for HeapTerm {
283 fn from(value: Boolean) -> Self {
284 Self::TypedValue(value.into())
285 }
286}
287
288impl From<(String, Language)> for HeapTerm {
289 fn from((literal, language): (String, Language)) -> Self {
290 Self::TaggedString(literal, language, None)
291 }
292}
293
294impl From<(String, Datatype)> for HeapTerm {
300 fn from((literal, datatype): (String, Datatype)) -> Self {
301 let result = match &datatype {
302 Datatype::Xsd(t) => xsd::parse(&literal, t),
303 _ => return Self::TypedLiteral(literal, datatype),
304 };
305 match result {
306 Ok(value) => Self::TypedValue(value),
307 Err(_) => Self::TypedLiteral(literal, datatype),
308 }
309 }
310}
311
312impl From<(String, Option<Datatype>, Option<Language>)> for HeapTerm {
313 fn from((literal, datatype, language): (String, Option<Datatype>, Option<Language>)) -> Self {
314 match (language, datatype) {
315 (None, None) => Self::string(literal),
316 (None, Some(datatype)) => Self::from((literal, datatype)),
317 (Some(language), None) => Self::tagged_string(literal, language),
318 (Some(language), Some(_)) => Self::tagged_string(literal, language), }
320 }
321}
322
323#[cfg(feature = "bson")]
324impl From<HeapTerm> for bson::Bson {
325 fn from(input: HeapTerm) -> Self {
326 input.into_bson()
327 }
328}
329
330#[cfg(feature = "serde")]
331impl TryFrom<serde_json::Value> for HeapTerm {
332 type Error = ();
333
334 fn try_from(input: serde_json::Value) -> Result<Self, Self::Error> {
335 use serde_json::Value;
336 Ok(match input {
337 Value::Null => return Err(()), Value::Bool(b) => Self::TypedValue(b.into()),
339 Value::Number(n) => Self::TypedValue(n.as_f64().unwrap().into()), Value::String(s) if s.starts_with("_:") => Self::BNode(s.clone()),
341 Value::String(s) => Self::Iri(s.clone()),
342 Value::Array(_) => return Err(()), Value::Object(mut input) => {
344 let value = input.remove("@value").ok_or(())?;
345 let r#type = input
346 .remove("@type")
347 .and_then(|s| s.as_str().map(|s| s.parse::<Datatype>().ok()))
348 .flatten();
349 let language = input
350 .remove("@language")
351 .and_then(|s| s.as_str().map(|s| s.to_string()));
352 match (r#type, language) {
353 (None, None) => Self::String(value.to_string()),
354 (None, Some(lang)) => Self::TaggedString(value.to_string(), lang.into(), None),
355 (Some(r#type), None) => Self::TypedLiteral(value.to_string(), r#type),
356 (Some(_), Some(_)) => return Err(()), }
358 },
359 })
360 }
361}
362
363#[cfg(feature = "serde")]
364impl TryFrom<&serde_json::Value> for HeapTerm {
365 type Error = ();
366
367 fn try_from(input: &serde_json::Value) -> Result<Self, Self::Error> {
368 input.clone().try_into()
369 }
370}