1use std::collections::HashSet;
7use std::sync::Arc;
8
9use async_trait::async_trait;
10use sbol_db_core::{
11 DomainError, GraphId, GraphRecord, ImportReport, IriString, NeighborhoodQuery,
12 NeighborhoodResult, ObjectId, SbolObjectRecord, SerializationFormat, Triple,
13};
14use sbol_db_derive::{build_import_plan, compose_merged_input, to_rdf_format};
15use sbol_db_rdf::{rdf_graph_to_triples, GRAPH_IRI_PREFIX};
16use sbol_db_storage::{
17 distinct_graph_iris, distinct_object_iris, AccelSolutions, AcceleratedQuery, AclStore,
18 BatchSequenceMatch, CatalogSequenceRecord, ClassCount, CorpusCounts, CorpusStats,
19 CorpusStatsStore, CursorPage, GraphFilter, GraphOverview, GraphStore, GraphTriplesPage,
20 GraphWriteMode, ImportInput, ImportOverwrite, LabStore, ListGraphsFilter, ListObjectsFilter,
21 NamedGraphCatalogStore, NamedGraphQuery, NamedGraphRecord, NeighborhoodStore, ObjectStore,
22 OntologyLoadReport, OntologyRecord, OntologyStore, OntologyTermRecord, PatternObject,
23 PatternSubject, ResourceCatalogStore, ResourceOccurrence, ResourceQuery, ResourceRecord,
24 SbolStore, SequenceCatalogStore, SequenceMatch, SequenceQuery, SequenceSearchOptions,
25 SequenceSearchStore, TextSearchQuery, TextSearchStore, TripleChange, TriplePageQuery,
26 TripleScanPage, TripleSource, TripleWriter, UpdateOutcome, SBH_CAN_VIEW, SBH_OWNED_BY,
27};
28use tokio::runtime::Handle;
29
30use crate::pool::db_err;
31use crate::repo::{
32 AccelRepository, GraphRepository, LabRepository, OntologyRepository, SbolObjectRepository,
33 SequenceSearchRepository, TripleRepository,
34};
35use crate::SqlitePool;
36
37const GRAPH_READ_LIMIT: i64 = 5_000_000;
39const UPDATE_SOURCE: &str = "sparql-update";
40
41#[derive(Clone)]
43pub struct SqliteStore {
44 pool: SqlitePool,
45 graphs: GraphRepository,
46 objects: SbolObjectRepository,
47 triples: TripleRepository,
48 accel: AccelRepository,
49 ontology: OntologyRepository,
50 sequences: SequenceSearchRepository,
51 lab: LabRepository,
52}
53
54impl SqliteStore {
55 pub fn new(pool: SqlitePool) -> Self {
56 Self {
57 graphs: GraphRepository::new(pool.clone()),
58 objects: SbolObjectRepository::new(pool.clone()),
59 triples: TripleRepository::new(pool.clone()),
60 accel: AccelRepository::new(pool.clone(), TripleRepository::new(pool.clone())),
61 ontology: OntologyRepository::new(pool.clone()),
62 sequences: SequenceSearchRepository::new(pool.clone()),
63 lab: LabRepository::new(pool.clone()),
64 pool,
65 }
66 }
67
68 pub fn pool(&self) -> &SqlitePool {
69 &self.pool
70 }
71
72 pub async fn ping(&self) -> Result<(), DomainError> {
73 sqlx::query("SELECT 1")
74 .execute(&self.pool)
75 .await
76 .map(|_| ())
77 .map_err(db_err)
78 }
79
80 pub fn triple_source(&self) -> Arc<dyn TripleSource> {
81 Arc::new(SqliteTripleSource {
82 triples: Arc::new(self.triples.clone()),
83 accel: self.accel.clone(),
84 })
85 }
86
87 pub fn triple_writer(&self) -> Arc<dyn TripleWriter> {
88 Arc::new(SqliteTripleWriter {
89 triples: Arc::new(self.triples.clone()),
90 accel: self.accel.clone(),
91 pool: self.pool.clone(),
92 })
93 }
94
95 pub async fn import_document(&self, input: ImportInput) -> Result<ImportReport, DomainError> {
96 let input = self.resolve_merge(input).await?;
97 let mut tx = self.pool.begin().await.map_err(db_err)?;
98 let report = self.import_into_conn(&mut tx, input).await?;
99 tx.commit().await.map_err(db_err)?;
100 Ok(report)
101 }
102
103 async fn resolve_merge(&self, input: ImportInput) -> Result<ImportInput, DomainError> {
106 if input.overwrite != ImportOverwrite::Merge {
107 return Ok(input);
108 }
109 let Some(document_iri) = input.document_iri.clone() else {
110 return Ok(input);
111 };
112 match self
113 .graphs
114 .id_by_document_iri(document_iri.as_str())
115 .await?
116 {
117 Some(old_id) => {
118 let old_graph_iri = format!("{GRAPH_IRI_PREFIX}{}", old_id.as_uuid());
119 let old_triples = self
120 .triples
121 .triples_for_graph(Some(&old_graph_iri), GRAPH_READ_LIMIT)
122 .await?;
123 compose_merged_input(&old_triples, &input)
124 }
125 None => Ok(ImportInput {
126 overwrite: ImportOverwrite::Replace,
127 ..input
128 }),
129 }
130 }
131
132 pub async fn import_documents(
133 &self,
134 inputs: Vec<ImportInput>,
135 ) -> Result<Vec<ImportReport>, DomainError> {
136 if inputs.is_empty() {
137 return Ok(Vec::new());
138 }
139 let mut tx = self.pool.begin().await.map_err(db_err)?;
140 let mut reports = Vec::with_capacity(inputs.len());
141 for input in inputs {
142 reports.push(self.import_into_conn(&mut tx, input).await?);
143 }
144 tx.commit().await.map_err(db_err)?;
145 Ok(reports)
146 }
147
148 async fn import_into_conn(
149 &self,
150 conn: &mut sqlx::SqliteConnection,
151 input: ImportInput,
152 ) -> Result<ImportReport, DomainError> {
153 if input.overwrite == ImportOverwrite::Replace {
154 if let Some(document_iri) = &input.document_iri {
155 if let Some(old_id) = self
156 .graphs
157 .id_by_document_iri_in(&mut *conn, document_iri.as_str())
158 .await?
159 {
160 self.graphs.delete_in(&mut *conn, old_id).await?;
161 }
162 }
163 }
164
165 let plan = build_import_plan(&input)?;
166
167 self.graphs
168 .insert(&mut *conn, plan.graph_id, plan.new_graph)
169 .await?;
170 let triple_count = self
171 .triples
172 .insert_triples(&mut *conn, &plan.triples, "sbol")
173 .await?;
174 self.accel
175 .refresh_graph(&mut *conn, plan.graph_iri.as_str())
176 .await?;
177 let object_count = plan.summaries.len();
178 for summary in &plan.summaries {
179 self.objects
180 .upsert(&mut *conn, summary, Some(plan.graph_id))
181 .await?;
182 }
183
184 for sequence in &plan.projections.sequences {
187 self.sequences.upsert_sequence(&mut *conn, sequence).await?;
188 }
189
190 Ok(ImportReport {
191 graph_id: plan.graph_id,
192 object_count,
193 triple_count,
194 validation_status: plan.validation_status,
195 validation_issue_count: plan.validation_issue_count,
196 })
197 }
198
199 pub async fn graph_store_write(
200 &self,
201 graph: &str,
202 body: &str,
203 format: SerializationFormat,
204 mode: GraphWriteMode,
205 ) -> Result<usize, DomainError> {
206 let rdf_format = to_rdf_format(format)?;
207 let parsed = sbol_rdf::Graph::parse(body, rdf_format)
208 .map_err(|e| DomainError::Parse(e.to_string()))?;
209 let triples = rdf_graph_to_triples(&parsed, &IriString::unchecked(graph));
210
211 let mut tx = self.pool.begin().await.map_err(db_err)?;
212 self.triples
213 .ensure_graph(&mut tx, graph, "verbatim")
214 .await?;
215 if mode == GraphWriteMode::Replace {
216 self.triples.clear_graph(&mut tx, Some(graph)).await?;
217 }
218 let inserted = self
219 .triples
220 .insert_triples(&mut tx, &triples, "graph-store")
221 .await?;
222 self.accel.refresh_graph(&mut tx, graph).await?;
223 tx.commit().await.map_err(db_err)?;
224 Ok(inserted)
225 }
226
227 pub async fn graph_store_clear(&self, graph: &str) -> Result<usize, DomainError> {
228 let mut tx = self.pool.begin().await.map_err(db_err)?;
229 let deleted = self.triples.clear_graph(&mut tx, Some(graph)).await?;
230 self.triples.delete_graph(&mut tx, graph).await?;
231 self.accel.refresh_graph(&mut tx, graph).await?;
232 tx.commit().await.map_err(db_err)?;
233 Ok(deleted)
234 }
235
236 pub async fn graph_store_read(&self, graph: &str) -> Result<Vec<Triple>, DomainError> {
237 self.triples
238 .triples_for_graph(Some(graph), GRAPH_READ_LIMIT)
239 .await
240 }
241
242 pub async fn graph_store_read_page(
243 &self,
244 graph: &str,
245 after: Option<&str>,
246 limit: usize,
247 ) -> Result<TripleScanPage, DomainError> {
248 let after_id = after
249 .map(str::parse::<i64>)
250 .transpose()
251 .map_err(|_| DomainError::InvalidInput("invalid SQLite graph cursor".to_owned()))?
252 .unwrap_or(0);
253 let rows = self
254 .triples
255 .scan_graph_page(graph, after_id, i64::try_from(limit).unwrap_or(i64::MAX))
256 .await?;
257 let next_cursor = (rows.len() == limit)
258 .then(|| rows.last().map(|(id, _)| id.to_string()))
259 .flatten();
260 Ok(TripleScanPage {
261 items: rows.into_iter().map(|(_, triple)| triple).collect(),
262 next_cursor,
263 })
264 }
265}
266
267#[derive(Clone)]
272struct SqliteTripleSource {
273 triples: Arc<TripleRepository>,
274 accel: AccelRepository,
275}
276
277impl TripleSource for SqliteTripleSource {
278 fn scan_all_page(
279 &self,
280 after: Option<&str>,
281 limit: usize,
282 ) -> Result<TripleScanPage, DomainError> {
283 let after_id = after
284 .map(str::parse::<i64>)
285 .transpose()
286 .map_err(|_| DomainError::InvalidInput("invalid SQLite triple cursor".to_owned()))?
287 .unwrap_or(0);
288 let rows = Handle::current().block_on(
289 self.triples
290 .scan_all_page(after_id, i64::try_from(limit).unwrap_or(i64::MAX)),
291 )?;
292 let next_cursor = (rows.len() == limit)
293 .then(|| rows.last().map(|(id, _)| id.to_string()))
294 .flatten();
295 Ok(TripleScanPage {
296 items: rows.into_iter().map(|(_, triple)| triple).collect(),
297 next_cursor,
298 })
299 }
300
301 fn scan_pattern(
302 &self,
303 subject: Option<&PatternSubject>,
304 predicate: Option<&str>,
305 object: Option<&PatternObject>,
306 graph: Option<&GraphFilter>,
307 limit: i64,
308 ) -> Result<Vec<Triple>, DomainError> {
309 Handle::current().block_on(
310 self.triples
311 .scan_pattern(subject, predicate, object, graph, limit),
312 )
313 }
314
315 fn distinct_named_graphs(&self) -> Result<Vec<String>, DomainError> {
316 Handle::current().block_on(self.triples.distinct_named_graphs())
317 }
318
319 fn triples_for_graph(
320 &self,
321 graph: Option<&str>,
322 limit: i64,
323 ) -> Result<Vec<Triple>, DomainError> {
324 Handle::current().block_on(self.triples.triples_for_graph(graph, limit))
325 }
326
327 fn triples_for_subject(&self, subject_iri: &str) -> Result<Vec<Triple>, DomainError> {
328 Handle::current().block_on(self.triples.triples_for_subject(subject_iri))
329 }
330
331 fn run_accelerated(
332 &self,
333 query: &AcceleratedQuery,
334 ) -> Result<Option<AccelSolutions>, DomainError> {
335 Handle::current().block_on(self.accel.run(query)).map(Some)
336 }
337}
338
339#[derive(Clone)]
342struct SqliteTripleWriter {
343 triples: Arc<TripleRepository>,
344 accel: AccelRepository,
345 pool: SqlitePool,
346}
347
348fn touched_named_graphs(changes: &[TripleChange]) -> HashSet<String> {
352 let mut graphs = HashSet::new();
353 for change in changes {
354 match change {
355 TripleChange::Change { deletes, inserts } => {
356 for triple in deletes.iter().chain(inserts) {
357 if let Some(graph) = &triple.graph_iri {
358 graphs.insert(graph.as_str().to_owned());
359 }
360 }
361 }
362 TripleChange::Clear(graph) => {
363 if let Some(graph) = graph {
364 graphs.insert(graph.as_str().to_owned());
365 }
366 }
367 }
368 }
369 graphs
370}
371
372#[async_trait]
373impl TripleWriter for SqliteTripleWriter {
374 async fn apply_update(&self, changes: Vec<TripleChange>) -> Result<UpdateOutcome, DomainError> {
375 let mut outcome = UpdateOutcome::default();
376 let mut tx = self.pool.begin().await.map_err(db_err)?;
377 for change in &changes {
378 match change {
379 TripleChange::Change { deletes, inserts } => {
380 outcome.deleted += self.triples.delete_triples(&mut tx, deletes).await?;
381 let mut ensured = HashSet::new();
382 for triple in inserts {
383 if let Some(graph) = &triple.graph_iri {
384 if ensured.insert(graph.as_str().to_owned()) {
385 self.triples
386 .ensure_graph(&mut tx, graph.as_str(), "verbatim")
387 .await?;
388 }
389 }
390 }
391 outcome.inserted += self
392 .triples
393 .insert_triples(&mut tx, inserts, UPDATE_SOURCE)
394 .await?;
395 }
396 TripleChange::Clear(graph) => {
397 outcome.deleted += self
398 .triples
399 .clear_graph(&mut tx, graph.as_ref().map(|i| i.as_str()))
400 .await?;
401 }
402 }
403 }
404 for graph in touched_named_graphs(&changes) {
405 self.accel.refresh_graph(&mut tx, &graph).await?;
406 }
407 tx.commit().await.map_err(db_err)?;
408 Ok(outcome)
409 }
410}
411
412#[async_trait]
413impl ObjectStore for SqliteStore {
414 async fn get_object_by_iri(&self, iri: &str) -> Result<Option<SbolObjectRecord>, DomainError> {
415 self.objects.get_by_iri(iri).await
416 }
417
418 async fn get_objects_by_iris(
419 &self,
420 iris: &[&str],
421 ) -> Result<Vec<SbolObjectRecord>, DomainError> {
422 self.objects.get_by_iris(iris).await
423 }
424
425 async fn list_objects(
426 &self,
427 filter: &ListObjectsFilter,
428 ) -> Result<Vec<SbolObjectRecord>, DomainError> {
429 self.objects.list(filter).await
430 }
431
432 async fn get_object_iri_by_id(&self, id: ObjectId) -> Result<Option<String>, DomainError> {
433 self.objects.get_iri_by_id(id).await
434 }
435}
436
437#[async_trait]
438impl GraphStore for SqliteStore {
439 async fn get_graph(&self, id: GraphId) -> Result<Option<GraphRecord>, DomainError> {
440 self.graphs.get(id).await
441 }
442
443 async fn list_graphs(
444 &self,
445 filter: &ListGraphsFilter,
446 ) -> Result<Vec<GraphRecord>, DomainError> {
447 self.graphs.list(filter).await
448 }
449
450 async fn delete_graph(&self, id: GraphId) -> Result<bool, DomainError> {
451 self.graphs.delete(id).await
452 }
453
454 async fn graph_exists_by_hash(&self, hash: &[u8]) -> Result<bool, DomainError> {
455 self.graphs.exists_by_hash(hash).await
456 }
457
458 async fn graph_id_by_document_iri(
459 &self,
460 document_iri: &str,
461 ) -> Result<Option<GraphId>, DomainError> {
462 self.graphs.id_by_document_iri(document_iri).await
463 }
464}
465
466#[async_trait]
467impl TextSearchStore for SqliteStore {
468 async fn search_objects(
469 &self,
470 query: &TextSearchQuery,
471 ) -> Result<(Vec<SbolObjectRecord>, i64), DomainError> {
472 self.objects.search(query).await
473 }
474}
475
476#[async_trait]
477impl OntologyStore for SqliteStore {
478 async fn load_ontology_from_url(
479 &self,
480 prefix: &str,
481 name: &str,
482 source_url: &str,
483 ) -> Result<OntologyLoadReport, DomainError> {
484 let client = reqwest::Client::builder()
485 .user_agent("sbol-db/0.1 (+https://github.com/marpaia/sbol-db)")
486 .build()
487 .map_err(|e| DomainError::InvalidInput(format!("reqwest client: {e}")))?;
488 let body = client
489 .get(source_url)
490 .send()
491 .await
492 .map_err(|e| DomainError::InvalidInput(format!("fetch {source_url}: {e}")))?
493 .error_for_status()
494 .map_err(|e| DomainError::InvalidInput(format!("HTTP {source_url}: {e}")))?
495 .text()
496 .await
497 .map_err(|e| DomainError::InvalidInput(format!("decode {source_url}: {e}")))?;
498 self.ontology
499 .load_from_text(prefix, name, Some(source_url), &body)
500 .await
501 }
502
503 async fn load_ontology_from_text(
504 &self,
505 prefix: &str,
506 name: &str,
507 source_url: Option<&str>,
508 text: &str,
509 ) -> Result<OntologyLoadReport, DomainError> {
510 self.ontology
511 .load_from_text(prefix, name, source_url, text)
512 .await
513 }
514
515 async fn list_ontologies(&self) -> Result<Vec<OntologyRecord>, DomainError> {
516 self.ontology.list_ontologies().await
517 }
518
519 async fn canonicalize(&self, iri: &str) -> Result<Option<String>, DomainError> {
520 self.ontology.canonicalize(iri).await
521 }
522
523 async fn descendants(&self, iri: &str) -> Result<Vec<(String, i16)>, DomainError> {
524 self.ontology.descendants(iri).await
525 }
526
527 async fn list_ontology_terms(
528 &self,
529 prefix: &str,
530 limit: i64,
531 offset: i64,
532 search: Option<&str>,
533 ) -> Result<(Vec<OntologyTermRecord>, i64), DomainError> {
534 self.ontology
535 .list_terms(prefix, limit, offset, search)
536 .await
537 }
538
539 async fn get_ontology_term(
540 &self,
541 iri: &str,
542 ) -> Result<Option<OntologyTermRecord>, DomainError> {
543 self.ontology.get_term(iri).await
544 }
545}
546
547#[async_trait]
548impl NeighborhoodStore for SqliteStore {
549 async fn walk(&self, query: &NeighborhoodQuery) -> Result<NeighborhoodResult, DomainError> {
550 crate::repo::neighborhood::walk(&self.triples, &self.objects, query).await
551 }
552}
553
554#[async_trait]
555impl SequenceSearchStore for SqliteStore {
556 async fn search(
557 &self,
558 pattern: &str,
559 options: SequenceSearchOptions,
560 ) -> Result<Vec<SequenceMatch>, DomainError> {
561 self.sequences.search(pattern, options).await
562 }
563
564 async fn search_many(
565 &self,
566 patterns: &[String],
567 options: SequenceSearchOptions,
568 ) -> Result<Vec<BatchSequenceMatch>, DomainError> {
569 self.sequences.search_many(patterns, options).await
570 }
571
572 async fn align_candidates(&self, query: &str) -> Result<Vec<(String, String)>, DomainError> {
573 self.sequences.align_candidates(query).await
574 }
575
576 async fn sequences_by_iris(
577 &self,
578 iris: &[String],
579 ) -> Result<Vec<(String, String)>, DomainError> {
580 self.sequences.sequences_by_iris(iris).await
581 }
582
583 async fn all_nucleotide_sequences(&self) -> Result<Vec<(String, String)>, DomainError> {
584 self.sequences.all_nucleotide_sequences().await
585 }
586}
587
588#[async_trait]
589impl LabStore for SqliteStore {
590 async fn corpus_counts(&self) -> Result<CorpusCounts, DomainError> {
591 self.lab.corpus_counts().await
592 }
593
594 async fn recent_graphs(&self, limit: i64) -> Result<Vec<GraphOverview>, DomainError> {
595 self.lab.list_graph_overviews(None, limit, 0).await
596 }
597
598 async fn top_classes(&self, limit: i64) -> Result<Vec<ClassCount>, DomainError> {
599 self.lab.top_classes(limit).await
600 }
601
602 async fn count_graphs(&self, kind: Option<&str>) -> Result<i64, DomainError> {
603 self.lab.count_graphs(kind).await
604 }
605
606 async fn list_graph_overviews(
607 &self,
608 kind: Option<&str>,
609 limit: i64,
610 offset: i64,
611 ) -> Result<Vec<GraphOverview>, DomainError> {
612 self.lab.list_graph_overviews(kind, limit, offset).await
613 }
614
615 async fn get_graph_overview(&self, id: GraphId) -> Result<Option<GraphOverview>, DomainError> {
616 self.lab.get_graph_overview(id).await
617 }
618
619 async fn graph_triples(
620 &self,
621 id: GraphId,
622 limit: i64,
623 offset: i64,
624 ) -> Result<Option<GraphTriplesPage>, DomainError> {
625 self.lab.graph_triples(id, limit, offset).await
626 }
627}
628
629#[async_trait]
630impl ResourceCatalogStore for SqliteStore {
631 async fn catalog_resource(&self, iri: &str) -> Result<Option<ResourceRecord>, DomainError> {
632 self.accel.resource(iri).await
633 }
634
635 async fn catalog_resource_occurrences(
636 &self,
637 iri: &str,
638 ) -> Result<Vec<ResourceOccurrence>, DomainError> {
639 self.accel.resource_occurrences(iri).await
640 }
641
642 async fn catalog_resources(
643 &self,
644 query: &ResourceQuery,
645 ) -> Result<CursorPage<ResourceRecord>, DomainError> {
646 self.accel.resources(query).await
647 }
648
649 async fn catalog_resources_by_iris(
650 &self,
651 iris: &[String],
652 ) -> Result<Vec<ResourceRecord>, DomainError> {
653 self.accel.resources_for_iris(iris).await
654 }
655
656 async fn catalog_top_classes(&self, limit: u32) -> Result<Vec<ClassCount>, DomainError> {
657 self.lab.top_classes(i64::from(limit.clamp(1, 500))).await
658 }
659}
660
661#[async_trait]
662impl SequenceCatalogStore for SqliteStore {
663 async fn catalog_sequence(
664 &self,
665 iri: &str,
666 ) -> Result<Option<CatalogSequenceRecord>, DomainError> {
667 self.accel.sequence(iri).await
668 }
669
670 async fn catalog_sequences(
671 &self,
672 query: &SequenceQuery,
673 ) -> Result<CursorPage<CatalogSequenceRecord>, DomainError> {
674 self.accel.sequences(query).await
675 }
676}
677
678#[async_trait]
679impl CorpusStatsStore for SqliteStore {
680 async fn catalog_stats(&self) -> Result<CorpusStats, DomainError> {
681 Ok(self.lab.corpus_counts().await?.into())
682 }
683}
684
685#[async_trait]
686impl NamedGraphCatalogStore for SqliteStore {
687 async fn catalog_graph(&self, id: GraphId) -> Result<Option<NamedGraphRecord>, DomainError> {
688 Ok(self.lab.get_graph_overview(id).await?.map(Into::into))
689 }
690
691 async fn catalog_graphs(
692 &self,
693 query: &NamedGraphQuery,
694 ) -> Result<CursorPage<NamedGraphRecord>, DomainError> {
695 let limit = query.limit.clamp(1, 500) as i64;
696 let mut rows = self
697 .lab
698 .catalog_graphs(query.after.as_deref(), query.text.as_deref(), limit + 1)
699 .await?;
700 let has_more = rows.len() > limit as usize;
701 rows.truncate(limit as usize);
702 let next_cursor = has_more.then(|| rows.last().expect("non-empty graph page").iri.clone());
703 Ok(CursorPage {
704 items: rows.into_iter().map(Into::into).collect(),
705 next_cursor,
706 })
707 }
708
709 async fn catalog_graph_triples(
710 &self,
711 id: GraphId,
712 query: &TriplePageQuery,
713 ) -> Result<Option<CursorPage<Triple>>, DomainError> {
714 let Some(graph) = self.lab.get_graph_overview(id).await? else {
715 return Ok(None);
716 };
717 let page = SqliteStore::graph_store_read_page(
718 self,
719 &graph.iri,
720 query.after.as_deref(),
721 query.limit.clamp(1, 5_000) as usize,
722 )
723 .await?;
724 Ok(Some(CursorPage {
725 items: page.items,
726 next_cursor: page.next_cursor,
727 }))
728 }
729}
730
731#[async_trait]
732impl AclStore for SqliteStore {
733 async fn owned_graphs(&self, owner_iri: &str) -> Result<Vec<String>, DomainError> {
734 let object = PatternObject::Iri(owner_iri.to_owned());
735 let triples = self
736 .triples
737 .scan_pattern(None, Some(SBH_OWNED_BY), Some(&object), None, i64::MAX)
738 .await?;
739 Ok(distinct_graph_iris(triples))
740 }
741
742 async fn viewable_objects(&self, owner_iri: &str) -> Result<Vec<String>, DomainError> {
743 let subject = PatternSubject::Iri(owner_iri.to_owned());
744 let triples = self
745 .triples
746 .scan_pattern(Some(&subject), Some(SBH_CAN_VIEW), None, None, i64::MAX)
747 .await?;
748 Ok(distinct_object_iris(triples))
749 }
750}
751
752#[async_trait]
753impl SbolStore for SqliteStore {
754 async fn import_document(&self, input: ImportInput) -> Result<ImportReport, DomainError> {
755 self.import_document(input).await
756 }
757
758 async fn import_documents(
759 &self,
760 inputs: Vec<ImportInput>,
761 ) -> Result<Vec<ImportReport>, DomainError> {
762 self.import_documents(inputs).await
763 }
764
765 async fn graph_store_write(
766 &self,
767 graph: &str,
768 body: &str,
769 format: SerializationFormat,
770 mode: GraphWriteMode,
771 ) -> Result<usize, DomainError> {
772 self.graph_store_write(graph, body, format, mode).await
773 }
774
775 async fn graph_store_clear(&self, graph: &str) -> Result<usize, DomainError> {
776 self.graph_store_clear(graph).await
777 }
778
779 async fn graph_store_read(&self, graph: &str) -> Result<Vec<Triple>, DomainError> {
780 self.graph_store_read(graph).await
781 }
782
783 async fn graph_store_read_page(
784 &self,
785 graph: &str,
786 after: Option<&str>,
787 limit: usize,
788 ) -> Result<TripleScanPage, DomainError> {
789 self.graph_store_read_page(graph, after, limit).await
790 }
791
792 async fn triples_for_subject(&self, subject_iri: &str) -> Result<Vec<Triple>, DomainError> {
793 self.triples.triples_for_subject(subject_iri).await
794 }
795
796 async fn ping(&self) -> Result<(), DomainError> {
797 self.ping().await
798 }
799}