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"));
779 }
780
781 #[test]
782 fn test_language_literal_en_us() {
783 let l = DataFactory::language_literal("Color", "en-US").expect("valid language literal");
784 assert_eq!(l.language(), Some("en-us"));
785 }
786
787 #[test]
788 fn test_language_literal_empty_lang_is_err() {
789 assert!(DataFactory::language_literal("hello", "").is_err());
790 }
791
792 #[test]
793 fn test_language_literal_invalid_lang_is_err() {
794 assert!(DataFactory::language_literal("hello", "en US").is_err());
796 }
797
798 #[test]
801 fn test_triple_subject_predicate_object() {
802 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
803 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
804 let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
805 let t = DataFactory::triple(s.into(), p.clone(), o.into());
806 let text = format!("{t}");
808 assert!(text.contains("http://example.org/s"));
809 }
810
811 #[test]
812 fn test_triple_with_literal_object() {
813 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
814 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
815 let o: Object = DataFactory::literal("hello").into();
816 let t = DataFactory::triple(s.into(), p, o);
817 let text = format!("{t}");
818 assert!(text.contains("hello"));
819 }
820
821 #[test]
822 fn test_triple_with_blank_node_subject() {
823 let s: Subject = DataFactory::blank_node().into();
824 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
825 let o: Object = DataFactory::literal("val").into();
826 let _t = DataFactory::triple(s, p, o);
827 }
828
829 #[test]
832 fn test_quad_default_graph() {
833 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
834 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
835 let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
836 let g = DataFactory::default_graph();
837 let q = DataFactory::quad(s.into(), p, o.into(), g);
838 let text = format!("{q}");
839 assert!(text.contains("http://example.org/s"));
840 }
841
842 #[test]
843 fn test_quad_named_graph() {
844 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
845 let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
846 let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
847 let g = DataFactory::named_graph("http://example.org/graph1")
848 .expect("construction should succeed");
849 let q = DataFactory::quad(s.into(), p, o.into(), g);
850 let text = format!("{q}");
851 assert!(text.contains("graph1"));
852 }
853
854 #[test]
855 fn test_quad_named_graph_invalid_iri_is_err() {
856 assert!(DataFactory::named_graph("not an IRI").is_err());
857 }
858
859 #[test]
860 fn test_default_graph_is_default_graph_variant() {
861 let g = DataFactory::default_graph();
862 assert!(matches!(g, GraphName::DefaultGraph));
863 }
864
865 #[test]
868 fn test_validate_iri_http_ok() {
869 assert!(DataFactory::validate_iri("http://example.org/").is_ok());
870 }
871
872 #[test]
873 fn test_validate_iri_https_ok() {
874 assert!(DataFactory::validate_iri("https://example.org/path").is_ok());
875 }
876
877 #[test]
878 fn test_validate_iri_urn_ok() {
879 assert!(DataFactory::validate_iri("urn:isbn:0451450523").is_ok());
880 }
881
882 #[test]
883 fn test_validate_iri_bare_word_is_err() {
884 assert!(DataFactory::validate_iri("hello").is_err());
885 }
886
887 #[test]
888 fn test_validate_iri_empty_is_err() {
889 assert!(DataFactory::validate_iri("").is_err());
890 }
891
892 #[test]
893 fn test_validate_iri_space_is_err() {
894 assert!(DataFactory::validate_iri("http://example.org/hello world").is_err());
895 }
896
897 #[test]
900 fn test_validate_lang_tag_en_ok() {
901 assert!(DataFactory::validate_lang_tag("en").is_ok());
902 }
903
904 #[test]
905 fn test_validate_lang_tag_en_us_ok() {
906 assert!(DataFactory::validate_lang_tag("en-US").is_ok());
907 }
908
909 #[test]
910 fn test_validate_lang_tag_zh_hans_cn_ok() {
911 assert!(DataFactory::validate_lang_tag("zh-Hans-CN").is_ok());
912 }
913
914 #[test]
915 fn test_validate_lang_tag_empty_is_err() {
916 assert!(DataFactory::validate_lang_tag("").is_err());
917 }
918
919 #[test]
920 fn test_validate_lang_tag_space_is_err() {
921 assert!(DataFactory::validate_lang_tag("en US").is_err());
922 }
923
924 #[test]
925 fn test_validate_lang_tag_double_dash_is_err() {
926 assert!(DataFactory::validate_lang_tag("en--US").is_err());
927 }
928
929 #[test]
930 fn test_validate_lang_tag_numeric_primary_is_err() {
931 assert!(DataFactory::validate_lang_tag("123").is_err());
933 }
934
935 #[test]
938 fn test_xsd_string_iri() {
939 assert_eq!(
940 xsd_types::string().as_str(),
941 "http://www.w3.org/2001/XMLSchema#string"
942 );
943 }
944
945 #[test]
946 fn test_xsd_integer_iri() {
947 assert_eq!(
948 xsd_types::integer().as_str(),
949 "http://www.w3.org/2001/XMLSchema#integer"
950 );
951 }
952
953 #[test]
954 fn test_xsd_float_iri() {
955 assert!(xsd_types::float().as_str().ends_with("float"));
956 }
957
958 #[test]
959 fn test_xsd_double_iri() {
960 assert!(xsd_types::double().as_str().ends_with("double"));
961 }
962
963 #[test]
964 fn test_xsd_boolean_iri() {
965 assert!(xsd_types::boolean().as_str().ends_with("boolean"));
966 }
967
968 #[test]
969 fn test_xsd_date_time_iri() {
970 assert!(xsd_types::date_time().as_str().ends_with("dateTime"));
971 }
972
973 #[test]
974 fn test_xsd_date_iri() {
975 assert!(xsd_types::date().as_str().ends_with("date"));
976 }
977
978 #[test]
979 fn test_xsd_decimal_iri() {
980 assert!(xsd_types::decimal().as_str().ends_with("decimal"));
981 }
982
983 #[test]
984 fn test_xsd_long_iri() {
985 assert!(xsd_types::long().as_str().ends_with("long"));
986 }
987
988 #[test]
989 fn test_xsd_int_iri() {
990 assert!(xsd_types::int().as_str().ends_with("#int"));
991 }
992
993 #[test]
994 fn test_xsd_any_uri_iri() {
995 assert!(xsd_types::any_uri().as_str().ends_with("anyURI"));
996 }
997
998 #[test]
999 fn test_xsd_base64_binary_iri() {
1000 assert!(xsd_types::base64_binary()
1001 .as_str()
1002 .ends_with("base64Binary"));
1003 }
1004
1005 #[test]
1006 fn test_xsd_hex_binary_iri() {
1007 assert!(xsd_types::hex_binary().as_str().ends_with("hexBinary"));
1008 }
1009
1010 #[test]
1013 fn test_vocab_rdf_type() {
1014 assert_eq!(
1015 vocab::rdf::r#type().as_str(),
1016 "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
1017 );
1018 }
1019
1020 #[test]
1021 fn test_vocab_rdf_first_last() {
1022 assert!(vocab::rdf::first().as_str().ends_with("first"));
1023 assert!(vocab::rdf::rest().as_str().ends_with("rest"));
1024 assert!(vocab::rdf::nil().as_str().ends_with("nil"));
1025 }
1026
1027 #[test]
1028 fn test_vocab_rdfs_label() {
1029 assert_eq!(
1030 vocab::rdfs::label().as_str(),
1031 "http://www.w3.org/2000/01/rdf-schema#label"
1032 );
1033 }
1034
1035 #[test]
1036 fn test_vocab_rdfs_comment() {
1037 assert!(vocab::rdfs::comment().as_str().ends_with("comment"));
1038 }
1039
1040 #[test]
1041 fn test_vocab_rdfs_sub_class_of() {
1042 assert!(vocab::rdfs::sub_class_of().as_str().ends_with("subClassOf"));
1043 }
1044
1045 #[test]
1046 fn test_vocab_rdfs_domain_range() {
1047 assert!(vocab::rdfs::domain().as_str().ends_with("domain"));
1048 assert!(vocab::rdfs::range().as_str().ends_with("range"));
1049 }
1050
1051 #[test]
1052 fn test_vocab_owl_class() {
1053 assert_eq!(
1054 vocab::owl::class().as_str(),
1055 "http://www.w3.org/2002/07/owl#Class"
1056 );
1057 }
1058
1059 #[test]
1060 fn test_vocab_owl_same_as() {
1061 assert!(vocab::owl::same_as().as_str().ends_with("sameAs"));
1062 }
1063
1064 #[test]
1065 fn test_vocab_owl_thing_nothing() {
1066 assert!(vocab::owl::thing().as_str().ends_with("Thing"));
1067 assert!(vocab::owl::nothing().as_str().ends_with("Nothing"));
1068 }
1069
1070 #[test]
1071 fn test_vocab_owl_object_property() {
1072 assert!(vocab::owl::object_property()
1073 .as_str()
1074 .ends_with("ObjectProperty"));
1075 }
1076
1077 #[test]
1078 fn test_vocab_xsd_string() {
1079 assert!(vocab::xsd::string().as_str().ends_with("string"));
1080 }
1081
1082 #[test]
1083 fn test_vocab_foaf_name() {
1084 assert_eq!(
1085 vocab::foaf::name().as_str(),
1086 "http://xmlns.com/foaf/0.1/name"
1087 );
1088 }
1089
1090 #[test]
1091 fn test_vocab_foaf_person() {
1092 assert!(vocab::foaf::person().as_str().ends_with("Person"));
1093 }
1094
1095 #[test]
1096 fn test_vocab_foaf_knows() {
1097 assert!(vocab::foaf::knows().as_str().ends_with("knows"));
1098 }
1099
1100 #[test]
1103 fn test_roundtrip_named_node_via_string() {
1104 let iri = "http://example.org/roundtrip";
1105 let n = DataFactory::named_node(iri).expect("valid IRI for named node");
1106 let s = n.as_str().to_string();
1107 let n2 = DataFactory::named_node(s).expect("valid IRI for named node");
1108 assert_eq!(n, n2);
1109 }
1110
1111 #[test]
1112 fn test_roundtrip_typed_literal() {
1113 let l = DataFactory::typed_literal("123", xsd_types::integer());
1114 let val = l.value().to_string();
1115 let dt = l.datatype().into_owned();
1116 let l2 = DataFactory::typed_literal(val, dt);
1117 assert_eq!(l.value(), l2.value());
1118 }
1119
1120 #[test]
1121 fn test_roundtrip_language_literal() {
1122 let l = DataFactory::language_literal("Hola", "es").expect("valid language literal");
1123 let val = l.value().to_string();
1124 let lang = l.language().expect("operation should succeed").to_string();
1125 let l2 = DataFactory::language_literal(val, lang).expect("valid language literal");
1126 assert_eq!(l.value(), l2.value());
1127 assert_eq!(l.language(), l2.language());
1128 }
1129
1130 #[test]
1131 fn test_roundtrip_blank_node_with_id() {
1132 let b = DataFactory::blank_node_with_id("stable");
1133 let id = b.as_str().to_string();
1134 let b2 = DataFactory::blank_node_with_id(id.clone());
1135 assert_eq!(b2.as_str(), id);
1136 }
1137
1138 #[test]
1139 fn test_quad_default_graph_roundtrip() {
1140 let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
1141 let p = vocab::rdf::r#type();
1142 let o = vocab::owl::class();
1143 let g = DataFactory::default_graph();
1144 let q = DataFactory::quad(s.into(), p, o.into(), g.clone());
1145 assert!(matches!(q.graph_name(), GraphName::DefaultGraph));
1147 }
1148
1149 #[test]
1150 fn test_namespace_constants() {
1151 assert!(vocab::rdf::NAMESPACE.starts_with("http://"));
1152 assert!(vocab::rdfs::NAMESPACE.starts_with("http://"));
1153 assert!(vocab::owl::NAMESPACE.starts_with("http://"));
1154 assert!(vocab::xsd::NAMESPACE.starts_with("http://"));
1155 assert!(vocab::foaf::NAMESPACE.starts_with("http://"));
1156 }
1157
1158 #[test]
1159 fn test_xsd_types_all_in_xsd_namespace() {
1160 let checks = [
1161 xsd_types::string(),
1162 xsd_types::integer(),
1163 xsd_types::float(),
1164 xsd_types::double(),
1165 xsd_types::boolean(),
1166 xsd_types::date_time(),
1167 xsd_types::date(),
1168 xsd_types::decimal(),
1169 xsd_types::long(),
1170 xsd_types::int(),
1171 xsd_types::short(),
1172 xsd_types::byte(),
1173 xsd_types::unsigned_long(),
1174 xsd_types::unsigned_int(),
1175 xsd_types::non_negative_integer(),
1176 xsd_types::positive_integer(),
1177 xsd_types::any_uri(),
1178 xsd_types::base64_binary(),
1179 xsd_types::hex_binary(),
1180 xsd_types::g_year(),
1181 xsd_types::duration(),
1182 xsd_types::time(),
1183 xsd_types::normalized_string(),
1184 xsd_types::token(),
1185 ];
1186 for node in &checks {
1187 assert!(
1188 node.as_str()
1189 .starts_with("http://www.w3.org/2001/XMLSchema#"),
1190 "Not in XSD namespace: {}",
1191 node.as_str()
1192 );
1193 }
1194 }
1195
1196 #[test]
1197 fn test_multiple_blank_nodes_in_triple() {
1198 let s: Subject = DataFactory::blank_node().into();
1199 let p = vocab::rdf::r#type();
1200 let o: Object = DataFactory::blank_node().into();
1201 let _t = DataFactory::triple(s, p, o);
1202 }
1203
1204 #[test]
1205 fn test_literal_with_unicode_value() {
1206 let l = DataFactory::literal("日本語テスト");
1207 assert_eq!(l.value(), "日本語テスト");
1208 }
1209
1210 #[test]
1211 fn test_typed_literal_float() {
1212 let l = DataFactory::typed_literal("1.5", xsd_types::float());
1213 assert!(l.datatype().as_str().ends_with("float"));
1214 }
1215
1216 #[test]
1217 fn test_typed_literal_decimal() {
1218 let l = DataFactory::typed_literal("9.99", xsd_types::decimal());
1219 assert!(l.datatype().as_str().ends_with("decimal"));
1220 }
1221
1222 #[test]
1223 fn test_rdfs_all_vocabs_are_valid_iris() {
1224 let nodes = [
1225 vocab::rdfs::label(),
1226 vocab::rdfs::comment(),
1227 vocab::rdfs::sub_class_of(),
1228 vocab::rdfs::sub_property_of(),
1229 vocab::rdfs::domain(),
1230 vocab::rdfs::range(),
1231 vocab::rdfs::class(),
1232 vocab::rdfs::resource(),
1233 vocab::rdfs::literal(),
1234 vocab::rdfs::datatype(),
1235 vocab::rdfs::is_defined_by(),
1236 vocab::rdfs::see_also(),
1237 vocab::rdfs::member(),
1238 vocab::rdfs::container(),
1239 ];
1240 for n in &nodes {
1241 assert!(
1242 DataFactory::validate_iri(n.as_str()).is_ok(),
1243 "Invalid IRI for vocab node: {}",
1244 n.as_str()
1245 );
1246 }
1247 }
1248
1249 #[test]
1250 fn test_rdf_all_vocabs_are_valid_iris() {
1251 let nodes = [
1252 vocab::rdf::r#type(),
1253 vocab::rdf::subject(),
1254 vocab::rdf::predicate(),
1255 vocab::rdf::object(),
1256 vocab::rdf::property(),
1257 vocab::rdf::statement(),
1258 vocab::rdf::first(),
1259 vocab::rdf::rest(),
1260 vocab::rdf::nil(),
1261 vocab::rdf::list(),
1262 vocab::rdf::bag(),
1263 vocab::rdf::seq(),
1264 vocab::rdf::alt(),
1265 vocab::rdf::value(),
1266 vocab::rdf::lang_string(),
1267 ];
1268 for n in &nodes {
1269 assert!(
1270 DataFactory::validate_iri(n.as_str()).is_ok(),
1271 "Invalid IRI: {}",
1272 n.as_str()
1273 );
1274 }
1275 }
1276
1277 #[test]
1278 fn test_owl_all_vocabs_are_valid_iris() {
1279 let nodes = [
1280 vocab::owl::class(),
1281 vocab::owl::object_property(),
1282 vocab::owl::datatype_property(),
1283 vocab::owl::annotation_property(),
1284 vocab::owl::thing(),
1285 vocab::owl::nothing(),
1286 vocab::owl::same_as(),
1287 vocab::owl::equivalent_class(),
1288 vocab::owl::equivalent_property(),
1289 vocab::owl::inverse_of(),
1290 vocab::owl::disjoint_with(),
1291 vocab::owl::functional_property(),
1292 vocab::owl::ontology(),
1293 ];
1294 for n in &nodes {
1295 assert!(
1296 DataFactory::validate_iri(n.as_str()).is_ok(),
1297 "Invalid IRI: {}",
1298 n.as_str()
1299 );
1300 }
1301 }
1302}