1use crate::named_node::{NamedNode, NamedNodeRef};
2use crate::vocab::{rdf, xsd};
3use oxilangtag::{LanguageTag, LanguageTagParseError};
4#[cfg(feature = "oxsdatatypes")]
5use oxsdatatypes::*;
6use std::borrow::Cow;
7use std::fmt;
8use std::fmt::Write;
9
10#[derive(Eq, PartialEq, Debug, Clone, Hash)]
35pub struct Literal(LiteralContent);
36
37#[derive(PartialEq, Eq, Debug, Clone, Hash)]
38enum LiteralContent {
39 String(String),
40 LanguageTaggedString { value: String, language: String },
41 TypedLiteral { value: String, datatype: NamedNode },
42}
43
44impl Literal {
45 #[inline]
47 pub fn new_simple_literal(value: impl Into<String>) -> Self {
48 Self(LiteralContent::String(value.into()))
49 }
50
51 #[inline]
53 pub fn new_typed_literal(value: impl Into<String>, datatype: impl Into<NamedNode>) -> Self {
54 let value = value.into();
55 let datatype = datatype.into();
56 Self(if datatype == xsd::STRING {
57 LiteralContent::String(value)
58 } else {
59 LiteralContent::TypedLiteral { value, datatype }
60 })
61 }
62
63 #[inline]
65 pub fn new_language_tagged_literal(
66 value: impl Into<String>,
67 language: impl Into<String>,
68 ) -> Result<Self, LanguageTagParseError> {
69 let mut language = language.into();
70 language.make_ascii_lowercase();
71 Ok(Self::new_language_tagged_literal_unchecked(
72 value,
73 LanguageTag::parse(language)?.into_inner(),
74 ))
75 }
76
77 #[inline]
85 pub fn new_language_tagged_literal_unchecked(
86 value: impl Into<String>,
87 language: impl Into<String>,
88 ) -> Self {
89 Self(LiteralContent::LanguageTaggedString {
90 value: value.into(),
91 language: language.into(),
92 })
93 }
94
95 #[inline]
97 pub fn value(&self) -> &str {
98 self.as_ref().value()
99 }
100
101 #[inline]
106 pub fn language(&self) -> Option<&str> {
107 self.as_ref().language()
108 }
109
110 #[inline]
115 pub fn datatype(&self) -> NamedNodeRef<'_> {
116 self.as_ref().datatype()
117 }
118
119 #[inline]
124 pub fn is_plain(&self) -> bool {
125 self.as_ref().is_plain()
126 }
127
128 #[inline]
129 pub fn as_ref(&self) -> LiteralRef<'_> {
130 LiteralRef(match &self.0 {
131 LiteralContent::String(value) => LiteralRefContent::String(value),
132 LiteralContent::LanguageTaggedString { value, language } => {
133 LiteralRefContent::LanguageTaggedString { value, language }
134 }
135 LiteralContent::TypedLiteral { value, datatype } => LiteralRefContent::TypedLiteral {
136 value,
137 datatype: datatype.as_ref(),
138 },
139 })
140 }
141
142 #[inline]
144 pub fn destruct(self) -> (String, Option<NamedNode>, Option<String>) {
145 match self.0 {
146 LiteralContent::String(s) => (s, None, None),
147 LiteralContent::LanguageTaggedString { value, language } => {
148 (value, None, Some(language))
149 }
150 LiteralContent::TypedLiteral { value, datatype } => (value, Some(datatype), None),
151 }
152 }
153}
154
155impl fmt::Display for Literal {
156 #[inline]
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 self.as_ref().fmt(f)
159 }
160}
161
162impl<'a> From<&'a str> for Literal {
163 #[inline]
164 fn from(value: &'a str) -> Self {
165 Self(LiteralContent::String(value.into()))
166 }
167}
168
169impl From<String> for Literal {
170 #[inline]
171 fn from(value: String) -> Self {
172 Self(LiteralContent::String(value))
173 }
174}
175
176impl<'a> From<Cow<'a, str>> for Literal {
177 #[inline]
178 fn from(value: Cow<'a, str>) -> Self {
179 Self(LiteralContent::String(value.into()))
180 }
181}
182
183impl From<bool> for Literal {
184 #[inline]
185 fn from(value: bool) -> Self {
186 Self(LiteralContent::TypedLiteral {
187 value: value.to_string(),
188 datatype: xsd::BOOLEAN.into(),
189 })
190 }
191}
192
193impl From<i128> for Literal {
194 #[inline]
195 fn from(value: i128) -> Self {
196 Self(LiteralContent::TypedLiteral {
197 value: value.to_string(),
198 datatype: xsd::INTEGER.into(),
199 })
200 }
201}
202
203impl From<i64> for Literal {
204 #[inline]
205 fn from(value: i64) -> Self {
206 Self(LiteralContent::TypedLiteral {
207 value: value.to_string(),
208 datatype: xsd::INTEGER.into(),
209 })
210 }
211}
212
213impl From<i32> for Literal {
214 #[inline]
215 fn from(value: i32) -> Self {
216 Self(LiteralContent::TypedLiteral {
217 value: value.to_string(),
218 datatype: xsd::INTEGER.into(),
219 })
220 }
221}
222
223impl From<i16> for Literal {
224 #[inline]
225 fn from(value: i16) -> Self {
226 Self(LiteralContent::TypedLiteral {
227 value: value.to_string(),
228 datatype: xsd::INTEGER.into(),
229 })
230 }
231}
232
233impl From<u64> for Literal {
234 #[inline]
235 fn from(value: u64) -> Self {
236 Self(LiteralContent::TypedLiteral {
237 value: value.to_string(),
238 datatype: xsd::INTEGER.into(),
239 })
240 }
241}
242
243impl From<u32> for Literal {
244 #[inline]
245 fn from(value: u32) -> Self {
246 Self(LiteralContent::TypedLiteral {
247 value: value.to_string(),
248 datatype: xsd::INTEGER.into(),
249 })
250 }
251}
252
253impl From<u16> for Literal {
254 #[inline]
255 fn from(value: u16) -> Self {
256 Self(LiteralContent::TypedLiteral {
257 value: value.to_string(),
258 datatype: xsd::INTEGER.into(),
259 })
260 }
261}
262
263impl From<f32> for Literal {
264 #[inline]
265 fn from(value: f32) -> Self {
266 Self(LiteralContent::TypedLiteral {
267 value: if value == f32::INFINITY {
268 "INF".to_owned()
269 } else if value == f32::NEG_INFINITY {
270 "-INF".to_owned()
271 } else {
272 value.to_string()
273 },
274 datatype: xsd::FLOAT.into(),
275 })
276 }
277}
278
279impl From<f64> for Literal {
280 #[inline]
281 fn from(value: f64) -> Self {
282 Self(LiteralContent::TypedLiteral {
283 value: if value == f64::INFINITY {
284 "INF".to_owned()
285 } else if value == f64::NEG_INFINITY {
286 "-INF".to_owned()
287 } else {
288 value.to_string()
289 },
290 datatype: xsd::DOUBLE.into(),
291 })
292 }
293}
294
295#[cfg(feature = "oxsdatatypes")]
296impl From<Boolean> for Literal {
297 #[inline]
298 fn from(value: Boolean) -> Self {
299 Self::new_typed_literal(value.to_string(), xsd::BOOLEAN)
300 }
301}
302
303#[cfg(feature = "oxsdatatypes")]
304impl From<Float> for Literal {
305 #[inline]
306 fn from(value: Float) -> Self {
307 Self::new_typed_literal(value.to_string(), xsd::FLOAT)
308 }
309}
310
311#[cfg(feature = "oxsdatatypes")]
312impl From<Double> for Literal {
313 #[inline]
314 fn from(value: Double) -> Self {
315 Self::new_typed_literal(value.to_string(), xsd::DOUBLE)
316 }
317}
318
319#[cfg(feature = "oxsdatatypes")]
320impl From<Integer> for Literal {
321 #[inline]
322 fn from(value: Integer) -> Self {
323 Self::new_typed_literal(value.to_string(), xsd::INTEGER)
324 }
325}
326
327#[cfg(feature = "oxsdatatypes")]
328impl From<Decimal> for Literal {
329 #[inline]
330 fn from(value: Decimal) -> Self {
331 Self::new_typed_literal(value.to_string(), xsd::DECIMAL)
332 }
333}
334
335#[cfg(feature = "oxsdatatypes")]
336impl From<DateTime> for Literal {
337 #[inline]
338 fn from(value: DateTime) -> Self {
339 Self::new_typed_literal(value.to_string(), xsd::DATE_TIME)
340 }
341}
342
343#[cfg(feature = "oxsdatatypes")]
344impl From<Time> for Literal {
345 #[inline]
346 fn from(value: Time) -> Self {
347 Self::new_typed_literal(value.to_string(), xsd::TIME)
348 }
349}
350
351#[cfg(feature = "oxsdatatypes")]
352impl From<Date> for Literal {
353 #[inline]
354 fn from(value: Date) -> Self {
355 Self::new_typed_literal(value.to_string(), xsd::DATE)
356 }
357}
358
359#[cfg(feature = "oxsdatatypes")]
360impl From<GYearMonth> for Literal {
361 #[inline]
362 fn from(value: GYearMonth) -> Self {
363 Self::new_typed_literal(value.to_string(), xsd::G_YEAR_MONTH)
364 }
365}
366
367#[cfg(feature = "oxsdatatypes")]
368impl From<GYear> for Literal {
369 #[inline]
370 fn from(value: GYear) -> Self {
371 Self::new_typed_literal(value.to_string(), xsd::G_YEAR)
372 }
373}
374
375#[cfg(feature = "oxsdatatypes")]
376impl From<GMonthDay> for Literal {
377 #[inline]
378 fn from(value: GMonthDay) -> Self {
379 Self::new_typed_literal(value.to_string(), xsd::G_MONTH_DAY)
380 }
381}
382
383#[cfg(feature = "oxsdatatypes")]
384impl From<GMonth> for Literal {
385 #[inline]
386 fn from(value: GMonth) -> Self {
387 Self::new_typed_literal(value.to_string(), xsd::G_MONTH)
388 }
389}
390
391#[cfg(feature = "oxsdatatypes")]
392impl From<GDay> for Literal {
393 #[inline]
394 fn from(value: GDay) -> Self {
395 Self::new_typed_literal(value.to_string(), xsd::G_DAY)
396 }
397}
398
399#[cfg(feature = "oxsdatatypes")]
400impl From<Duration> for Literal {
401 #[inline]
402 fn from(value: Duration) -> Self {
403 Self::new_typed_literal(value.to_string(), xsd::DURATION)
404 }
405}
406
407#[cfg(feature = "oxsdatatypes")]
408impl From<YearMonthDuration> for Literal {
409 #[inline]
410 fn from(value: YearMonthDuration) -> Self {
411 Self::new_typed_literal(value.to_string(), xsd::YEAR_MONTH_DURATION)
412 }
413}
414
415#[cfg(feature = "oxsdatatypes")]
416impl From<DayTimeDuration> for Literal {
417 #[inline]
418 fn from(value: DayTimeDuration) -> Self {
419 Self::new_typed_literal(value.to_string(), xsd::DAY_TIME_DURATION)
420 }
421}
422
423#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
441pub struct LiteralRef<'a>(LiteralRefContent<'a>);
442
443#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
444enum LiteralRefContent<'a> {
445 String(&'a str),
446 LanguageTaggedString {
447 value: &'a str,
448 language: &'a str,
449 },
450 TypedLiteral {
451 value: &'a str,
452 datatype: NamedNodeRef<'a>,
453 },
454}
455
456impl<'a> LiteralRef<'a> {
457 #[inline]
459 pub const fn new_simple_literal(value: &'a str) -> Self {
460 LiteralRef(LiteralRefContent::String(value))
461 }
462
463 #[inline]
465 pub fn new_typed_literal(value: &'a str, datatype: impl Into<NamedNodeRef<'a>>) -> Self {
466 let datatype = datatype.into();
467 LiteralRef(if datatype == xsd::STRING {
468 LiteralRefContent::String(value)
469 } else {
470 LiteralRefContent::TypedLiteral { value, datatype }
471 })
472 }
473
474 #[inline]
482 pub const fn new_language_tagged_literal_unchecked(value: &'a str, language: &'a str) -> Self {
483 LiteralRef(LiteralRefContent::LanguageTaggedString { value, language })
484 }
485
486 #[inline]
488 pub const fn value(self) -> &'a str {
489 match self.0 {
490 LiteralRefContent::String(value)
491 | LiteralRefContent::LanguageTaggedString { value, .. }
492 | LiteralRefContent::TypedLiteral { value, .. } => value,
493 }
494 }
495
496 #[inline]
501 pub const fn language(self) -> Option<&'a str> {
502 match self.0 {
503 LiteralRefContent::LanguageTaggedString { language, .. } => Some(language),
504 _ => None,
505 }
506 }
507
508 #[inline]
513 pub const fn datatype(self) -> NamedNodeRef<'a> {
514 match self.0 {
515 LiteralRefContent::String(_) => xsd::STRING,
516 LiteralRefContent::LanguageTaggedString { .. } => rdf::LANG_STRING,
517 LiteralRefContent::TypedLiteral { datatype, .. } => datatype,
518 }
519 }
520
521 #[inline]
526 pub const fn is_plain(self) -> bool {
527 matches!(
528 self.0,
529 LiteralRefContent::String(_) | LiteralRefContent::LanguageTaggedString { .. }
530 )
531 }
532
533 #[inline]
534 pub fn into_owned(self) -> Literal {
535 Literal(match self.0 {
536 LiteralRefContent::String(value) => LiteralContent::String(value.to_owned()),
537 LiteralRefContent::LanguageTaggedString { value, language } => {
538 LiteralContent::LanguageTaggedString {
539 value: value.to_owned(),
540 language: language.to_owned(),
541 }
542 }
543 LiteralRefContent::TypedLiteral { value, datatype } => LiteralContent::TypedLiteral {
544 value: value.to_owned(),
545 datatype: datatype.into_owned(),
546 },
547 })
548 }
549
550 #[inline]
552 pub const fn destruct(self) -> (&'a str, Option<NamedNodeRef<'a>>, Option<&'a str>) {
553 match self.0 {
554 LiteralRefContent::String(s) => (s, None, None),
555 LiteralRefContent::LanguageTaggedString { value, language } => {
556 (value, None, Some(language))
557 }
558 LiteralRefContent::TypedLiteral { value, datatype } => (value, Some(datatype), None),
559 }
560 }
561}
562
563impl fmt::Display for LiteralRef<'_> {
564 #[inline]
565 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
566 match self.0 {
567 LiteralRefContent::String(value) => print_quoted_str(value, f),
568 LiteralRefContent::LanguageTaggedString { value, language } => {
569 print_quoted_str(value, f)?;
570 write!(f, "@{language}")
571 }
572 LiteralRefContent::TypedLiteral { value, datatype } => {
573 print_quoted_str(value, f)?;
574 write!(f, "^^{datatype}")
575 }
576 }
577 }
578}
579
580impl<'a> From<&'a Literal> for LiteralRef<'a> {
581 #[inline]
582 fn from(node: &'a Literal) -> Self {
583 node.as_ref()
584 }
585}
586
587impl<'a> From<LiteralRef<'a>> for Literal {
588 #[inline]
589 fn from(node: LiteralRef<'a>) -> Self {
590 node.into_owned()
591 }
592}
593
594impl<'a> From<&'a str> for LiteralRef<'a> {
595 #[inline]
596 fn from(value: &'a str) -> Self {
597 LiteralRef(LiteralRefContent::String(value))
598 }
599}
600
601impl PartialEq<Literal> for LiteralRef<'_> {
602 #[inline]
603 fn eq(&self, other: &Literal) -> bool {
604 *self == other.as_ref()
605 }
606}
607
608impl PartialEq<LiteralRef<'_>> for Literal {
609 #[inline]
610 fn eq(&self, other: &LiteralRef<'_>) -> bool {
611 self.as_ref() == *other
612 }
613}
614
615#[inline]
616pub fn print_quoted_str(string: &str, f: &mut impl Write) -> fmt::Result {
617 f.write_char('"')?;
618 for c in string.chars() {
619 match c {
620 '\u{08}' => f.write_str("\\b"),
621 '\t' => f.write_str("\\t"),
622 '\n' => f.write_str("\\n"),
623 '\u{0C}' => f.write_str("\\f"),
624 '\r' => f.write_str("\\r"),
625 '"' => f.write_str("\\\""),
626 '\\' => f.write_str("\\\\"),
627 '\0'..='\u{1F}' | '\u{7F}' => write!(f, "\\u{:04X}", u32::from(c)),
628 _ => f.write_char(c),
629 }?;
630 }
631 f.write_char('"')
632}
633
634#[cfg(test)]
635#[allow(clippy::panic_in_result_fn)]
636mod tests {
637 use super::*;
638
639 #[test]
640 fn test_simple_literal_equality() {
641 assert_eq!(
642 Literal::new_simple_literal("foo"),
643 Literal::new_typed_literal("foo", xsd::STRING)
644 );
645 assert_eq!(
646 Literal::new_simple_literal("foo"),
647 LiteralRef::new_typed_literal("foo", xsd::STRING)
648 );
649 assert_eq!(
650 LiteralRef::new_simple_literal("foo"),
651 Literal::new_typed_literal("foo", xsd::STRING)
652 );
653 assert_eq!(
654 LiteralRef::new_simple_literal("foo"),
655 LiteralRef::new_typed_literal("foo", xsd::STRING)
656 );
657 }
658
659 #[test]
660 fn test_float_format() {
661 assert_eq!("INF", Literal::from(f32::INFINITY).value());
662 assert_eq!("INF", Literal::from(f64::INFINITY).value());
663 assert_eq!("-INF", Literal::from(f32::NEG_INFINITY).value());
664 assert_eq!("-INF", Literal::from(f64::NEG_INFINITY).value());
665 assert_eq!("NaN", Literal::from(f32::NAN).value());
666 assert_eq!("NaN", Literal::from(f64::NAN).value());
667 }
668}