1use std::error::Error;
10use std::fmt;
11use std::sync::Arc;
12
13use crate::access::{EngineNetwork, ReadOnlyNetwork, ReadOnlyViolation};
14use crate::configuration::AccessMode;
15use crate::link_network::{Link, LinkId, LinkMetadata, LinkNetwork, LinkType};
16
17#[derive(Debug)]
19pub enum StorageError {
20 ReadOnly(ReadOnlyViolation),
22 Io(std::io::Error),
24 Doublets(String),
26 Corrupt(String),
28}
29
30impl fmt::Display for StorageError {
31 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::ReadOnly(error) => error.fmt(formatter),
34 Self::Io(error) => write!(formatter, "storage I/O error: {error}"),
35 Self::Doublets(error) => write!(formatter, "doublets storage error: {error}"),
36 Self::Corrupt(error) => write!(formatter, "corrupt storage: {error}"),
37 }
38 }
39}
40
41impl Error for StorageError {
42 fn source(&self) -> Option<&(dyn Error + 'static)> {
43 match self {
44 Self::ReadOnly(error) => Some(error),
45 Self::Io(error) => Some(error),
46 Self::Doublets(_) | Self::Corrupt(_) => None,
47 }
48 }
49}
50
51impl From<ReadOnlyViolation> for StorageError {
52 fn from(error: ReadOnlyViolation) -> Self {
53 Self::ReadOnly(error)
54 }
55}
56
57impl From<std::io::Error> for StorageError {
58 fn from(error: std::io::Error) -> Self {
59 Self::Io(error)
60 }
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65pub enum LinkStoreBackend {
66 LinoProjection,
68 DoubletsRs,
70 DoubletsWeb,
72}
73
74impl LinkStoreBackend {
75 #[must_use]
77 pub const fn label(self) -> &'static str {
78 match self {
79 Self::LinoProjection => "LiNo projection",
80 Self::DoubletsRs => "doublets-rs",
81 Self::DoubletsWeb => "doublets-web",
82 }
83 }
84}
85
86#[derive(Clone, Debug, Default, PartialEq, Eq)]
88pub struct LinkStoreQuery {
89 id: Option<LinkId>,
90 references: Option<Vec<LinkId>>,
91 link_type: Option<LinkType>,
92 term: Option<String>,
93 language: Option<String>,
94 named: Option<bool>,
95}
96
97impl LinkStoreQuery {
98 #[must_use]
100 pub const fn new() -> Self {
101 Self {
102 id: None,
103 references: None,
104 link_type: None,
105 term: None,
106 language: None,
107 named: None,
108 }
109 }
110
111 #[must_use]
113 pub const fn with_id(mut self, id: LinkId) -> Self {
114 self.id = Some(id);
115 self
116 }
117
118 #[must_use]
120 pub fn with_references<I>(mut self, references: I) -> Self
121 where
122 I: IntoIterator<Item = LinkId>,
123 {
124 self.references = Some(references.into_iter().collect());
125 self
126 }
127
128 #[must_use]
130 pub const fn with_link_type(mut self, link_type: LinkType) -> Self {
131 self.link_type = Some(link_type);
132 self
133 }
134
135 #[must_use]
137 pub fn with_term(mut self, term: impl Into<String>) -> Self {
138 self.term = Some(term.into());
139 self
140 }
141
142 #[must_use]
144 pub fn with_language(mut self, language: impl Into<String>) -> Self {
145 self.language = Some(language.into());
146 self
147 }
148
149 #[must_use]
151 pub const fn with_named(mut self, named: bool) -> Self {
152 self.named = Some(named);
153 self
154 }
155
156 fn matches(&self, link: &Link) -> bool {
157 if self.id.is_some_and(|id| id != link.id()) {
158 return false;
159 }
160 if self
161 .references
162 .as_deref()
163 .is_some_and(|references| references != link.references())
164 {
165 return false;
166 }
167 if self
168 .link_type
169 .is_some_and(|link_type| Some(link_type) != link.metadata().link_type())
170 {
171 return false;
172 }
173 if self
174 .term
175 .as_deref()
176 .is_some_and(|term| Some(term) != link.metadata().term())
177 {
178 return false;
179 }
180 if self
181 .language
182 .as_deref()
183 .is_some_and(|language| Some(language) != link.metadata().language())
184 {
185 return false;
186 }
187 if self
188 .named
189 .is_some_and(|named| named != link.metadata().is_named())
190 {
191 return false;
192 }
193 true
194 }
195}
196
197pub trait LinkStore {
199 fn create(
205 &mut self,
206 references: &[LinkId],
207 metadata: LinkMetadata,
208 ) -> Result<LinkId, StorageError>;
209
210 fn read(&self, id: LinkId) -> Result<Option<Link>, StorageError>;
216
217 fn update(
223 &mut self,
224 id: LinkId,
225 references: &[LinkId],
226 metadata: LinkMetadata,
227 ) -> Result<bool, StorageError>;
228
229 fn delete(&mut self, id: LinkId) -> Result<bool, StorageError>;
235
236 fn search(&self, query: &LinkStoreQuery) -> Result<Vec<Link>, StorageError>;
242
243 fn count(&self, query: &LinkStoreQuery) -> Result<usize, StorageError> {
249 self.search(query).map(|links| links.len())
250 }
251}
252
253impl LinkStore for LinkNetwork {
254 fn create(
255 &mut self,
256 references: &[LinkId],
257 metadata: LinkMetadata,
258 ) -> Result<LinkId, StorageError> {
259 Ok(self.insert_dynamic_link(references, metadata))
260 }
261
262 fn read(&self, id: LinkId) -> Result<Option<Link>, StorageError> {
263 Ok(self.link(id).cloned())
264 }
265
266 fn update(
267 &mut self,
268 id: LinkId,
269 references: &[LinkId],
270 metadata: LinkMetadata,
271 ) -> Result<bool, StorageError> {
272 Ok(replace_network_link(self, id, references, metadata, true))
273 }
274
275 fn delete(&mut self, id: LinkId) -> Result<bool, StorageError> {
276 Ok(delete_network_link(self, id))
277 }
278
279 fn search(&self, query: &LinkStoreQuery) -> Result<Vec<Link>, StorageError> {
280 Ok(self
281 .links()
282 .filter(|link| query.matches(link))
283 .cloned()
284 .collect())
285 }
286}
287
288fn insert_network_link_with_id(
289 network: &mut LinkNetwork,
290 id: LinkId,
291 references: &[LinkId],
292 metadata: LinkMetadata,
293 registered_term: bool,
294) {
295 let term = registered_term
296 .then(|| metadata.term().map(Arc::<str>::from))
297 .flatten();
298 network.links.insert(
299 id,
300 Arc::new(Link {
301 id,
302 references: Arc::from(references.to_vec()),
303 metadata,
304 }),
305 );
306 if let Some(term) = term {
307 network.terms.insert(term, id);
308 }
309 network.next_id = network.next_id.max(id.as_u64() + 1);
310}
311
312fn replace_network_link(
313 network: &mut LinkNetwork,
314 id: LinkId,
315 references: &[LinkId],
316 metadata: LinkMetadata,
317 registered_term: bool,
318) -> bool {
319 if !network.links.contains_key(&id) {
320 return false;
321 }
322 network.terms.retain(|_, stored_id| *stored_id != id);
323 insert_network_link_with_id(network, id, references, metadata, registered_term);
324 true
325}
326
327fn delete_network_link(network: &mut LinkNetwork, id: LinkId) -> bool {
328 let removed = network.links.remove(&id).is_some();
329 if removed {
330 network.terms.retain(|_, stored_id| *stored_id != id);
331 }
332 removed
333}
334
335#[cfg(feature = "doublets")]
336fn network_from_stored_links(links: Vec<(Link, bool)>) -> LinkNetwork {
337 let mut network = LinkNetwork::new();
338 for (link, registered_term) in links {
339 insert_network_link_with_id(
340 &mut network,
341 link.id,
342 &link.references,
343 link.metadata,
344 registered_term,
345 );
346 }
347 network
348}
349
350impl LinkStore for ReadOnlyNetwork {
351 fn create(
352 &mut self,
353 _references: &[LinkId],
354 _metadata: LinkMetadata,
355 ) -> Result<LinkId, StorageError> {
356 Err(ReadOnlyViolation.into())
357 }
358
359 fn read(&self, id: LinkId) -> Result<Option<Link>, StorageError> {
360 LinkStore::read(self.network(), id)
361 }
362
363 fn update(
364 &mut self,
365 _id: LinkId,
366 _references: &[LinkId],
367 _metadata: LinkMetadata,
368 ) -> Result<bool, StorageError> {
369 Err(ReadOnlyViolation.into())
370 }
371
372 fn delete(&mut self, _id: LinkId) -> Result<bool, StorageError> {
373 Err(ReadOnlyViolation.into())
374 }
375
376 fn search(&self, query: &LinkStoreQuery) -> Result<Vec<Link>, StorageError> {
377 LinkStore::search(self.network(), query)
378 }
379}
380
381impl LinkStore for EngineNetwork {
382 fn create(
383 &mut self,
384 references: &[LinkId],
385 metadata: LinkMetadata,
386 ) -> Result<LinkId, StorageError> {
387 LinkStore::create(self.as_mutable()?, references, metadata)
388 }
389
390 fn read(&self, id: LinkId) -> Result<Option<Link>, StorageError> {
391 LinkStore::read(self.network(), id)
392 }
393
394 fn update(
395 &mut self,
396 id: LinkId,
397 references: &[LinkId],
398 metadata: LinkMetadata,
399 ) -> Result<bool, StorageError> {
400 LinkStore::update(self.as_mutable()?, id, references, metadata)
401 }
402
403 fn delete(&mut self, id: LinkId) -> Result<bool, StorageError> {
404 LinkStore::delete(self.as_mutable()?, id)
405 }
406
407 fn search(&self, query: &LinkStoreQuery) -> Result<Vec<Link>, StorageError> {
408 LinkStore::search(self.network(), query)
409 }
410}
411
412#[derive(Clone, Debug, PartialEq, Eq)]
414pub enum EngineLinkStore<S> {
415 Mutable(S),
417 ReadOnly(S),
419}
420
421impl<S> EngineLinkStore<S> {
422 #[must_use]
424 pub const fn with_access_mode(store: S, access_mode: AccessMode) -> Self {
425 match access_mode {
426 AccessMode::Mutable => Self::Mutable(store),
427 AccessMode::ReadOnly => Self::ReadOnly(store),
428 }
429 }
430
431 #[must_use]
433 pub const fn access_mode(&self) -> AccessMode {
434 match self {
435 Self::Mutable(_) => AccessMode::Mutable,
436 Self::ReadOnly(_) => AccessMode::ReadOnly,
437 }
438 }
439
440 #[must_use]
442 pub const fn store(&self) -> &S {
443 match self {
444 Self::Mutable(store) | Self::ReadOnly(store) => store,
445 }
446 }
447
448 pub fn into_inner(self) -> S {
450 match self {
451 Self::Mutable(store) | Self::ReadOnly(store) => store,
452 }
453 }
454}
455
456impl<S: LinkStore> LinkStore for EngineLinkStore<S> {
457 fn create(
458 &mut self,
459 references: &[LinkId],
460 metadata: LinkMetadata,
461 ) -> Result<LinkId, StorageError> {
462 match self {
463 Self::Mutable(store) => store.create(references, metadata),
464 Self::ReadOnly(_) => Err(ReadOnlyViolation.into()),
465 }
466 }
467
468 fn read(&self, id: LinkId) -> Result<Option<Link>, StorageError> {
469 self.store().read(id)
470 }
471
472 fn update(
473 &mut self,
474 id: LinkId,
475 references: &[LinkId],
476 metadata: LinkMetadata,
477 ) -> Result<bool, StorageError> {
478 match self {
479 Self::Mutable(store) => store.update(id, references, metadata),
480 Self::ReadOnly(_) => Err(ReadOnlyViolation.into()),
481 }
482 }
483
484 fn delete(&mut self, id: LinkId) -> Result<bool, StorageError> {
485 match self {
486 Self::Mutable(store) => store.delete(id),
487 Self::ReadOnly(_) => Err(ReadOnlyViolation.into()),
488 }
489 }
490
491 fn search(&self, query: &LinkStoreQuery) -> Result<Vec<Link>, StorageError> {
492 self.store().search(query)
493 }
494}
495
496#[cfg(feature = "doublets")]
497mod doublets;
498#[cfg(feature = "doublets")]
499pub use doublets::DoubletsLinkStore;