1use crate::model::{BlankNode, GraphName, Literal, NamedNode, Object, Quad, Subject, Triple};
41use crate::{OxirsError, Result};
42use oxiri::Iri;
43
44pub struct DataFactory;
51
52impl DataFactory {
53 pub fn named_node(iri: impl Into<String>) -> Result<NamedNode> {
65 NamedNode::new(iri)
66 }
67
68 pub fn blank_node() -> BlankNode {
79 BlankNode::new_unique()
80 }
81
82 pub fn blank_node_with_id(id: impl Into<String>) -> BlankNode {
93 let s = id.into();
94 BlankNode::new(s.clone()).unwrap_or_else(|_| BlankNode::new_unique())
95 }
96
97 pub fn literal(value: impl Into<String>) -> Literal {
107 Literal::new(value)
108 }
109
110 pub fn typed_literal(value: impl Into<String>, datatype: NamedNode) -> Literal {
118 Literal::new_typed(value, datatype)
119 }
120
121 pub fn language_literal(value: impl Into<String>, lang: impl Into<String>) -> Result<Literal> {
131 let lang_str = lang.into();
132 Self::validate_lang_tag(&lang_str)?;
133 Literal::new_lang(value, lang_str)
134 }
135
136 pub fn triple(subject: Subject, predicate: NamedNode, object: Object) -> Triple {
149 Triple::new(subject, predicate, object)
150 }
151
152 pub fn quad(subject: Subject, predicate: NamedNode, object: Object, graph: GraphName) -> Quad {
168 Quad::new(subject, predicate, object, graph)
169 }
170
171 pub fn default_graph() -> GraphName {
175 GraphName::DefaultGraph
176 }
177
178 pub fn named_graph(iri: impl Into<String>) -> Result<GraphName> {
182 let nn = NamedNode::new(iri)?;
183 Ok(GraphName::NamedNode(nn))
184 }
185
186 pub fn validate_iri(iri: &str) -> Result<()> {
196 Iri::parse(iri.to_owned())
197 .map(|_| ())
198 .map_err(|e| OxirsError::Parse(format!("Invalid IRI '{iri}': {e}")))
199 }
200
201 pub fn validate_lang_tag(lang: &str) -> Result<()> {
211 if lang.is_empty() {
212 return Err(OxirsError::Parse(
213 "Language tag must not be empty".to_string(),
214 ));
215 }
216 for part in lang.split('-') {
219 if part.is_empty() {
220 return Err(OxirsError::Parse(format!(
221 "Invalid language tag '{lang}': empty subtag"
222 )));
223 }
224 if !part.chars().all(|c| c.is_ascii_alphanumeric()) {
226 return Err(OxirsError::Parse(format!(
227 "Invalid language tag '{lang}': subtag '{part}' contains non-alphanumeric characters"
228 )));
229 }
230 }
231 let primary = lang.split('-').next().unwrap_or(lang);
233 if !primary.chars().all(|c| c.is_ascii_alphabetic()) {
234 return Err(OxirsError::Parse(format!(
235 "Invalid language tag '{lang}': primary subtag must be alphabetic"
236 )));
237 }
238 Ok(())
239 }
240}
241
242pub mod xsd_types {
248 use crate::model::NamedNode;
249
250 const XSD: &str = "http://www.w3.org/2001/XMLSchema#";
251
252 pub fn string() -> NamedNode {
254 NamedNode::new_unchecked(format!("{XSD}string"))
255 }
256 pub fn integer() -> NamedNode {
258 NamedNode::new_unchecked(format!("{XSD}integer"))
259 }
260 pub fn float() -> NamedNode {
262 NamedNode::new_unchecked(format!("{XSD}float"))
263 }
264 pub fn double() -> NamedNode {
266 NamedNode::new_unchecked(format!("{XSD}double"))
267 }
268 pub fn boolean() -> NamedNode {
270 NamedNode::new_unchecked(format!("{XSD}boolean"))
271 }
272 pub fn date_time() -> NamedNode {
274 NamedNode::new_unchecked(format!("{XSD}dateTime"))
275 }
276 pub fn date() -> NamedNode {
278 NamedNode::new_unchecked(format!("{XSD}date"))
279 }
280 pub fn decimal() -> NamedNode {
282 NamedNode::new_unchecked(format!("{XSD}decimal"))
283 }
284 pub fn long() -> NamedNode {
286 NamedNode::new_unchecked(format!("{XSD}long"))
287 }
288 pub fn int() -> NamedNode {
290 NamedNode::new_unchecked(format!("{XSD}int"))
291 }
292 pub fn short() -> NamedNode {
294 NamedNode::new_unchecked(format!("{XSD}short"))
295 }
296 pub fn byte() -> NamedNode {
298 NamedNode::new_unchecked(format!("{XSD}byte"))
299 }
300 pub fn unsigned_long() -> NamedNode {
302 NamedNode::new_unchecked(format!("{XSD}unsignedLong"))
303 }
304 pub fn unsigned_int() -> NamedNode {
306 NamedNode::new_unchecked(format!("{XSD}unsignedInt"))
307 }
308 pub fn non_negative_integer() -> NamedNode {
310 NamedNode::new_unchecked(format!("{XSD}nonNegativeInteger"))
311 }
312 pub fn positive_integer() -> NamedNode {
314 NamedNode::new_unchecked(format!("{XSD}positiveInteger"))
315 }
316 pub fn any_uri() -> NamedNode {
318 NamedNode::new_unchecked(format!("{XSD}anyURI"))
319 }
320 pub fn base64_binary() -> NamedNode {
322 NamedNode::new_unchecked(format!("{XSD}base64Binary"))
323 }
324 pub fn hex_binary() -> NamedNode {
326 NamedNode::new_unchecked(format!("{XSD}hexBinary"))
327 }
328 pub fn g_year() -> NamedNode {
330 NamedNode::new_unchecked(format!("{XSD}gYear"))
331 }
332 pub fn duration() -> NamedNode {
334 NamedNode::new_unchecked(format!("{XSD}duration"))
335 }
336 pub fn time() -> NamedNode {
338 NamedNode::new_unchecked(format!("{XSD}time"))
339 }
340 pub fn normalized_string() -> NamedNode {
342 NamedNode::new_unchecked(format!("{XSD}normalizedString"))
343 }
344 pub fn token() -> NamedNode {
346 NamedNode::new_unchecked(format!("{XSD}token"))
347 }
348}
349
350pub mod vocab {
356 use crate::model::NamedNode;
357
358 pub mod rdf {
360 use super::NamedNode;
361 const NS: &str = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
362 pub fn r#type() -> NamedNode {
364 NamedNode::new_unchecked(format!("{NS}type"))
365 }
366 pub fn subject() -> NamedNode {
368 NamedNode::new_unchecked(format!("{NS}subject"))
369 }
370 pub fn predicate() -> NamedNode {
372 NamedNode::new_unchecked(format!("{NS}predicate"))
373 }
374 pub fn object() -> NamedNode {
376 NamedNode::new_unchecked(format!("{NS}object"))
377 }
378 pub fn property() -> NamedNode {
380 NamedNode::new_unchecked(format!("{NS}Property"))
381 }
382 pub fn statement() -> NamedNode {
384 NamedNode::new_unchecked(format!("{NS}Statement"))
385 }
386 pub fn first() -> NamedNode {
388 NamedNode::new_unchecked(format!("{NS}first"))
389 }
390 pub fn rest() -> NamedNode {
392 NamedNode::new_unchecked(format!("{NS}rest"))
393 }
394 pub fn nil() -> NamedNode {
396 NamedNode::new_unchecked(format!("{NS}nil"))
397 }
398 pub fn list() -> NamedNode {
400 NamedNode::new_unchecked(format!("{NS}List"))
401 }
402 pub fn bag() -> NamedNode {
404 NamedNode::new_unchecked(format!("{NS}Bag"))
405 }
406 pub fn seq() -> NamedNode {
408 NamedNode::new_unchecked(format!("{NS}Seq"))
409 }
410 pub fn alt() -> NamedNode {
412 NamedNode::new_unchecked(format!("{NS}Alt"))
413 }
414 pub fn value() -> NamedNode {
416 NamedNode::new_unchecked(format!("{NS}value"))
417 }
418 pub fn lang_string() -> NamedNode {
420 NamedNode::new_unchecked(format!("{NS}langString"))
421 }
422 pub const NAMESPACE: &str = NS;
424 }
425
426 pub mod rdfs {
428 use super::NamedNode;
429 const NS: &str = "http://www.w3.org/2000/01/rdf-schema#";
430 pub fn label() -> NamedNode {
432 NamedNode::new_unchecked(format!("{NS}label"))
433 }
434 pub fn comment() -> NamedNode {
436 NamedNode::new_unchecked(format!("{NS}comment"))
437 }
438 pub fn sub_class_of() -> NamedNode {
440 NamedNode::new_unchecked(format!("{NS}subClassOf"))
441 }
442 pub fn sub_property_of() -> NamedNode {
444 NamedNode::new_unchecked(format!("{NS}subPropertyOf"))
445 }
446 pub fn domain() -> NamedNode {
448 NamedNode::new_unchecked(format!("{NS}domain"))
449 }
450 pub fn range() -> NamedNode {
452 NamedNode::new_unchecked(format!("{NS}range"))
453 }
454 pub fn class() -> NamedNode {
456 NamedNode::new_unchecked(format!("{NS}Class"))
457 }
458 pub fn resource() -> NamedNode {
460 NamedNode::new_unchecked(format!("{NS}Resource"))
461 }
462 pub fn literal() -> NamedNode {
464 NamedNode::new_unchecked(format!("{NS}Literal"))
465 }
466 pub fn datatype() -> NamedNode {
468 NamedNode::new_unchecked(format!("{NS}Datatype"))
469 }
470 pub fn is_defined_by() -> NamedNode {
472 NamedNode::new_unchecked(format!("{NS}isDefinedBy"))
473 }
474 pub fn see_also() -> NamedNode {
476 NamedNode::new_unchecked(format!("{NS}seeAlso"))
477 }
478 pub fn member() -> NamedNode {
480 NamedNode::new_unchecked(format!("{NS}member"))
481 }
482 pub fn container() -> NamedNode {
484 NamedNode::new_unchecked(format!("{NS}Container"))
485 }
486 pub const NAMESPACE: &str = NS;
488 }
489
490 pub mod owl {
492 use super::NamedNode;
493 const NS: &str = "http://www.w3.org/2002/07/owl#";
494 pub fn class() -> NamedNode {
496 NamedNode::new_unchecked(format!("{NS}Class"))
497 }
498 pub fn object_property() -> NamedNode {
500 NamedNode::new_unchecked(format!("{NS}ObjectProperty"))
501 }
502 pub fn datatype_property() -> NamedNode {
504 NamedNode::new_unchecked(format!("{NS}DatatypeProperty"))
505 }
506 pub fn annotation_property() -> NamedNode {
508 NamedNode::new_unchecked(format!("{NS}AnnotationProperty"))
509 }
510 pub fn thing() -> NamedNode {
512 NamedNode::new_unchecked(format!("{NS}Thing"))
513 }
514 pub fn nothing() -> NamedNode {
516 NamedNode::new_unchecked(format!("{NS}Nothing"))
517 }
518 pub fn same_as() -> NamedNode {
520 NamedNode::new_unchecked(format!("{NS}sameAs"))
521 }
522 pub fn equivalent_class() -> NamedNode {
524 NamedNode::new_unchecked(format!("{NS}equivalentClass"))
525 }
526 pub fn equivalent_property() -> NamedNode {
528 NamedNode::new_unchecked(format!("{NS}equivalentProperty"))
529 }
530 pub fn inverse_of() -> NamedNode {
532 NamedNode::new_unchecked(format!("{NS}inverseOf"))
533 }
534 pub fn disjoint_with() -> NamedNode {
536 NamedNode::new_unchecked(format!("{NS}disjointWith"))
537 }
538 pub fn functional_property() -> NamedNode {
540 NamedNode::new_unchecked(format!("{NS}FunctionalProperty"))
541 }
542 pub fn ontology() -> NamedNode {
544 NamedNode::new_unchecked(format!("{NS}Ontology"))
545 }
546 pub const NAMESPACE: &str = NS;
548 }
549
550 pub mod xsd {
552 use super::NamedNode;
553 const NS: &str = "http://www.w3.org/2001/XMLSchema#";
554 pub fn string() -> NamedNode {
556 NamedNode::new_unchecked(format!("{NS}string"))
557 }
558 pub fn integer() -> NamedNode {
560 NamedNode::new_unchecked(format!("{NS}integer"))
561 }
562 pub fn boolean() -> NamedNode {
564 NamedNode::new_unchecked(format!("{NS}boolean"))
565 }
566 pub fn double() -> NamedNode {
568 NamedNode::new_unchecked(format!("{NS}double"))
569 }
570 pub fn date_time() -> NamedNode {
572 NamedNode::new_unchecked(format!("{NS}dateTime"))
573 }
574 pub const NAMESPACE: &str = NS;
576 }
577
578 pub mod foaf {
580 use super::NamedNode;
581 const NS: &str = "http://xmlns.com/foaf/0.1/";
582 pub fn name() -> NamedNode {
584 NamedNode::new_unchecked(format!("{NS}name"))
585 }
586 pub fn person() -> NamedNode {
588 NamedNode::new_unchecked(format!("{NS}Person"))
589 }
590 pub fn knows() -> NamedNode {
592 NamedNode::new_unchecked(format!("{NS}knows"))
593 }
594 pub fn mbox() -> NamedNode {
596 NamedNode::new_unchecked(format!("{NS}mbox"))
597 }
598 pub fn homepage() -> NamedNode {
600 NamedNode::new_unchecked(format!("{NS}homepage"))
601 }
602 pub const NAMESPACE: &str = NS;
604 }
605}
606
607#[cfg(test)]
610mod tests {
611 use super::*;
612
613 #[test]
616 fn test_named_node_valid_http() {
617 let n = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
618 assert_eq!(n.as_str(), "http://example.org/s");
619 }
620
621 #[test]
622 fn test_named_node_valid_https() {
623 let n =
624 DataFactory::named_node("https://schema.org/Person").expect("valid IRI for named node");
625 assert_eq!(n.as_str(), "https://schema.org/Person");
626 }
627
628 #[test]
629 fn test_named_node_valid_urn() {
630 let n = DataFactory::named_node("urn:example:foo").expect("valid IRI for named node");
631 assert_eq!(n.as_str(), "urn:example:foo");
632 }
633
634 #[test]
635 fn test_named_node_invalid_returns_err() {
636 assert!(DataFactory::named_node("not an IRI").is_err());
637 }
638
639 #[test]
640 fn test_named_node_empty_string_is_err() {
641 assert!(DataFactory::named_node("").is_err());
642 }
643
644 #[test]
645 fn test_named_node_with_fragment() {
646 let n = DataFactory::named_node("http://example.org/ont#Class")
647 .expect("valid IRI for named node");
648 assert!(n.as_str().ends_with("#Class"));
649 }
650
651 #[test]
652 fn test_named_node_with_query() {
653 let n =
654 DataFactory::named_node("http://example.org/q?a=1").expect("valid IRI for named node");
655 assert!(n.as_str().contains("a=1"));
656 }
657
658 #[test]
661 fn test_blank_node_auto_id_is_nonempty() {
662 let b = DataFactory::blank_node();
663 assert!(!b.as_str().is_empty());
664 }
665
666 #[test]
667 fn test_blank_node_auto_ids_are_unique() {
668 let b1 = DataFactory::blank_node();
669 let b2 = DataFactory::blank_node();
670 assert_ne!(b1.as_str(), b2.as_str());
671 }
672
673 #[test]
674 fn test_blank_node_with_id() {
675 let b = DataFactory::blank_node_with_id("my-node");
676 assert_eq!(b.as_str(), "my-node");
677 }
678
679 #[test]
680 fn test_blank_node_with_id_alpha() {
681 let b = DataFactory::blank_node_with_id("abc");
682 assert_eq!(b.as_str(), "abc");
683 }
684
685 #[test]
686 fn test_blank_node_with_id_alphanumeric() {
687 let b = DataFactory::blank_node_with_id("node1");
688 assert_eq!(b.as_str(), "node1");
689 }
690
691 #[test]
692 fn test_blank_node_with_invalid_id_still_returns_node() {
693 let b = DataFactory::blank_node_with_id(" spaces ");
695 assert!(!b.as_str().is_empty());
696 }
697
698 #[test]
701 fn test_literal_plain_value() {
702 let l = DataFactory::literal("hello");
703 assert_eq!(l.value(), "hello");
704 }
705
706 #[test]
707 fn test_literal_plain_no_language() {
708 let l = DataFactory::literal("hello");
709 assert_eq!(l.language(), None);
710 }
711
712 #[test]
713 fn test_literal_plain_datatype_is_xsd_string() {
714 let l = DataFactory::literal("hello");
715 assert!(l.datatype().as_str().contains("string"));
716 }
717
718 #[test]
719 fn test_literal_empty_string() {
720 let l = DataFactory::literal("");
721 assert_eq!(l.value(), "");
722 }
723
724 #[test]
727 fn test_typed_literal_integer() {
728 let l = DataFactory::typed_literal("42", xsd_types::integer());
729 assert_eq!(l.value(), "42");
730 assert!(l.datatype().as_str().ends_with("integer"));
731 }
732
733 #[test]
734 fn test_typed_literal_boolean() {
735 let l = DataFactory::typed_literal("true", xsd_types::boolean());
736 assert_eq!(l.value(), "true");
737 assert!(l.datatype().as_str().ends_with("boolean"));
738 }
739
740 #[test]
741 fn test_typed_literal_double() {
742 let l = DataFactory::typed_literal("3.14", xsd_types::double());
743 assert!(l.datatype().as_str().ends_with("double"));
744 }
745
746 #[test]
747 fn test_typed_literal_date_time() {
748 let l = DataFactory::typed_literal("2026-02-24T00:00:00Z", xsd_types::date_time());
749 assert!(l.datatype().as_str().ends_with("dateTime"));
750 }
751
752 #[test]
753 fn test_typed_literal_custom_datatype() {
754 let dt =
755 DataFactory::named_node("http://example.org/myType").expect("valid IRI for named node");
756 let l = DataFactory::typed_literal("custom", dt);
757 assert!(l.datatype().as_str().contains("myType"));
758 }
759
760 #[test]
763 fn test_language_literal_value_and_lang() {
764 let l = DataFactory::language_literal("Bonjour", "fr").expect("valid language literal");
765 assert_eq!(l.value(), "Bonjour");
766 assert_eq!(l.language(), Some("fr"));
767 }
768
769 #[test]
770 fn test_language_literal_en() {
771 let l = DataFactory::language_literal("Hello", "en").expect("valid language literal");
772 assert_eq!(l.language(), Some("en"));
773 }
774
775 #[test]
776 fn test_language_literal_zh_hans() {
777 let l = DataFactory::language_literal("你好", "zh-Hans").expect("valid language literal");
778 assert_eq!(l.language(), Some("zh-Hans"));
782 }
783
784 #[test]
785 fn test_language_literal_en_us() {
786 let l = DataFactory::language_literal("Color", "en-US").expect("valid language literal");
787 assert_eq!(l.language(), Some("en-US"));
788 }
789
790 #[test]
791 fn test_language_literal_empty_lang_is_err() {
792 assert!(DataFactory::language_literal("hello", "").is_err());
793 }
794
795 #[test]
796 fn test_language_literal_invalid_lang_is_err() {
797 assert!(DataFactory::language_literal("hello", "en US").is_err());
799 }
800
801 #[test]
804 fn test_triple_subject_predicate_object() {
805 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
806 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
807 let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
808 let t = DataFactory::triple(s.into(), p.clone(), o.into());
809 let text = format!("{t}");
811 assert!(text.contains("http://example.org/s"));
812 }
813
814 #[test]
815 fn test_triple_with_literal_object() {
816 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
817 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
818 let o: Object = DataFactory::literal("hello").into();
819 let t = DataFactory::triple(s.into(), p, o);
820 let text = format!("{t}");
821 assert!(text.contains("hello"));
822 }
823
824 #[test]
825 fn test_triple_with_blank_node_subject() {
826 let s: Subject = DataFactory::blank_node().into();
827 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
828 let o: Object = DataFactory::literal("val").into();
829 let _t = DataFactory::triple(s, p, o);
830 }
831
832 #[test]
835 fn test_quad_default_graph() {
836 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
837 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
838 let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
839 let g = DataFactory::default_graph();
840 let q = DataFactory::quad(s.into(), p, o.into(), g);
841 let text = format!("{q}");
842 assert!(text.contains("http://example.org/s"));
843 }
844
845 #[test]
846 fn test_quad_named_graph() {
847 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
848 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
849 let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
850 let g = DataFactory::named_graph("http://example.org/graph1")
851 .expect("construction should succeed");
852 let q = DataFactory::quad(s.into(), p, o.into(), g);
853 let text = format!("{q}");
854 assert!(text.contains("graph1"));
855 }
856
857 #[test]
858 fn test_quad_named_graph_invalid_iri_is_err() {
859 assert!(DataFactory::named_graph("not an IRI").is_err());
860 }
861
862 #[test]
863 fn test_default_graph_is_default_graph_variant() {
864 let g = DataFactory::default_graph();
865 assert!(matches!(g, GraphName::DefaultGraph));
866 }
867
868 #[test]
871 fn test_validate_iri_http_ok() {
872 assert!(DataFactory::validate_iri("http://example.org/").is_ok());
873 }
874
875 #[test]
876 fn test_validate_iri_https_ok() {
877 assert!(DataFactory::validate_iri("https://example.org/path").is_ok());
878 }
879
880 #[test]
881 fn test_validate_iri_urn_ok() {
882 assert!(DataFactory::validate_iri("urn:isbn:0451450523").is_ok());
883 }
884
885 #[test]
886 fn test_validate_iri_bare_word_is_err() {
887 assert!(DataFactory::validate_iri("hello").is_err());
888 }
889
890 #[test]
891 fn test_validate_iri_empty_is_err() {
892 assert!(DataFactory::validate_iri("").is_err());
893 }
894
895 #[test]
896 fn test_validate_iri_space_is_err() {
897 assert!(DataFactory::validate_iri("http://example.org/hello world").is_err());
898 }
899
900 #[test]
903 fn test_validate_lang_tag_en_ok() {
904 assert!(DataFactory::validate_lang_tag("en").is_ok());
905 }
906
907 #[test]
908 fn test_validate_lang_tag_en_us_ok() {
909 assert!(DataFactory::validate_lang_tag("en-US").is_ok());
910 }
911
912 #[test]
913 fn test_validate_lang_tag_zh_hans_cn_ok() {
914 assert!(DataFactory::validate_lang_tag("zh-Hans-CN").is_ok());
915 }
916
917 #[test]
918 fn test_validate_lang_tag_empty_is_err() {
919 assert!(DataFactory::validate_lang_tag("").is_err());
920 }
921
922 #[test]
923 fn test_validate_lang_tag_space_is_err() {
924 assert!(DataFactory::validate_lang_tag("en US").is_err());
925 }
926
927 #[test]
928 fn test_validate_lang_tag_double_dash_is_err() {
929 assert!(DataFactory::validate_lang_tag("en--US").is_err());
930 }
931
932 #[test]
933 fn test_validate_lang_tag_numeric_primary_is_err() {
934 assert!(DataFactory::validate_lang_tag("123").is_err());
936 }
937
938 #[test]
941 fn test_xsd_string_iri() {
942 assert_eq!(
943 xsd_types::string().as_str(),
944 "http://www.w3.org/2001/XMLSchema#string"
945 );
946 }
947
948 #[test]
949 fn test_xsd_integer_iri() {
950 assert_eq!(
951 xsd_types::integer().as_str(),
952 "http://www.w3.org/2001/XMLSchema#integer"
953 );
954 }
955
956 #[test]
957 fn test_xsd_float_iri() {
958 assert!(xsd_types::float().as_str().ends_with("float"));
959 }
960
961 #[test]
962 fn test_xsd_double_iri() {
963 assert!(xsd_types::double().as_str().ends_with("double"));
964 }
965
966 #[test]
967 fn test_xsd_boolean_iri() {
968 assert!(xsd_types::boolean().as_str().ends_with("boolean"));
969 }
970
971 #[test]
972 fn test_xsd_date_time_iri() {
973 assert!(xsd_types::date_time().as_str().ends_with("dateTime"));
974 }
975
976 #[test]
977 fn test_xsd_date_iri() {
978 assert!(xsd_types::date().as_str().ends_with("date"));
979 }
980
981 #[test]
982 fn test_xsd_decimal_iri() {
983 assert!(xsd_types::decimal().as_str().ends_with("decimal"));
984 }
985
986 #[test]
987 fn test_xsd_long_iri() {
988 assert!(xsd_types::long().as_str().ends_with("long"));
989 }
990
991 #[test]
992 fn test_xsd_int_iri() {
993 assert!(xsd_types::int().as_str().ends_with("#int"));
994 }
995
996 #[test]
997 fn test_xsd_any_uri_iri() {
998 assert!(xsd_types::any_uri().as_str().ends_with("anyURI"));
999 }
1000
1001 #[test]
1002 fn test_xsd_base64_binary_iri() {
1003 assert!(xsd_types::base64_binary()
1004 .as_str()
1005 .ends_with("base64Binary"));
1006 }
1007
1008 #[test]
1009 fn test_xsd_hex_binary_iri() {
1010 assert!(xsd_types::hex_binary().as_str().ends_with("hexBinary"));
1011 }
1012
1013 #[test]
1016 fn test_vocab_rdf_type() {
1017 assert_eq!(
1018 vocab::rdf::r#type().as_str(),
1019 "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
1020 );
1021 }
1022
1023 #[test]
1024 fn test_vocab_rdf_first_last() {
1025 assert!(vocab::rdf::first().as_str().ends_with("first"));
1026 assert!(vocab::rdf::rest().as_str().ends_with("rest"));
1027 assert!(vocab::rdf::nil().as_str().ends_with("nil"));
1028 }
1029
1030 #[test]
1031 fn test_vocab_rdfs_label() {
1032 assert_eq!(
1033 vocab::rdfs::label().as_str(),
1034 "http://www.w3.org/2000/01/rdf-schema#label"
1035 );
1036 }
1037
1038 #[test]
1039 fn test_vocab_rdfs_comment() {
1040 assert!(vocab::rdfs::comment().as_str().ends_with("comment"));
1041 }
1042
1043 #[test]
1044 fn test_vocab_rdfs_sub_class_of() {
1045 assert!(vocab::rdfs::sub_class_of().as_str().ends_with("subClassOf"));
1046 }
1047
1048 #[test]
1049 fn test_vocab_rdfs_domain_range() {
1050 assert!(vocab::rdfs::domain().as_str().ends_with("domain"));
1051 assert!(vocab::rdfs::range().as_str().ends_with("range"));
1052 }
1053
1054 #[test]
1055 fn test_vocab_owl_class() {
1056 assert_eq!(
1057 vocab::owl::class().as_str(),
1058 "http://www.w3.org/2002/07/owl#Class"
1059 );
1060 }
1061
1062 #[test]
1063 fn test_vocab_owl_same_as() {
1064 assert!(vocab::owl::same_as().as_str().ends_with("sameAs"));
1065 }
1066
1067 #[test]
1068 fn test_vocab_owl_thing_nothing() {
1069 assert!(vocab::owl::thing().as_str().ends_with("Thing"));
1070 assert!(vocab::owl::nothing().as_str().ends_with("Nothing"));
1071 }
1072
1073 #[test]
1074 fn test_vocab_owl_object_property() {
1075 assert!(vocab::owl::object_property()
1076 .as_str()
1077 .ends_with("ObjectProperty"));
1078 }
1079
1080 #[test]
1081 fn test_vocab_xsd_string() {
1082 assert!(vocab::xsd::string().as_str().ends_with("string"));
1083 }
1084
1085 #[test]
1086 fn test_vocab_foaf_name() {
1087 assert_eq!(
1088 vocab::foaf::name().as_str(),
1089 "http://xmlns.com/foaf/0.1/name"
1090 );
1091 }
1092
1093 #[test]
1094 fn test_vocab_foaf_person() {
1095 assert!(vocab::foaf::person().as_str().ends_with("Person"));
1096 }
1097
1098 #[test]
1099 fn test_vocab_foaf_knows() {
1100 assert!(vocab::foaf::knows().as_str().ends_with("knows"));
1101 }
1102
1103 #[test]
1106 fn test_roundtrip_named_node_via_string() {
1107 let iri = "http://example.org/roundtrip";
1108 let n = DataFactory::named_node(iri).expect("valid IRI for named node");
1109 let s = n.as_str().to_string();
1110 let n2 = DataFactory::named_node(s).expect("valid IRI for named node");
1111 assert_eq!(n, n2);
1112 }
1113
1114 #[test]
1115 fn test_roundtrip_typed_literal() {
1116 let l = DataFactory::typed_literal("123", xsd_types::integer());
1117 let val = l.value().to_string();
1118 let dt = l.datatype().into_owned();
1119 let l2 = DataFactory::typed_literal(val, dt);
1120 assert_eq!(l.value(), l2.value());
1121 }
1122
1123 #[test]
1124 fn test_roundtrip_language_literal() {
1125 let l = DataFactory::language_literal("Hola", "es").expect("valid language literal");
1126 let val = l.value().to_string();
1127 let lang = l.language().expect("operation should succeed").to_string();
1128 let l2 = DataFactory::language_literal(val, lang).expect("valid language literal");
1129 assert_eq!(l.value(), l2.value());
1130 assert_eq!(l.language(), l2.language());
1131 }
1132
1133 #[test]
1134 fn test_roundtrip_blank_node_with_id() {
1135 let b = DataFactory::blank_node_with_id("stable");
1136 let id = b.as_str().to_string();
1137 let b2 = DataFactory::blank_node_with_id(id.clone());
1138 assert_eq!(b2.as_str(), id);
1139 }
1140
1141 #[test]
1142 fn test_quad_default_graph_roundtrip() {
1143 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
1144 let p = vocab::rdf::r#type();
1145 let o = vocab::owl::class();
1146 let g = DataFactory::default_graph();
1147 let q = DataFactory::quad(s.into(), p, o.into(), g.clone());
1148 assert!(matches!(q.graph_name(), GraphName::DefaultGraph));
1150 }
1151
1152 #[test]
1153 fn test_namespace_constants() {
1154 assert!(vocab::rdf::NAMESPACE.starts_with("http://"));
1155 assert!(vocab::rdfs::NAMESPACE.starts_with("http://"));
1156 assert!(vocab::owl::NAMESPACE.starts_with("http://"));
1157 assert!(vocab::xsd::NAMESPACE.starts_with("http://"));
1158 assert!(vocab::foaf::NAMESPACE.starts_with("http://"));
1159 }
1160
1161 #[test]
1162 fn test_xsd_types_all_in_xsd_namespace() {
1163 let checks = [
1164 xsd_types::string(),
1165 xsd_types::integer(),
1166 xsd_types::float(),
1167 xsd_types::double(),
1168 xsd_types::boolean(),
1169 xsd_types::date_time(),
1170 xsd_types::date(),
1171 xsd_types::decimal(),
1172 xsd_types::long(),
1173 xsd_types::int(),
1174 xsd_types::short(),
1175 xsd_types::byte(),
1176 xsd_types::unsigned_long(),
1177 xsd_types::unsigned_int(),
1178 xsd_types::non_negative_integer(),
1179 xsd_types::positive_integer(),
1180 xsd_types::any_uri(),
1181 xsd_types::base64_binary(),
1182 xsd_types::hex_binary(),
1183 xsd_types::g_year(),
1184 xsd_types::duration(),
1185 xsd_types::time(),
1186 xsd_types::normalized_string(),
1187 xsd_types::token(),
1188 ];
1189 for node in &checks {
1190 assert!(
1191 node.as_str()
1192 .starts_with("http://www.w3.org/2001/XMLSchema#"),
1193 "Not in XSD namespace: {}",
1194 node.as_str()
1195 );
1196 }
1197 }
1198
1199 #[test]
1200 fn test_multiple_blank_nodes_in_triple() {
1201 let s: Subject = DataFactory::blank_node().into();
1202 let p = vocab::rdf::r#type();
1203 let o: Object = DataFactory::blank_node().into();
1204 let _t = DataFactory::triple(s, p, o);
1205 }
1206
1207 #[test]
1208 fn test_literal_with_unicode_value() {
1209 let l = DataFactory::literal("日本語テスト");
1210 assert_eq!(l.value(), "日本語テスト");
1211 }
1212
1213 #[test]
1214 fn test_typed_literal_float() {
1215 let l = DataFactory::typed_literal("1.5", xsd_types::float());
1216 assert!(l.datatype().as_str().ends_with("float"));
1217 }
1218
1219 #[test]
1220 fn test_typed_literal_decimal() {
1221 let l = DataFactory::typed_literal("9.99", xsd_types::decimal());
1222 assert!(l.datatype().as_str().ends_with("decimal"));
1223 }
1224
1225 #[test]
1226 fn test_rdfs_all_vocabs_are_valid_iris() {
1227 let nodes = [
1228 vocab::rdfs::label(),
1229 vocab::rdfs::comment(),
1230 vocab::rdfs::sub_class_of(),
1231 vocab::rdfs::sub_property_of(),
1232 vocab::rdfs::domain(),
1233 vocab::rdfs::range(),
1234 vocab::rdfs::class(),
1235 vocab::rdfs::resource(),
1236 vocab::rdfs::literal(),
1237 vocab::rdfs::datatype(),
1238 vocab::rdfs::is_defined_by(),
1239 vocab::rdfs::see_also(),
1240 vocab::rdfs::member(),
1241 vocab::rdfs::container(),
1242 ];
1243 for n in &nodes {
1244 assert!(
1245 DataFactory::validate_iri(n.as_str()).is_ok(),
1246 "Invalid IRI for vocab node: {}",
1247 n.as_str()
1248 );
1249 }
1250 }
1251
1252 #[test]
1253 fn test_rdf_all_vocabs_are_valid_iris() {
1254 let nodes = [
1255 vocab::rdf::r#type(),
1256 vocab::rdf::subject(),
1257 vocab::rdf::predicate(),
1258 vocab::rdf::object(),
1259 vocab::rdf::property(),
1260 vocab::rdf::statement(),
1261 vocab::rdf::first(),
1262 vocab::rdf::rest(),
1263 vocab::rdf::nil(),
1264 vocab::rdf::list(),
1265 vocab::rdf::bag(),
1266 vocab::rdf::seq(),
1267 vocab::rdf::alt(),
1268 vocab::rdf::value(),
1269 vocab::rdf::lang_string(),
1270 ];
1271 for n in &nodes {
1272 assert!(
1273 DataFactory::validate_iri(n.as_str()).is_ok(),
1274 "Invalid IRI: {}",
1275 n.as_str()
1276 );
1277 }
1278 }
1279
1280 #[test]
1281 fn test_owl_all_vocabs_are_valid_iris() {
1282 let nodes = [
1283 vocab::owl::class(),
1284 vocab::owl::object_property(),
1285 vocab::owl::datatype_property(),
1286 vocab::owl::annotation_property(),
1287 vocab::owl::thing(),
1288 vocab::owl::nothing(),
1289 vocab::owl::same_as(),
1290 vocab::owl::equivalent_class(),
1291 vocab::owl::equivalent_property(),
1292 vocab::owl::inverse_of(),
1293 vocab::owl::disjoint_with(),
1294 vocab::owl::functional_property(),
1295 vocab::owl::ontology(),
1296 ];
1297 for n in &nodes {
1298 assert!(
1299 DataFactory::validate_iri(n.as_str()).is_ok(),
1300 "Invalid IRI: {}",
1301 n.as_str()
1302 );
1303 }
1304 }
1305}