1use crate::{
7 model::*,
8 parser::RdfFormat,
9 rdf_store::{OxirsQueryResults, RdfStore},
10 transaction::{IsolationLevel, TransactionManager},
11 OxirsError, Result, Store as OxirsStoreTrait,
12};
13use std::io::{BufRead, Write};
14use std::path::{Path, PathBuf};
15use std::sync::{Arc, RwLock};
16
17pub struct Store {
23 inner: Arc<RwLock<RdfStore>>,
24 tx_manager: Arc<RwLock<Option<TransactionManager>>>,
25 wal_dir: Option<PathBuf>,
26}
27
28impl Store {
29 pub fn new() -> Result<Self> {
33 Ok(Store {
34 inner: Arc::new(RwLock::new(RdfStore::new()?)),
35 tx_manager: Arc::new(RwLock::new(None)),
36 wal_dir: None,
37 })
38 }
39
40 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
44 let path_buf = path.as_ref().to_path_buf();
45 let wal_dir = path_buf.join("wal");
46
47 Ok(Store {
48 inner: Arc::new(RwLock::new(RdfStore::open(&path_buf)?)),
49 tx_manager: Arc::new(RwLock::new(None)),
50 wal_dir: Some(wal_dir),
51 })
52 }
53
54 pub fn insert<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool> {
58 let quad_ref = quad.into();
59 let quad = Quad::new(
60 quad_ref.subject().to_owned(),
61 quad_ref.predicate().to_owned(),
62 quad_ref.object().to_owned(),
63 quad_ref.graph_name().to_owned(),
64 );
65
66 let store = self
67 .inner
68 .write()
69 .map_err(|e| OxirsError::Store(format!("Failed to acquire write lock: {e}")))?;
70 store.insert_quad(quad)
71 }
72
73 pub fn extend<'a>(
75 &self,
76 quads: impl IntoIterator<Item = impl Into<QuadRef<'a>>>,
77 ) -> Result<()> {
78 let store = self
79 .inner
80 .write()
81 .map_err(|e| OxirsError::Store(format!("Failed to acquire write lock: {e}")))?;
82
83 for quad in quads {
84 let quad_ref = quad.into();
85 let quad = Quad::new(
86 quad_ref.subject().to_owned(),
87 quad_ref.predicate().to_owned(),
88 quad_ref.object().to_owned(),
89 quad_ref.graph_name().to_owned(),
90 );
91 store.insert_quad(quad)?;
92 }
93
94 Ok(())
95 }
96
97 pub fn remove<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool> {
101 let quad_ref = quad.into();
102 let quad = Quad::new(
103 quad_ref.subject().to_owned(),
104 quad_ref.predicate().to_owned(),
105 quad_ref.object().to_owned(),
106 quad_ref.graph_name().to_owned(),
107 );
108
109 let store = self
110 .inner
111 .write()
112 .map_err(|e| OxirsError::Store(format!("Failed to acquire write lock: {e}")))?;
113 store.remove_quad(&quad)
114 }
115
116 pub fn load_from_reader<R: BufRead>(
118 &self,
119 reader: R,
120 format: RdfFormat,
121 base_iri: Option<&str>,
122 graph: Option<impl Into<GraphName>>,
123 ) -> Result<()> {
124 use crate::parser::Parser;
125
126 let mut data = String::new();
128 let mut reader = reader;
129 reader
131 .read_to_string(&mut data)
132 .map_err(|e| OxirsError::Parse(format!("Failed to read input: {e}")))?;
133
134 let mut parser = Parser::new(format);
136 if let Some(base) = base_iri {
137 parser = parser.with_base_iri(base);
138 }
139
140 let quads = parser.parse_str_to_quads(&data)?;
142
143 let store = self
145 .inner
146 .write()
147 .map_err(|e| OxirsError::Store(format!("Failed to acquire write lock: {e}")))?;
148
149 let target_graph = graph.map(|g| g.into());
151 for quad in quads {
152 let final_quad = if let Some(ref g) = target_graph {
153 Quad::new(
155 quad.subject().clone(),
156 quad.predicate().clone(),
157 quad.object().clone(),
158 g.clone(),
159 )
160 } else {
161 quad
162 };
163 store.insert_quad(final_quad)?;
164 }
165
166 Ok(())
167 }
168
169 pub fn dump_to_writer<'a, W: Write>(
171 &self,
172 mut writer: W,
173 format: RdfFormat,
174 graph: Option<impl Into<GraphNameRef<'a>>>,
175 ) -> Result<()> {
176 use crate::model::{dataset::Dataset, graph::Graph};
177 use crate::serializer::Serializer;
178
179 let store = self
180 .inner
181 .read()
182 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
183
184 let serializer = Serializer::new(format);
185
186 let quads = if let Some(g) = graph {
188 let graph_ref = g.into();
189 let graph_name = graph_ref.to_owned();
190 store.query_quads(None, None, None, Some(&graph_name))?
191 } else {
192 store.iter_quads()?
193 };
194
195 let output = match format {
197 RdfFormat::Turtle | RdfFormat::NTriples | RdfFormat::RdfXml => {
198 let triples: Vec<_> = quads
200 .into_iter()
201 .filter(|q| q.is_default_graph())
202 .map(|q| q.to_triple())
203 .collect();
204 let graph = Graph::from_iter(triples);
205 serializer.serialize_graph(&graph)?
206 }
207 RdfFormat::TriG | RdfFormat::NQuads | RdfFormat::JsonLd => {
208 let dataset = Dataset::from_iter(quads);
210 serializer.serialize_dataset(&dataset)?
211 }
212 };
213
214 writer
215 .write_all(output.as_bytes())
216 .map_err(|e| OxirsError::Serialize(format!("Failed to write output: {e}")))?;
217
218 Ok(())
219 }
220
221 pub fn contains<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool> {
223 let quad_ref = quad.into();
224 let quad = Quad::new(
225 quad_ref.subject().to_owned(),
226 quad_ref.predicate().to_owned(),
227 quad_ref.object().to_owned(),
228 quad_ref.graph_name().to_owned(),
229 );
230
231 let store = self
232 .inner
233 .read()
234 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
235 store.contains_quad(&quad)
236 }
237
238 pub fn len(&self) -> Result<usize> {
240 let store = self
241 .inner
242 .read()
243 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
244 store.len()
245 }
246
247 pub fn is_empty(&self) -> Result<bool> {
249 let store = self
250 .inner
251 .read()
252 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
253 store.is_empty()
254 }
255
256 pub fn quads_for_pattern<'a>(
258 &self,
259 subject: Option<impl Into<SubjectRef<'a>>>,
260 predicate: Option<impl Into<PredicateRef<'a>>>,
261 object: Option<impl Into<ObjectRef<'a>>>,
262 graph_name: Option<impl Into<GraphNameRef<'a>>>,
263 ) -> QuadIter {
264 let subject = subject.map(|s| {
265 let s_ref = s.into();
266 match s_ref {
267 SubjectRef::NamedNode(n) => Subject::NamedNode(n.to_owned()),
268 SubjectRef::BlankNode(b) => Subject::BlankNode(b.to_owned()),
269 SubjectRef::Variable(v) => Subject::Variable(v.to_owned()),
270 }
271 });
272
273 let predicate = predicate.map(|p| {
274 let p_ref = p.into();
275 match p_ref {
276 PredicateRef::NamedNode(n) => Predicate::NamedNode(n.to_owned()),
277 PredicateRef::Variable(v) => Predicate::Variable(v.to_owned()),
278 }
279 });
280
281 let object = object.map(|o| {
282 let o_ref = o.into();
283 match o_ref {
284 ObjectRef::NamedNode(n) => Object::NamedNode(n.to_owned()),
285 ObjectRef::BlankNode(b) => Object::BlankNode(b.to_owned()),
286 ObjectRef::Literal(l) => Object::Literal(l.to_owned()),
287 ObjectRef::Variable(v) => Object::Variable(v.to_owned()),
288 }
289 });
290
291 let graph_name = graph_name.map(|g| {
292 let g_ref = g.into();
293 g_ref.to_owned()
294 });
295
296 let quads = match self.inner.read() {
298 Ok(store) => store
299 .query_quads(
300 subject.as_ref(),
301 predicate.as_ref(),
302 object.as_ref(),
303 graph_name.as_ref(),
304 )
305 .unwrap_or_default(),
306 _ => Vec::new(),
307 };
308
309 QuadIter { quads, index: 0 }
310 }
311
312 pub fn iter(&self) -> QuadIter {
314 self.quads_for_pattern(
315 None::<SubjectRef>,
316 None::<PredicateRef>,
317 None::<ObjectRef>,
318 None::<GraphNameRef>,
319 )
320 }
321
322 pub fn named_graphs(&self) -> GraphNameIter {
324 let mut graph_names = std::collections::HashSet::new();
326 if let Ok(store) = self.inner.read() {
327 if let Ok(quads) = store.iter_quads() {
328 for quad in quads {
329 if let GraphName::NamedNode(n) = quad.graph_name() {
330 graph_names.insert(n.clone());
331 }
332 }
333 }
334 }
335
336 GraphNameIter {
337 graphs: graph_names.into_iter().collect(),
338 index: 0,
339 }
340 }
341
342 pub fn contains_named_graph<'a>(
344 &self,
345 graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
346 ) -> Result<bool> {
347 let graph_ref = graph_name.into();
348 let graph = match graph_ref {
349 NamedOrBlankNodeRef::NamedNode(n) => GraphName::NamedNode(n.to_owned()),
350 NamedOrBlankNodeRef::BlankNode(b) => GraphName::BlankNode(b.to_owned()),
351 };
352
353 let store = self
355 .inner
356 .read()
357 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
358 let quads = store.query_quads(None, None, None, Some(&graph))?;
359 Ok(!quads.is_empty())
360 }
361
362 pub fn clear(&self) -> Result<()> {
364 let mut store = self
365 .inner
366 .write()
367 .map_err(|e| OxirsError::Store(format!("Failed to acquire write lock: {e}")))?;
368 store.clear()
369 }
370
371 pub fn clear_graph<'a>(&self, graph_name: impl Into<GraphNameRef<'a>>) -> Result<()> {
373 let graph_ref = graph_name.into();
374 let graph = graph_ref.to_owned();
375
376 let store = self
377 .inner
378 .write()
379 .map_err(|e| OxirsError::Store(format!("Failed to acquire write lock: {e}")))?;
380
381 let quads_to_remove = store.query_quads(None, None, None, Some(&graph))?;
383
384 for quad in quads_to_remove {
386 store.remove_quad(&quad)?;
387 }
388
389 Ok(())
390 }
391
392 pub fn query(&self, query: &str) -> Result<QueryResults> {
394 let store = self
395 .inner
396 .read()
397 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
398 let results = store.query(query)?;
399 Ok(QueryResults { inner: results })
400 }
401
402 pub fn update(&self, update_str: &str) -> Result<()> {
404 use crate::query::{UpdateExecutor, UpdateParser};
405
406 let parser = UpdateParser::new();
408 let update = parser.parse(update_str)?;
409
410 let store = self
412 .inner
413 .write()
414 .map_err(|e| OxirsError::Store(format!("Failed to acquire write lock: {e}")))?;
415
416 let executor = UpdateExecutor::new(&*store);
418 executor.execute(&update)?;
419
420 Ok(())
421 }
422
423 pub fn transaction<T, E>(
437 &self,
438 f: impl FnOnce(&mut crate::AcidTransaction) -> std::result::Result<T, E>,
439 ) -> std::result::Result<T, E>
440 where
441 E: From<OxirsError>,
442 {
443 self.ensure_tx_manager()?;
445
446 let mut tx_mgr_guard = self
448 .tx_manager
449 .write()
450 .map_err(|e| E::from(OxirsError::Store(format!("Failed to acquire lock: {e}"))))?;
451
452 let tx_mgr = tx_mgr_guard.as_mut().ok_or_else(|| {
453 E::from(OxirsError::Store(
454 "Transaction manager not initialized".to_string(),
455 ))
456 })?;
457
458 let mut transaction = tx_mgr.begin(IsolationLevel::Snapshot).map_err(E::from)?;
460
461 let result = f(&mut transaction);
463
464 match result {
466 Ok(value) => {
467 let inserts = transaction.pending_inserts().to_vec();
470 let deletes = transaction.pending_deletes().to_vec();
471
472 transaction.commit().map_err(E::from)?;
474
475 let store = self.inner.write().map_err(|e| {
479 E::from(OxirsError::Store(format!(
480 "Failed to acquire write lock: {e}"
481 )))
482 })?;
483 for quad in &deletes {
484 store.remove_quad(quad).map_err(E::from)?;
485 }
486 for quad in inserts {
487 store.insert_quad(quad).map_err(E::from)?;
488 }
489 Ok(value)
490 }
491 Err(error) => {
492 let _ = transaction.abort();
493 Err(error)
494 }
495 }
496 }
497
498 fn ensure_tx_manager(&self) -> Result<()> {
500 let mut tx_mgr_guard = self
501 .tx_manager
502 .write()
503 .map_err(|e| OxirsError::Store(format!("Failed to acquire lock: {e}")))?;
504
505 if tx_mgr_guard.is_none() {
506 let wal_dir = if let Some(ref wal_path) = self.wal_dir {
508 wal_path.clone()
509 } else {
510 std::env::temp_dir().join(format!("oxirs_wal_{}", uuid::Uuid::new_v4()))
514 };
515
516 let tx_mgr = TransactionManager::new(&wal_dir)?;
518 *tx_mgr_guard = Some(tx_mgr);
519 }
520
521 Ok(())
522 }
523
524 pub fn validate(&self) -> Result<()> {
530 let store = self
531 .inner
532 .read()
533 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
534
535 let reported = OxirsStoreTrait::len(&*store)?;
536 let materialized = OxirsStoreTrait::quads(&*store)?.len();
537 if reported != materialized {
538 return Err(OxirsError::Store(format!(
539 "Store consistency check failed: len() reports {reported} quads but {materialized} were found in the indexes"
540 )));
541 }
542
543 Ok(())
544 }
545
546 pub fn optimize(&self) -> Result<()> {
548 let store = self
550 .inner
551 .read()
552 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
553 store.clear_arena();
554 Ok(())
555 }
556
557 pub fn backup<P: AsRef<Path>>(&self, path: P) -> Result<()> {
559 use crate::parser::RdfFormat;
560 use crate::serializer::Serializer;
561 use std::fs::File;
562 use std::io::Write;
563 use std::time::{SystemTime, UNIX_EPOCH};
564
565 let backup_path = path.as_ref();
566
567 if let Some(parent) = backup_path.parent() {
569 std::fs::create_dir_all(parent).map_err(|e| {
570 OxirsError::Store(format!("Failed to create backup directory: {e}"))
571 })?;
572 }
573
574 let timestamp = SystemTime::now()
576 .duration_since(UNIX_EPOCH)
577 .unwrap_or_default()
578 .as_secs();
579
580 let backup_file_path = if backup_path.is_dir() {
581 backup_path.join(format!("oxirs_backup_{timestamp}.nq"))
582 } else {
583 backup_path.to_path_buf()
584 };
585
586 let store = self
588 .inner
589 .read()
590 .map_err(|e| OxirsError::Store(format!("Failed to acquire read lock: {e}")))?;
591
592 let quads = store
594 .iter_quads()
595 .map_err(|e| OxirsError::Store(format!("Failed to iterate quads: {e}")))?;
596
597 let dataset = crate::model::dataset::Dataset::from_iter(quads.clone());
599
600 let serializer = Serializer::new(RdfFormat::NQuads);
602 let serialized_data = serializer
603 .serialize_dataset(&dataset)
604 .map_err(|e| OxirsError::Store(format!("Failed to serialize dataset: {e}")))?;
605
606 let mut backup_file = File::create(&backup_file_path)
608 .map_err(|e| OxirsError::Store(format!("Failed to create backup file: {e}")))?;
609
610 backup_file
611 .write_all(serialized_data.as_bytes())
612 .map_err(|e| OxirsError::Store(format!("Failed to write backup data: {e}")))?;
613
614 backup_file
615 .sync_all()
616 .map_err(|e| OxirsError::Store(format!("Failed to sync backup file: {e}")))?;
617
618 let backup_size = serialized_data.len();
620 let quad_count = quads.len();
621
622 tracing::info!(
623 "Store backup completed successfully. File: {}, Quads: {}, Size: {} bytes",
624 backup_file_path.display(),
625 quad_count,
626 backup_size
627 );
628
629 Ok(())
630 }
631
632 pub fn flush(&self) -> Result<()> {
634 Ok(())
636 }
637}
638
639impl Default for Store {
640 fn default() -> Self {
641 Store::new().expect("Store::new() should not fail")
642 }
643}
644
645pub struct QuadIter {
647 quads: Vec<Quad>,
648 index: usize,
649}
650
651impl Iterator for QuadIter {
652 type Item = Quad;
653
654 fn next(&mut self) -> Option<Self::Item> {
655 if self.index < self.quads.len() {
656 let quad = self.quads[self.index].clone();
657 self.index += 1;
658 Some(quad)
659 } else {
660 None
661 }
662 }
663}
664
665pub struct GraphNameIter {
667 graphs: Vec<NamedNode>,
668 index: usize,
669}
670
671impl Iterator for GraphNameIter {
672 type Item = NamedNode;
673
674 fn next(&mut self) -> Option<Self::Item> {
675 if self.index < self.graphs.len() {
676 let graph = self.graphs[self.index].clone();
677 self.index += 1;
678 Some(graph)
679 } else {
680 None
681 }
682 }
683}
684
685pub struct QueryResults {
687 #[allow(dead_code)]
688 inner: OxirsQueryResults,
689}
690
691impl QueryResults {
692 pub fn is_boolean(&self) -> bool {
694 matches!(
695 self.inner.results(),
696 crate::rdf_store::types::QueryResults::Boolean(_)
697 )
698 }
699
700 pub fn boolean(&self) -> Option<bool> {
702 match self.inner.results() {
703 crate::rdf_store::types::QueryResults::Boolean(b) => Some(*b),
704 _ => None,
705 }
706 }
707
708 pub fn is_solutions(&self) -> bool {
710 matches!(
711 self.inner.results(),
712 crate::rdf_store::types::QueryResults::Bindings(_)
713 )
714 }
715
716 pub fn is_graph(&self) -> bool {
718 matches!(
719 self.inner.results(),
720 crate::rdf_store::types::QueryResults::Graph(_)
721 )
722 }
723}
724
725pub struct Transaction {
730 operations: Vec<TransactionOp>,
732}
733
734enum TransactionOp {
735 #[allow(dead_code)]
736 Insert(Quad),
737 #[allow(dead_code)]
738 Remove(Quad),
739}
740
741impl Transaction {
742 #[allow(dead_code)]
743 fn new() -> Self {
744 Transaction {
745 operations: Vec::new(),
746 }
747 }
748
749 pub fn insert<'b>(&mut self, quad: impl Into<QuadRef<'b>>) -> Result<bool> {
751 let quad_ref = quad.into();
752 let quad = Quad::new(
753 quad_ref.subject().to_owned(),
754 quad_ref.predicate().to_owned(),
755 quad_ref.object().to_owned(),
756 quad_ref.graph_name().to_owned(),
757 );
758 self.operations.push(TransactionOp::Insert(quad));
759 Ok(true) }
761
762 pub fn remove<'b>(&mut self, quad: impl Into<QuadRef<'b>>) -> Result<bool> {
764 let quad_ref = quad.into();
765 let quad = Quad::new(
766 quad_ref.subject().to_owned(),
767 quad_ref.predicate().to_owned(),
768 quad_ref.object().to_owned(),
769 quad_ref.graph_name().to_owned(),
770 );
771 self.operations.push(TransactionOp::Remove(quad));
772 Ok(true) }
774}
775
776#[derive(Debug, thiserror::Error)]
778pub enum OxigraphCompatError {
779 #[error("Store error: {0}")]
780 Store(String),
781 #[error("Parse error: {0}")]
782 Parse(String),
783 #[error("IO error: {0}")]
784 Io(#[from] std::io::Error),
785}
786
787impl From<OxirsError> for OxigraphCompatError {
788 fn from(err: OxirsError) -> Self {
789 match err {
790 OxirsError::Store(msg) => OxigraphCompatError::Store(msg),
791 OxirsError::Parse(msg) => OxigraphCompatError::Parse(msg),
792 _ => OxigraphCompatError::Store(err.to_string()),
793 }
794 }
795}
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800 use crate::model::{Literal, NamedNode};
801 use crate::parser::RdfFormat;
802 use std::io::Cursor;
803
804 #[test]
805 fn test_oxigraph_compat_store_creation() {
806 let store = Store::new().expect("construction should succeed");
807 assert!(store.is_empty().expect("store operation should succeed"));
808 assert_eq!(store.len().expect("store operation should succeed"), 0);
809 }
810
811 #[test]
812 fn test_oxigraph_compat_insert_and_query() {
813 let store = Store::new().expect("construction should succeed");
814
815 let subject = NamedNode::new("http://example.org/subject").expect("valid IRI");
817 let predicate = NamedNode::new("http://example.org/predicate").expect("valid IRI");
818 let object = Literal::new("test object");
819 let graph = NamedNode::new("http://example.org/graph").expect("valid IRI");
820
821 let quad = Quad::new(
822 subject.clone(),
823 predicate.clone(),
824 object.clone(),
825 graph.clone(),
826 );
827
828 assert!(store
830 .insert(QuadRef::from(&quad))
831 .expect("store insert should succeed"));
832 assert_eq!(store.len().expect("store operation should succeed"), 1);
833 assert!(!store.is_empty().expect("store operation should succeed"));
834
835 assert!(store
837 .contains(QuadRef::from(&quad))
838 .expect("store contains should succeed"));
839
840 let quads: Vec<_> = store
842 .quads_for_pattern(
843 Some(SubjectRef::NamedNode(&subject)),
844 None::<PredicateRef>,
845 None::<ObjectRef>,
846 None::<GraphNameRef>,
847 )
848 .collect();
849 assert_eq!(quads.len(), 1);
850 assert_eq!(quads[0], quad);
851
852 assert!(store
854 .remove(QuadRef::from(&quad))
855 .expect("store remove should succeed"));
856 assert!(store.is_empty().expect("store operation should succeed"));
857 }
858
859 #[test]
860 fn test_oxigraph_compat_extend() {
861 let store = Store::new().expect("construction should succeed");
862
863 let quads = [
864 Quad::new(
865 NamedNode::new("http://example.org/s1").expect("valid IRI"),
866 NamedNode::new("http://example.org/p1").expect("valid IRI"),
867 Literal::new("o1"),
868 GraphName::DefaultGraph,
869 ),
870 Quad::new(
871 NamedNode::new("http://example.org/s2").expect("valid IRI"),
872 NamedNode::new("http://example.org/p2").expect("valid IRI"),
873 Literal::new("o2"),
874 NamedNode::new("http://example.org/g1").expect("valid IRI"),
875 ),
876 ];
877
878 store
879 .extend(quads.iter().map(QuadRef::from))
880 .expect("extend should succeed");
881 assert_eq!(store.len().expect("store operation should succeed"), 2);
882 }
883
884 #[test]
885 fn test_oxigraph_compat_named_graphs() {
886 let store = Store::new().expect("construction should succeed");
887
888 let s1 = NamedNode::new("http://example.org/s1").expect("valid IRI");
890 let s2 = NamedNode::new("http://example.org/s2").expect("valid IRI");
891 let p1 = NamedNode::new("http://example.org/p1").expect("valid IRI");
892 let p2 = NamedNode::new("http://example.org/p2").expect("valid IRI");
893 let o1 = Literal::new("o1");
894 let o2 = Literal::new("o2");
895 let g1 = NamedNode::new("http://example.org/g1").expect("valid IRI");
896 let g2 = NamedNode::new("http://example.org/g2").expect("valid IRI");
897
898 store
900 .insert(QuadRef::new(
901 SubjectRef::NamedNode(&s1),
902 PredicateRef::NamedNode(&p1),
903 ObjectRef::Literal(&o1),
904 GraphNameRef::NamedNode(&g1),
905 ))
906 .expect("operation should succeed");
907
908 store
909 .insert(QuadRef::new(
910 SubjectRef::NamedNode(&s2),
911 PredicateRef::NamedNode(&p2),
912 ObjectRef::Literal(&o2),
913 GraphNameRef::NamedNode(&g2),
914 ))
915 .expect("operation should succeed");
916
917 let graphs: Vec<_> = store.named_graphs().collect();
919 assert_eq!(graphs.len(), 2);
920 assert!(graphs.contains(&g1));
921 assert!(graphs.contains(&g2));
922
923 assert!(store
925 .contains_named_graph(NamedOrBlankNodeRef::NamedNode(&g1))
926 .expect("operation should succeed"));
927 assert!(store
928 .contains_named_graph(NamedOrBlankNodeRef::NamedNode(&g2))
929 .expect("operation should succeed"));
930 }
931
932 #[test]
933 fn test_oxigraph_compat_clear_graph() {
934 let store = Store::new().expect("construction should succeed");
935
936 let s1 = NamedNode::new("http://example.org/s1").expect("valid IRI");
938 let s2 = NamedNode::new("http://example.org/s2").expect("valid IRI");
939 let p1 = NamedNode::new("http://example.org/p1").expect("valid IRI");
940 let p2 = NamedNode::new("http://example.org/p2").expect("valid IRI");
941 let o1 = Literal::new("o1");
942 let o2 = Literal::new("o2");
943 let graph = NamedNode::new("http://example.org/graph").expect("valid IRI");
944
945 store
947 .insert(QuadRef::new(
948 SubjectRef::NamedNode(&s1),
949 PredicateRef::NamedNode(&p1),
950 ObjectRef::Literal(&o1),
951 GraphNameRef::NamedNode(&graph),
952 ))
953 .expect("operation should succeed");
954
955 store
956 .insert(QuadRef::new(
957 SubjectRef::NamedNode(&s2),
958 PredicateRef::NamedNode(&p2),
959 ObjectRef::Literal(&o2),
960 GraphNameRef::DefaultGraph,
961 ))
962 .expect("operation should succeed");
963
964 assert_eq!(store.len().expect("store operation should succeed"), 2);
965
966 store
968 .clear_graph(GraphNameRef::NamedNode(&graph))
969 .expect("clear_graph should succeed");
970 assert_eq!(store.len().expect("store operation should succeed"), 1); store.clear().expect("store operation should succeed");
974 assert!(store.is_empty().expect("store operation should succeed"));
975 }
976
977 #[test]
978 fn test_oxigraph_compat_load_from_reader() {
979 let store = Store::new().expect("construction should succeed");
980
981 let turtle_data = r#"
982 @prefix ex: <http://example.org/> .
983 ex:subject ex:predicate "object" .
984 "#;
985
986 let reader = Cursor::new(turtle_data.as_bytes());
987 store
988 .load_from_reader(
989 reader,
990 RdfFormat::Turtle,
991 Some("http://example.org/"),
992 None::<GraphName>,
993 )
994 .expect("operation should succeed");
995
996 assert_eq!(store.len().expect("store operation should succeed"), 1);
997
998 let quads: Vec<_> = store.iter().collect();
1000 assert_eq!(quads.len(), 1);
1001 assert_eq!(
1002 quads[0].subject().to_string(),
1003 "<http://example.org/subject>"
1004 );
1005 assert_eq!(
1006 quads[0].predicate().to_string(),
1007 "<http://example.org/predicate>"
1008 );
1009 }
1010
1011 #[test]
1012 fn test_oxigraph_compat_dump_to_writer() {
1013 let store = Store::new().expect("construction should succeed");
1014
1015 let subject = NamedNode::new("http://example.org/subject").expect("valid IRI");
1017 let predicate = NamedNode::new("http://example.org/predicate").expect("valid IRI");
1018 let object = Literal::new("object");
1019
1020 store
1022 .insert(QuadRef::new(
1023 SubjectRef::NamedNode(&subject),
1024 PredicateRef::NamedNode(&predicate),
1025 ObjectRef::Literal(&object),
1026 GraphNameRef::DefaultGraph,
1027 ))
1028 .expect("operation should succeed");
1029
1030 let mut output = Vec::new();
1032 store
1033 .dump_to_writer(&mut output, RdfFormat::NTriples, None::<GraphNameRef>)
1034 .expect("operation should succeed");
1035
1036 let result = String::from_utf8(output).expect("bytes should be valid UTF-8");
1037 assert!(result.contains("<http://example.org/subject>"));
1038 assert!(result.contains("<http://example.org/predicate>"));
1039 assert!(result.contains("\"object\""));
1040 }
1041}