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(
41 key: impl Into<String>,
42 accepted_prefix: impl Into<String>,
43 extraction: Extraction,
44 ) -> Self {
45 Self::new(
46 key,
47 [accepted_prefix],
48 ReferenceExtractionOutcome::Success(extraction),
49 )
50 }
51
52 #[must_use]
54 pub fn fails(
55 key: impl Into<String>,
56 accepted_prefix: impl Into<String>,
57 message: impl Into<String>,
58 ) -> Self {
59 Self::new(
60 key,
61 [accepted_prefix],
62 ReferenceExtractionOutcome::Failure(message.into()),
63 )
64 }
65}
66
67#[derive(Debug, Error, PartialEq, Eq)]
69pub enum ReferenceExtractionError {
70 #[error("unknown extractor {0:?}")]
72 UnknownExtractor(ExtractorKey),
73 #[error("extractor {extractor:?} failed: {message}")]
75 ExtractorFailed {
76 extractor: ExtractorKey,
78 message: String,
80 },
81}
82
83#[derive(Debug, Default)]
85pub struct ReferenceExtractorRegistry {
86 extractors: Vec<ReferenceExtractor>,
87 applications: RefCell<Vec<ExtractorKey>>,
88}
89
90impl ReferenceExtractorRegistry {
91 pub fn push(&mut self, extractor: ReferenceExtractor) {
93 self.extractors.push(extractor);
94 }
95
96 #[must_use]
98 pub fn applications(&self) -> Vec<ExtractorKey> {
99 self.applications.borrow().clone()
100 }
101}
102
103impl MediaSorts for ReferenceExtractorRegistry {
104 type Value = ();
105 type Metadata = ();
106 type Format = ();
107 type Artifact = ();
108 type Media = ();
109 type Extraction = Extraction;
110 type Extractor = ExtractorKey;
111 type Predicate = ();
112 type Selection = ();
113 type Output = ();
114}
115
116impl ExtractionCatalogAlg for ReferenceExtractorRegistry {
117 fn extractor_keys(&self) -> impl Iterator<Item = &ExtractorKey> {
118 self.extractors.iter().map(|extractor| &extractor.key)
119 }
120
121 fn extractor_accepts(&self, extractor: &ExtractorKey, url: &str) -> bool {
122 self.extractors
123 .iter()
124 .find(|candidate| candidate.key == *extractor)
125 .is_some_and(|candidate| {
126 candidate
127 .accepted_prefixes
128 .iter()
129 .any(|prefix| url.starts_with(prefix))
130 })
131 }
132}
133
134impl ExtractionApplyAlg for ReferenceExtractorRegistry {
135 type Error = ReferenceExtractionError;
136
137 fn extract_with(
138 &self,
139 extractor: &ExtractorKey,
140 _url: &str,
141 ) -> Result<Extraction, Self::Error> {
142 let candidate = self
143 .extractors
144 .iter()
145 .find(|candidate| candidate.key == *extractor)
146 .ok_or_else(|| ReferenceExtractionError::UnknownExtractor(extractor.clone()))?;
147 self.applications.borrow_mut().push(extractor.clone());
148 match &candidate.outcome {
149 ReferenceExtractionOutcome::Success(extraction) => Ok(extraction.clone()),
150 ReferenceExtractionOutcome::Failure(message) => {
151 Err(ReferenceExtractionError::ExtractorFailed {
152 extractor: extractor.clone(),
153 message: message.clone(),
154 })
155 }
156 }
157 }
158}