1use crate::named_node::{NamedNode, NamedNodeRef};
2use crate::vocab::{rdf, xsd};
3use oxilangtag::{LanguageTag, LanguageTagParseError};
4#[cfg(feature = "oxsdatatypes")]
5use oxsdatatypes::*;
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
8use std::borrow::Cow;
9use std::fmt;
10use std::fmt::Write;
11
12#[derive(Eq, PartialEq, Debug, Clone, Hash)]
37pub struct Literal(LiteralContent);
38
39#[derive(PartialEq, Eq, Debug, Clone, Hash)]
40enum LiteralContent {
41 String(String),
42 LanguageTaggedString {
43 value: String,
44 language: String,
45 },
46 #[cfg(feature = "rdf-12")]
47 DirectionalLanguageTaggedString {
48 value: String,
49 language: String,
50 direction: BaseDirection,
51 },
52 TypedLiteral {
53 value: String,
54 datatype: NamedNode,
55 },
56}
57
58impl Literal {
59 #[inline]
61 pub fn new_simple_literal(value: impl Into<String>) -> Self {
62 Self(LiteralContent::String(value.into()))
63 }
64
65 #[inline]
67 pub fn new_typed_literal(value: impl Into<String>, datatype: impl Into<NamedNode>) -> Self {
68 let value = value.into();
69 let datatype = datatype.into();
70 Self(if datatype == xsd::STRING {
71 LiteralContent::String(value)
72 } else {
73 LiteralContent::TypedLiteral { value, datatype }
74 })
75 }
76
77 #[inline]
79 pub fn new_language_tagged_literal(
80 value: impl Into<String>,
81 language: impl Into<String>,
82 ) -> Result<Self, LanguageTagParseError> {
83 let mut language = language.into();
84 language.make_ascii_lowercase();
85 Ok(Self::new_language_tagged_literal_unchecked(
86 value,
87 LanguageTag::parse(language)?.into_inner(),
88 ))
89 }
90
91 #[inline]
99 pub fn new_language_tagged_literal_unchecked(
100 value: impl Into<String>,
101 language: impl Into<String>,
102 ) -> Self {
103 Self(LiteralContent::LanguageTaggedString {
104 value: value.into(),
105 language: language.into(),
106 })
107 }
108
109 #[cfg(feature = "rdf-12")]
111 #[inline]
112 pub fn new_directional_language_tagged_literal(
113 value: impl Into<String>,
114 language: impl Into<String>,
115 direction: impl Into<BaseDirection>,
116 ) -> Result<Self, LanguageTagParseError> {
117 let mut language = language.into();
118 language.make_ascii_lowercase();
119 Ok(Self::new_directional_language_tagged_literal_unchecked(
120 value,
121 LanguageTag::parse(language)?.into_inner(),
122 direction,
123 ))
124 }
125
126 #[cfg(feature = "rdf-12")]
134 #[inline]
135 pub fn new_directional_language_tagged_literal_unchecked(
136 value: impl Into<String>,
137 language: impl Into<String>,
138 direction: impl Into<BaseDirection>,
139 ) -> Self {
140 Self(LiteralContent::DirectionalLanguageTaggedString {
141 value: value.into(),
142 language: language.into(),
143 direction: direction.into(),
144 })
145 }
146
147 #[inline]
149 pub fn value(&self) -> &str {
150 self.as_ref().value()
151 }
152
153 #[inline]
158 pub fn language(&self) -> Option<&str> {
159 self.as_ref().language()
160 }
161
162 #[cfg(feature = "rdf-12")]
166 #[inline]
167 pub fn direction(&self) -> Option<BaseDirection> {
168 self.as_ref().direction()
169 }
170
171 #[inline]
176 pub fn datatype(&self) -> NamedNodeRef<'_> {
177 self.as_ref().datatype()
178 }
179
180 #[inline]
185 #[deprecated(note = "Plain literal concept is removed in RDF 1.1", since = "0.3.0")]
186 pub fn is_plain(&self) -> bool {
187 #[expect(deprecated)]
188 self.as_ref().is_plain()
189 }
190
191 #[inline]
192 pub fn as_ref(&self) -> LiteralRef<'_> {
193 LiteralRef(match &self.0 {
194 LiteralContent::String(value) => LiteralRefContent::String(value),
195 LiteralContent::LanguageTaggedString { value, language } => {
196 LiteralRefContent::LanguageTaggedString { value, language }
197 }
198 #[cfg(feature = "rdf-12")]
199 LiteralContent::DirectionalLanguageTaggedString {
200 value,
201 language,
202 direction,
203 } => LiteralRefContent::DirectionalLanguageTaggedString {
204 value,
205 language,
206 direction: *direction,
207 },
208 LiteralContent::TypedLiteral { value, datatype } => LiteralRefContent::TypedLiteral {
209 value,
210 datatype: datatype.as_ref(),
211 },
212 })
213 }
214
215 #[cfg(feature = "rdf-12")]
217 #[inline]
218 pub fn destruct(
219 self,
220 ) -> (
221 String,
222 Option<NamedNode>,
223 Option<String>,
224 Option<BaseDirection>,
225 ) {
226 match self.0 {
227 LiteralContent::String(s) => (s, None, None, None),
228 LiteralContent::LanguageTaggedString { value, language } => {
229 (value, None, Some(language), None)
230 }
231 LiteralContent::DirectionalLanguageTaggedString {
232 value,
233 language,
234 direction,
235 } => (value, None, Some(language), Some(direction)),
236 LiteralContent::TypedLiteral { value, datatype } => (value, Some(datatype), None, None),
237 }
238 }
239
240 #[cfg(not(feature = "rdf-12"))]
242 #[inline]
243 pub fn destruct(self) -> (String, Option<NamedNode>, Option<String>) {
244 match self.0 {
245 LiteralContent::String(s) => (s, None, None),
246 LiteralContent::LanguageTaggedString { value, language } => {
247 (value, None, Some(language))
248 }
249 LiteralContent::TypedLiteral { value, datatype } => (value, Some(datatype), None),
250 }
251 }
252}
253
254impl fmt::Display for Literal {
255 #[inline]
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 self.as_ref().fmt(f)
258 }
259}
260
261impl<'a> From<&'a str> for Literal {
262 #[inline]
263 fn from(value: &'a str) -> Self {
264 Self(LiteralContent::String(value.into()))
265 }
266}
267
268impl From<String> for Literal {
269 #[inline]
270 fn from(value: String) -> Self {
271 Self(LiteralContent::String(value))
272 }
273}
274
275impl<'a> From<Cow<'a, str>> for Literal {
276 #[inline]
277 fn from(value: Cow<'a, str>) -> Self {
278 Self(LiteralContent::String(value.into()))
279 }
280}
281
282impl From<bool> for Literal {
283 #[inline]
284 fn from(value: bool) -> Self {
285 Self(LiteralContent::TypedLiteral {
286 value: value.to_string(),
287 datatype: xsd::BOOLEAN.into(),
288 })
289 }
290}
291
292impl From<i128> for Literal {
293 #[inline]
294 fn from(value: i128) -> Self {
295 Self(LiteralContent::TypedLiteral {
296 value: value.to_string(),
297 datatype: xsd::INTEGER.into(),
298 })
299 }
300}
301
302impl From<i64> for Literal {
303 #[inline]
304 fn from(value: i64) -> Self {
305 Self(LiteralContent::TypedLiteral {
306 value: value.to_string(),
307 datatype: xsd::INTEGER.into(),
308 })
309 }
310}
311
312impl From<i32> for Literal {
313 #[inline]
314 fn from(value: i32) -> Self {
315 Self(LiteralContent::TypedLiteral {
316 value: value.to_string(),
317 datatype: xsd::INTEGER.into(),
318 })
319 }
320}
321
322impl From<i16> for Literal {
323 #[inline]
324 fn from(value: i16) -> Self {
325 Self(LiteralContent::TypedLiteral {
326 value: value.to_string(),
327 datatype: xsd::INTEGER.into(),
328 })
329 }
330}
331
332impl From<u64> for Literal {
333 #[inline]
334 fn from(value: u64) -> Self {
335 Self(LiteralContent::TypedLiteral {
336 value: value.to_string(),
337 datatype: xsd::INTEGER.into(),
338 })
339 }
340}
341
342impl From<u32> for Literal {
343 #[inline]
344 fn from(value: u32) -> Self {
345 Self(LiteralContent::TypedLiteral {
346 value: value.to_string(),
347 datatype: xsd::INTEGER.into(),
348 })
349 }
350}
351
352impl From<u16> for Literal {
353 #[inline]
354 fn from(value: u16) -> Self {
355 Self(LiteralContent::TypedLiteral {
356 value: value.to_string(),
357 datatype: xsd::INTEGER.into(),
358 })
359 }
360}
361
362impl From<f32> for Literal {
363 #[inline]
364 fn from(value: f32) -> Self {
365 Self(LiteralContent::TypedLiteral {
366 value: if value == f32::INFINITY {
367 "INF".to_owned()
368 } else if value == f32::NEG_INFINITY {
369 "-INF".to_owned()
370 } else {
371 value.to_string()
372 },
373 datatype: xsd::FLOAT.into(),
374 })
375 }
376}
377
378impl From<f64> for Literal {
379 #[inline]
380 fn from(value: f64) -> Self {
381 Self(LiteralContent::TypedLiteral {
382 value: if value == f64::INFINITY {
383 "INF".to_owned()
384 } else if value == f64::NEG_INFINITY {
385 "-INF".to_owned()
386 } else {
387 value.to_string()
388 },
389 datatype: xsd::DOUBLE.into(),
390 })
391 }
392}
393
394#[cfg(feature = "oxsdatatypes")]
395impl From<Boolean> for Literal {
396 #[inline]
397 fn from(value: Boolean) -> Self {
398 Self::new_typed_literal(value.to_string(), xsd::BOOLEAN)
399 }
400}
401
402#[cfg(feature = "oxsdatatypes")]
403impl From<Float> for Literal {
404 #[inline]
405 fn from(value: Float) -> Self {
406 Self::new_typed_literal(value.to_string(), xsd::FLOAT)
407 }
408}
409
410#[cfg(feature = "oxsdatatypes")]
411impl From<Double> for Literal {
412 #[inline]
413 fn from(value: Double) -> Self {
414 Self::new_typed_literal(value.to_string(), xsd::DOUBLE)
415 }
416}
417
418#[cfg(feature = "oxsdatatypes")]
419impl From<Integer> for Literal {
420 #[inline]
421 fn from(value: Integer) -> Self {
422 Self::new_typed_literal(value.to_string(), xsd::INTEGER)
423 }
424}
425
426#[cfg(feature = "oxsdatatypes")]
427impl From<Decimal> for Literal {
428 #[inline]
429 fn from(value: Decimal) -> Self {
430 Self::new_typed_literal(value.to_string(), xsd::DECIMAL)
431 }
432}
433
434#[cfg(feature = "oxsdatatypes")]
435impl From<DateTime> for Literal {
436 #[inline]
437 fn from(value: DateTime) -> Self {
438 Self::new_typed_literal(value.to_string(), xsd::DATE_TIME)
439 }
440}
441
442#[cfg(feature = "oxsdatatypes")]
443impl From<Time> for Literal {
444 #[inline]
445 fn from(value: Time) -> Self {
446 Self::new_typed_literal(value.to_string(), xsd::TIME)
447 }
448}
449
450#[cfg(feature = "oxsdatatypes")]
451impl From<Date> for Literal {
452 #[inline]
453 fn from(value: Date) -> Self {
454 Self::new_typed_literal(value.to_string(), xsd::DATE)
455 }
456}
457
458#[cfg(feature = "oxsdatatypes")]
459impl From<GYearMonth> for Literal {
460 #[inline]
461 fn from(value: GYearMonth) -> Self {
462 Self::new_typed_literal(value.to_string(), xsd::G_YEAR_MONTH)
463 }
464}
465
466#[cfg(feature = "oxsdatatypes")]
467impl From<GYear> for Literal {
468 #[inline]
469 fn from(value: GYear) -> Self {
470 Self::new_typed_literal(value.to_string(), xsd::G_YEAR)
471 }
472}
473
474#[cfg(feature = "oxsdatatypes")]
475impl From<GMonthDay> for Literal {
476 #[inline]
477 fn from(value: GMonthDay) -> Self {
478 Self::new_typed_literal(value.to_string(), xsd::G_MONTH_DAY)
479 }
480}
481
482#[cfg(feature = "oxsdatatypes")]
483impl From<GMonth> for Literal {
484 #[inline]
485 fn from(value: GMonth) -> Self {
486 Self::new_typed_literal(value.to_string(), xsd::G_MONTH)
487 }
488}
489
490#[cfg(feature = "oxsdatatypes")]
491impl From<GDay> for Literal {
492 #[inline]
493 fn from(value: GDay) -> Self {
494 Self::new_typed_literal(value.to_string(), xsd::G_DAY)
495 }
496}
497
498#[cfg(feature = "oxsdatatypes")]
499impl From<Duration> for Literal {
500 #[inline]
501 fn from(value: Duration) -> Self {
502 Self::new_typed_literal(value.to_string(), xsd::DURATION)
503 }
504}
505
506#[cfg(feature = "oxsdatatypes")]
507impl From<YearMonthDuration> for Literal {
508 #[inline]
509 fn from(value: YearMonthDuration) -> Self {
510 Self::new_typed_literal(value.to_string(), xsd::YEAR_MONTH_DURATION)
511 }
512}
513
514#[cfg(feature = "oxsdatatypes")]
515impl From<DayTimeDuration> for Literal {
516 #[inline]
517 fn from(value: DayTimeDuration) -> Self {
518 Self::new_typed_literal(value.to_string(), xsd::DAY_TIME_DURATION)
519 }
520}
521
522#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
540pub struct LiteralRef<'a>(LiteralRefContent<'a>);
541
542#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
543enum LiteralRefContent<'a> {
544 String(&'a str),
545 LanguageTaggedString {
546 value: &'a str,
547 language: &'a str,
548 },
549 #[cfg(feature = "rdf-12")]
550 DirectionalLanguageTaggedString {
551 value: &'a str,
552 language: &'a str,
553 direction: BaseDirection,
554 },
555 TypedLiteral {
556 value: &'a str,
557 datatype: NamedNodeRef<'a>,
558 },
559}
560
561impl<'a> LiteralRef<'a> {
562 #[inline]
564 pub const fn new_simple_literal(value: &'a str) -> Self {
565 LiteralRef(LiteralRefContent::String(value))
566 }
567
568 #[inline]
570 pub fn new_typed_literal(value: &'a str, datatype: impl Into<NamedNodeRef<'a>>) -> Self {
571 let datatype = datatype.into();
572 LiteralRef(if datatype == xsd::STRING {
573 LiteralRefContent::String(value)
574 } else {
575 LiteralRefContent::TypedLiteral { value, datatype }
576 })
577 }
578
579 #[inline]
587 pub const fn new_language_tagged_literal_unchecked(value: &'a str, language: &'a str) -> Self {
588 LiteralRef(LiteralRefContent::LanguageTaggedString { value, language })
589 }
590
591 #[cfg(feature = "rdf-12")]
599 #[inline]
600 pub const fn new_directional_language_tagged_literal_unchecked(
601 value: &'a str,
602 language: &'a str,
603 direction: BaseDirection,
604 ) -> Self {
605 LiteralRef(LiteralRefContent::DirectionalLanguageTaggedString {
606 value,
607 language,
608 direction,
609 })
610 }
611
612 #[inline]
614 pub const fn value(self) -> &'a str {
615 match self.0 {
616 LiteralRefContent::String(value)
617 | LiteralRefContent::LanguageTaggedString { value, .. }
618 | LiteralRefContent::TypedLiteral { value, .. } => value,
619 #[cfg(feature = "rdf-12")]
620 LiteralRefContent::DirectionalLanguageTaggedString { value, .. } => value,
621 }
622 }
623
624 #[inline]
629 pub const fn language(self) -> Option<&'a str> {
630 match self.0 {
631 LiteralRefContent::LanguageTaggedString { language, .. } => Some(language),
632 #[cfg(feature = "rdf-12")]
633 LiteralRefContent::DirectionalLanguageTaggedString { language, .. } => Some(language),
634 _ => None,
635 }
636 }
637
638 #[cfg(feature = "rdf-12")]
642 #[inline]
643 pub const fn direction(self) -> Option<BaseDirection> {
644 match self.0 {
645 LiteralRefContent::DirectionalLanguageTaggedString { direction, .. } => Some(direction),
646 _ => None,
647 }
648 }
649
650 #[inline]
655 pub const fn datatype(self) -> NamedNodeRef<'a> {
656 match self.0 {
657 LiteralRefContent::String(_) => xsd::STRING,
658 LiteralRefContent::LanguageTaggedString { .. } => rdf::LANG_STRING,
659 #[cfg(feature = "rdf-12")]
660 LiteralRefContent::DirectionalLanguageTaggedString { .. } => rdf::DIR_LANG_STRING,
661 LiteralRefContent::TypedLiteral { datatype, .. } => datatype,
662 }
663 }
664
665 #[inline]
670 #[deprecated(note = "Plain literal concept is removed in RDF 1.1", since = "0.3.0")]
671 pub const fn is_plain(self) -> bool {
672 matches!(
673 self.0,
674 LiteralRefContent::String(_) | LiteralRefContent::LanguageTaggedString { .. }
675 )
676 }
677
678 #[inline]
679 pub fn into_owned(self) -> Literal {
680 Literal(match self.0 {
681 LiteralRefContent::String(value) => LiteralContent::String(value.to_owned()),
682 LiteralRefContent::LanguageTaggedString { value, language } => {
683 LiteralContent::LanguageTaggedString {
684 value: value.to_owned(),
685 language: language.to_owned(),
686 }
687 }
688 #[cfg(feature = "rdf-12")]
689 LiteralRefContent::DirectionalLanguageTaggedString {
690 value,
691 language,
692 direction,
693 } => LiteralContent::DirectionalLanguageTaggedString {
694 value: value.to_owned(),
695 language: language.to_owned(),
696 direction,
697 },
698 LiteralRefContent::TypedLiteral { value, datatype } => LiteralContent::TypedLiteral {
699 value: value.to_owned(),
700 datatype: datatype.into_owned(),
701 },
702 })
703 }
704
705 #[cfg(not(feature = "rdf-12"))]
707 #[inline]
708 #[deprecated(
709 note = "Use directly .value(), .datatype() and .language()",
710 since = "0.3.0"
711 )]
712 pub const fn destruct(self) -> (&'a str, Option<NamedNodeRef<'a>>, Option<&'a str>) {
713 match self.0 {
714 LiteralRefContent::String(s) => (s, None, None),
715 LiteralRefContent::LanguageTaggedString { value, language } => {
716 (value, None, Some(language))
717 }
718 LiteralRefContent::TypedLiteral { value, datatype } => (value, Some(datatype), None),
719 }
720 }
721}
722
723impl fmt::Display for LiteralRef<'_> {
724 #[inline]
725 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
726 match self.0 {
727 LiteralRefContent::String(value) => print_quoted_str(value, f),
728 LiteralRefContent::LanguageTaggedString { value, language } => {
729 print_quoted_str(value, f)?;
730 write!(f, "@{language}")
731 }
732 #[cfg(feature = "rdf-12")]
733 LiteralRefContent::DirectionalLanguageTaggedString {
734 value,
735 language,
736 direction,
737 } => {
738 print_quoted_str(value, f)?;
739 write!(f, "@{language}--{direction}")
740 }
741 LiteralRefContent::TypedLiteral { value, datatype } => {
742 print_quoted_str(value, f)?;
743 write!(f, "^^{datatype}")
744 }
745 }
746 }
747}
748
749impl<'a> From<&'a Literal> for LiteralRef<'a> {
750 #[inline]
751 fn from(node: &'a Literal) -> Self {
752 node.as_ref()
753 }
754}
755
756impl<'a> From<LiteralRef<'a>> for Literal {
757 #[inline]
758 fn from(node: LiteralRef<'a>) -> Self {
759 node.into_owned()
760 }
761}
762
763impl<'a> From<&'a str> for LiteralRef<'a> {
764 #[inline]
765 fn from(value: &'a str) -> Self {
766 LiteralRef(LiteralRefContent::String(value))
767 }
768}
769
770impl PartialEq<Literal> for LiteralRef<'_> {
771 #[inline]
772 fn eq(&self, other: &Literal) -> bool {
773 *self == other.as_ref()
774 }
775}
776
777impl PartialEq<LiteralRef<'_>> for Literal {
778 #[inline]
779 fn eq(&self, other: &LiteralRef<'_>) -> bool {
780 self.as_ref() == *other
781 }
782}
783
784#[inline]
785pub fn print_quoted_str(string: &str, f: &mut impl Write) -> fmt::Result {
786 f.write_char('"')?;
787 for c in string.chars() {
788 match c {
789 '\u{8}' => f.write_str("\\b"),
790 '\t' => f.write_str("\\t"),
791 '\n' => f.write_str("\\n"),
792 '\u{C}' => f.write_str("\\f"),
793 '\r' => f.write_str("\\r"),
794 '"' => f.write_str("\\\""),
795 '\\' => f.write_str("\\\\"),
796 '\0'..='\u{1F}' | '\u{7F}' | '\u{FFFE}' | '\u{FFFF}' => {
797 write!(f, "\\u{:04X}", u32::from(c))
798 }
799 _ => f.write_char(c),
800 }?;
801 }
802 f.write_char('"')
803}
804
805#[cfg(feature = "rdf-12")]
807#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
808#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
809pub enum BaseDirection {
810 #[cfg_attr(feature = "serde", serde(rename = "ltr"))]
812 Ltr,
813 #[cfg_attr(feature = "serde", serde(rename = "rtl"))]
815 Rtl,
816}
817
818#[cfg(feature = "rdf-12")]
819impl fmt::Display for BaseDirection {
820 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
821 f.write_str(match self {
822 Self::Ltr => "ltr",
823 Self::Rtl => "rtl",
824 })
825 }
826}
827
828#[cfg(feature = "serde")]
829impl Serialize for Literal {
830 #[inline]
831 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
832 self.as_ref().serialize(serializer)
833 }
834}
835
836#[cfg(feature = "serde")]
837impl Serialize for LiteralRef<'_> {
838 #[inline]
839 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
840 #[expect(clippy::struct_field_names)]
841 #[derive(Serialize)]
842 #[serde(rename = "Literal")]
843 struct Value<'a> {
844 value: &'a str,
845 #[serde(skip_serializing_if = "Option::is_none")]
846 language: Option<&'a str>,
847 #[cfg(feature = "rdf-12")]
848 #[serde(skip_serializing_if = "Option::is_none")]
849 direction: Option<BaseDirection>,
850 #[serde(skip_serializing_if = "Option::is_none")]
851 datatype: Option<&'a str>,
852 }
853 match self.0 {
854 LiteralRefContent::String(value) => Value {
855 value,
856 language: None,
857 #[cfg(feature = "rdf-12")]
858 direction: None,
859 datatype: None,
860 },
861 LiteralRefContent::LanguageTaggedString { value, language } => Value {
862 value,
863 language: Some(language),
864 #[cfg(feature = "rdf-12")]
865 direction: None,
866 datatype: None,
867 },
868 #[cfg(feature = "rdf-12")]
869 LiteralRefContent::DirectionalLanguageTaggedString {
870 value,
871 language,
872 direction,
873 } => Value {
874 value,
875 language: Some(language),
876 direction: Some(direction),
877 datatype: None,
878 },
879 LiteralRefContent::TypedLiteral { value, datatype } => Value {
880 value,
881 language: None,
882 #[cfg(feature = "rdf-12")]
883 direction: None,
884 datatype: Some(datatype.as_str()),
885 },
886 }
887 .serialize(serializer)
888 }
889}
890
891#[cfg(feature = "serde")]
892impl<'de> Deserialize<'de> for Literal {
893 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
894 where
895 D: Deserializer<'de>,
896 {
897 #[expect(clippy::struct_field_names)]
898 #[derive(Deserialize)]
899 #[serde(rename = "Literal")]
900 struct Value {
901 value: String,
902 language: Option<String>,
903 #[cfg(feature = "rdf-12")]
904 direction: Option<BaseDirection>,
905 datatype: Option<String>,
906 }
907 let Value {
908 value,
909 language,
910 #[cfg(feature = "rdf-12")]
911 direction,
912 datatype,
913 } = Value::deserialize(deserializer)?;
914 if let Some(language) = language {
915 #[cfg(feature = "rdf-12")]
916 if let Some(direction) = direction {
917 return Literal::new_directional_language_tagged_literal(
918 value, language, direction,
919 )
920 .map_err(de::Error::custom);
921 }
922 Literal::new_language_tagged_literal(value, language).map_err(de::Error::custom)
923 } else if let Some(datatype) = datatype {
924 Ok(Literal::new_typed_literal(
925 value,
926 NamedNode::new(datatype).map_err(de::Error::custom)?,
927 ))
928 } else {
929 Ok(Literal::new_simple_literal(value))
930 }
931 }
932}
933
934#[cfg(test)]
935mod tests {
936 use super::*;
937
938 #[test]
939 fn test_simple_literal_equality() {
940 assert_eq!(
941 Literal::new_simple_literal("foo"),
942 Literal::new_typed_literal("foo", xsd::STRING)
943 );
944 assert_eq!(
945 Literal::new_simple_literal("foo"),
946 LiteralRef::new_typed_literal("foo", xsd::STRING)
947 );
948 assert_eq!(
949 LiteralRef::new_simple_literal("foo"),
950 Literal::new_typed_literal("foo", xsd::STRING)
951 );
952 assert_eq!(
953 LiteralRef::new_simple_literal("foo"),
954 LiteralRef::new_typed_literal("foo", xsd::STRING)
955 );
956 }
957
958 #[test]
959 fn test_float_format() {
960 assert_eq!("INF", Literal::from(f32::INFINITY).value());
961 assert_eq!("INF", Literal::from(f64::INFINITY).value());
962 assert_eq!("-INF", Literal::from(f32::NEG_INFINITY).value());
963 assert_eq!("-INF", Literal::from(f64::NEG_INFINITY).value());
964 assert_eq!("NaN", Literal::from(f32::NAN).value());
965 assert_eq!("NaN", Literal::from(f64::NAN).value());
966 }
967
968 #[test]
969 #[cfg(feature = "serde")]
970 fn test_serde() {
971 let simple = Literal::new_simple_literal("foo");
973 let j = serde_json::to_string(&simple).unwrap();
974 assert_eq!("{\"value\":\"foo\"}", j);
975 let simple2: Literal = serde_json::from_str(&j).unwrap();
976 assert_eq!(simple, simple2);
977
978 let typed = Literal::new_typed_literal("foo", xsd::BOOLEAN);
980 let j = serde_json::to_string(&typed).unwrap();
981 assert_eq!(
982 "{\"value\":\"foo\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#boolean\"}",
983 j
984 );
985 let typed2: Literal = serde_json::from_str(&j).unwrap();
986 assert_eq!(typed, typed2);
987
988 let lt = Literal::new_language_tagged_literal("foo", "en").unwrap();
990 let j = serde_json::to_string(<).unwrap();
991 assert_eq!("{\"value\":\"foo\",\"language\":\"en\"}", j);
992 let lt2: Literal = serde_json::from_str(&j).unwrap();
993 assert_eq!(lt, lt2);
994 }
995}