rsynko_memory/
extraction.rs1use crate::{Extraction, ExtractorKey};
2use rsynko_media::{ExtractionApplyAlg, ExtractionCatalogAlg, MediaSorts};
3use std::cell::RefCell;
4use thiserror::Error;
5
6#[derive(Clone, Debug, PartialEq)]
8pub enum ReferenceExtractionOutcome {
9 Success(Extraction),
11 Failure(String),
13}
14
15#[derive(Clone, Debug, PartialEq)]
17pub struct ReferenceExtractor {
18 key: ExtractorKey,
19 accepted_prefixes: Vec<String>,
20 outcome: ReferenceExtractionOutcome,
21}
22
23impl ReferenceExtractor {
24 #[must_use]
26 pub fn new(
27 key: impl Into<String>,
28 accepted_prefixes: impl IntoIterator<Item = impl Into<String>>,
29 outcome: ReferenceExtractionOutcome,
30 ) -> Self {
31 Self {
32 key: ExtractorKey::new(key),
33 accepted_prefixes: accepted_prefixes.into_iter().map(Into::into).collect(),
34 outcome,
35 }
36 }
37
38 #[must_use]
40 pub fn succeeds(key: impl Into<String>, accepted_prefix: impl Into<String>, extraction: Extraction) -> Self {
41 Self::new(key, [accepted_prefix], ReferenceExtractionOutcome::Success(extraction))
42 }
43
44 #[must_use]
46 pub fn fails(key: impl Into<String>, accepted_prefix: impl Into<String>, message: impl Into<String>) -> Self {
47 Self::new(key, [accepted_prefix], ReferenceExtractionOutcome::Failure(message.into()))
48 }
49}
50
51#[derive(Debug, Error, PartialEq, Eq)]
53pub enum ReferenceExtractionError {
54 #[error("unknown extractor {0:?}")]
56 UnknownExtractor(ExtractorKey),
57 #[error("extractor {extractor:?} failed: {message}")]
59 ExtractorFailed {
60 extractor: ExtractorKey,
62 message: String,
64 },
65}
66
67#[derive(Debug, Default)]
69pub struct ReferenceExtractorRegistry {
70 extractors: Vec<ReferenceExtractor>,
71 applications: RefCell<Vec<ExtractorKey>>,
72}
73
74impl ReferenceExtractorRegistry {
75 pub fn push(&mut self, extractor: ReferenceExtractor) {
77 self.extractors.push(extractor);
78 }
79
80 #[must_use]
82 pub fn applications(&self) -> Vec<ExtractorKey> {
83 self.applications.borrow().clone()
84 }
85}
86
87impl MediaSorts for ReferenceExtractorRegistry {
88 type Value = ();
89 type Metadata = ();
90 type Format = ();
91 type Artifact = ();
92 type Media = ();
93 type Extraction = Extraction;
94 type Extractor = ExtractorKey;
95 type Predicate = ();
96 type Selection = ();
97 type Output = ();
98}
99
100impl ExtractionCatalogAlg for ReferenceExtractorRegistry {
101 fn extractor_keys(&self) -> impl Iterator<Item = &ExtractorKey> {
102 self.extractors.iter().map(|extractor| &extractor.key)
103 }
104
105 fn extractor_accepts(&self, extractor: &ExtractorKey, url: &str) -> bool {
106 self.extractors
107 .iter()
108 .find(|candidate| candidate.key == *extractor)
109 .is_some_and(|candidate| candidate.accepted_prefixes.iter().any(|prefix| url.starts_with(prefix)))
110 }
111}
112
113impl ExtractionApplyAlg for ReferenceExtractorRegistry {
114 type Error = ReferenceExtractionError;
115
116 fn extract_with(&self, extractor: &ExtractorKey, _url: &str) -> Result<Extraction, Self::Error> {
117 let candidate = self
118 .extractors
119 .iter()
120 .find(|candidate| candidate.key == *extractor)
121 .ok_or_else(|| ReferenceExtractionError::UnknownExtractor(extractor.clone()))?;
122 self.applications.borrow_mut().push(extractor.clone());
123 match &candidate.outcome {
124 ReferenceExtractionOutcome::Success(extraction) => Ok(extraction.clone()),
125 ReferenceExtractionOutcome::Failure(message) => Err(ReferenceExtractionError::ExtractorFailed {
126 extractor: extractor.clone(),
127 message: message.clone(),
128 }),
129 }
130 }
131}