Skip to main content

rsynko_memory/
syntax.rs

1//! Reference interpreters that tie the specification sorts to inspectable Rust syntax.
2
3use crate::{
4    Artifact, Collection, DownloadEvent, DownloadProgress, Extraction, ExtractorKey, Format,
5    FormatPredicate, FormatSelection, InfoRecord, InfoValue, Media, ProcessingProgram,
6    ProcessingStep, ProcessorId, UrlReference, interpret_selection, predicate_accepts,
7};
8use rsynko_download::DownloadObservationAlg;
9use rsynko_media::*;
10use std::path::{Path, PathBuf};
11
12/// Reifies every media sort as inspectable Rust syntax.
13///
14/// One interpreter ties the whole knot: the sorts are shared, so the classes are only meaningful
15/// together and a single reification serves all of them.
16#[derive(Clone, Copy, Debug, Default)]
17pub struct MediaSyntax;
18
19impl MediaSorts for MediaSyntax {
20    type Value = InfoValue;
21    type Metadata = InfoRecord;
22    type Format = Format;
23    type Artifact = Artifact;
24    type Media = Media;
25    type Extraction = Extraction;
26    type Extractor = ExtractorKey;
27    type Predicate = FormatPredicate;
28    type Selection = FormatSelection;
29    type Output = PathBuf;
30}
31
32impl FormatViewAlg for MediaSyntax {
33    fn format_text<'a>(&self, format: &'a Format, key: &str) -> Option<&'a str> {
34        format.text(key)
35    }
36}
37
38impl MetadataViewAlg for MediaSyntax {
39    fn metadata_text<'a>(&self, metadata: &'a InfoRecord, key: &str) -> Option<&'a str> {
40        match metadata.get(key) {
41            Some(InfoValue::String(value)) => Some(value),
42            Some(
43                InfoValue::Null
44                | InfoValue::Bool(_)
45                | InfoValue::Integer(_)
46                | InfoValue::Float(_)
47                | InfoValue::List(_)
48                | InfoValue::Record(_),
49            )
50            | None => None,
51        }
52    }
53
54    fn metadata_number(&self, metadata: &InfoRecord, key: &str) -> Option<i64> {
55        match metadata.get(key) {
56            Some(InfoValue::Integer(value)) => Some(*value),
57            Some(
58                InfoValue::Null
59                | InfoValue::Bool(_)
60                | InfoValue::Float(_)
61                | InfoValue::String(_)
62                | InfoValue::List(_)
63                | InfoValue::Record(_),
64            )
65            | None => None,
66        }
67    }
68}
69
70impl MetadataAlg for MediaSyntax {
71    fn empty_metadata(&self) -> Self::Metadata {
72        InfoRecord::default()
73    }
74
75    fn null_metadata(&self) -> Self::Value {
76        InfoValue::Null
77    }
78    fn boolean_metadata(&self, value: bool) -> Self::Value {
79        InfoValue::Bool(value)
80    }
81    fn integer_metadata(&self, value: i64) -> Self::Value {
82        InfoValue::Integer(value)
83    }
84    fn float_metadata(&self, value: f64) -> Self::Value {
85        InfoValue::Float(value)
86    }
87    fn string_metadata(&self, value: impl Into<String>) -> Self::Value {
88        InfoValue::String(value.into())
89    }
90    fn list_metadata(&self, values: impl IntoIterator<Item = Self::Value>) -> Self::Value {
91        InfoValue::List(values.into_iter().collect())
92    }
93    fn record_metadata(&self, record: Self::Metadata) -> Self::Value {
94        InfoValue::Record(record)
95    }
96    fn metadata(&self, fields: impl IntoIterator<Item = (String, Self::Value)>) -> Self::Metadata {
97        let mut record = InfoRecord::default();
98        for (key, value) in fields {
99            record.insert(key, value);
100        }
101        record
102    }
103}
104
105impl FormatAlg for MediaSyntax {
106    fn format(&self, id: impl Into<String>, metadata: Self::Metadata) -> Self::Format {
107        Format::new(id.into(), metadata)
108    }
109}
110
111impl ArtifactAlg for MediaSyntax {
112    fn artifact(
113        &self,
114        id: impl Into<String>,
115        kind: ArtifactKind,
116        metadata: Self::Metadata,
117    ) -> Self::Artifact {
118        Artifact::new(id.into(), kind, metadata)
119    }
120}
121
122impl ExtractionAlg for MediaSyntax {
123    fn media(
124        &self,
125        id: impl Into<String>,
126        metadata: Self::Metadata,
127        formats: impl IntoIterator<Item = Self::Format>,
128    ) -> Self::Extraction {
129        Extraction::Media(Media::new(
130            id.into(),
131            metadata,
132            formats.into_iter().collect(),
133        ))
134    }
135
136    fn url_reference(
137        &self,
138        url: impl Into<String>,
139        extractor: Option<Self::Extractor>,
140        transparent: bool,
141    ) -> Self::Extraction {
142        Extraction::Url(UrlReference::new(url.into(), extractor, transparent))
143    }
144
145    fn extraction_collection(
146        &self,
147        id: Option<String>,
148        kind: CollectionKind,
149        metadata: Self::Metadata,
150        entries: impl IntoIterator<Item = Self::Extraction>,
151    ) -> Self::Extraction {
152        Extraction::Collection(Collection::new(
153            id,
154            kind,
155            metadata,
156            entries.into_iter().collect(),
157        ))
158    }
159}
160
161impl ExtractionViewAlg for MediaSyntax {
162    fn as_media(&self, extraction: Extraction) -> Option<Media> {
163        match extraction {
164            Extraction::Media(media) => Some(media),
165            Extraction::Url(_) | Extraction::Collection(_) => None,
166        }
167    }
168}
169
170impl MediaViewAlg for MediaSyntax {
171    fn media_id<'a>(&self, media: &'a Media) -> &'a str {
172        &media.id
173    }
174
175    fn media_title<'a>(&self, media: &'a Media) -> Option<&'a str> {
176        media.metadata.get("title").and_then(InfoValue::as_text)
177    }
178
179    fn media_formats<'a>(&self, media: &'a Media) -> &'a [Format] {
180        &media.formats
181    }
182}
183
184impl FormatPredicateAlg for MediaSyntax {
185    fn any_format(&self) -> Self::Predicate {
186        FormatPredicate::Any
187    }
188    fn format_id(&self, id: impl Into<String>) -> Self::Predicate {
189        FormatPredicate::Id(id.into())
190    }
191    fn observed_format(&self, key: impl Into<String>) -> Self::Predicate {
192        FormatPredicate::Observed(key.into())
193    }
194    fn text_format(&self, key: impl Into<String>, value: impl Into<String>) -> Self::Predicate {
195        FormatPredicate::Text {
196            key: key.into(),
197            value: value.into(),
198        }
199    }
200    fn flag_format(&self, key: impl Into<String>, value: bool) -> Self::Predicate {
201        FormatPredicate::Flag {
202            key: key.into(),
203            value,
204        }
205    }
206    fn number_format(
207        &self,
208        key: impl Into<String>,
209        comparison: FormatComparison,
210        value: f64,
211    ) -> Self::Predicate {
212        FormatPredicate::Number {
213            key: key.into(),
214            comparison,
215            value,
216        }
217    }
218    fn all_formats(&self, left: Self::Predicate, right: Self::Predicate) -> Self::Predicate {
219        FormatPredicate::All(Box::new(left), Box::new(right))
220    }
221    fn not_format(&self, predicate: Self::Predicate) -> Self::Predicate {
222        FormatPredicate::Not(Box::new(predicate))
223    }
224}
225
226impl FormatSelectionAlg for MediaSyntax {
227    fn best_format(&self, predicate: Self::Predicate) -> Self::Selection {
228        FormatSelection::Best(predicate)
229    }
230    fn worst_format(&self, predicate: Self::Predicate) -> Self::Selection {
231        FormatSelection::Worst(predicate)
232    }
233    fn merge_formats(
234        &self,
235        selections: impl IntoIterator<Item = Self::Selection>,
236    ) -> Self::Selection {
237        FormatSelection::Merge(selections.into_iter().collect())
238    }
239    fn fallback_formats(
240        &self,
241        selections: impl IntoIterator<Item = Self::Selection>,
242    ) -> Self::Selection {
243        FormatSelection::Fallback(selections.into_iter().collect())
244    }
245}
246
247impl OutputNameAlg for MediaSyntax {
248    fn output_name(&self, component: String) -> Self::Output {
249        PathBuf::from(component)
250    }
251}
252
253impl ProcessingProgramAlg for ProcessingSyntax {
254    fn processor(&self, id: impl Into<String>) -> Self::Processor {
255        ProcessorId(id.into())
256    }
257
258    fn processing_step(&self, stage: ProcessingStage, processor: Self::Processor) -> Self::Step {
259        ProcessingStep { stage, processor }
260    }
261
262    fn empty_processing(&self) -> Self::Program {
263        ProcessingProgram::default()
264    }
265
266    fn processing(&self, steps: impl IntoIterator<Item = Self::Step>) -> Self::Program {
267        ProcessingProgram::from_steps(steps)
268    }
269
270    fn then_processing(&self, first: Self::Program, next: Self::Program) -> Self::Program {
271        first.then(next)
272    }
273}
274
275impl ProcessingProgramViewAlg for ProcessingSyntax {
276    fn processing_steps<'a>(program: &'a Self::Program) -> impl Iterator<Item = &'a Self::Step>
277    where
278        Self::Step: 'a,
279    {
280        program.steps()
281    }
282
283    fn processing_stage(step: &Self::Step) -> ProcessingStage {
284        step.stage
285    }
286}
287
288/// Reifies processing programs as ordered inspectable syntax.
289#[derive(Clone, Copy, Debug, Default)]
290pub struct ProcessingSyntax;
291
292impl ProcessingSorts for ProcessingSyntax {
293    type Processor = ProcessorId;
294    type Step = ProcessingStep;
295    type Program = ProcessingProgram;
296}
297
298/// Reifies download observations as inspectable values.
299#[derive(Clone, Copy, Debug, Default)]
300pub struct DownloadSyntax;
301
302impl DownloadObservationAlg for DownloadSyntax {
303    type Event = DownloadEvent;
304    type Progress = DownloadProgress;
305
306    fn download_progress(
307        &self,
308        destination: &Path,
309        downloaded: u64,
310        total: Option<u64>,
311    ) -> Self::Progress {
312        DownloadProgress::new(destination.to_owned(), downloaded, total)
313    }
314
315    fn download_succeeded(&self, destination: &Path, bytes: u64) -> Self::Event {
316        DownloadEvent::Succeeded {
317            destination: destination.to_owned(),
318            bytes,
319        }
320    }
321
322    fn download_failed(&self, destination: &Path, message: String) -> Self::Event {
323        DownloadEvent::Failed {
324            destination: destination.to_owned(),
325            message,
326        }
327    }
328}
329
330impl FormatPredicateMatchAlg for MediaSyntax {
331    fn format_matches(&self, predicate: &FormatPredicate, format: &Format) -> bool {
332        predicate_accepts(predicate, format)
333    }
334}
335
336impl FormatSelectionApplyAlg for MediaSyntax {
337    fn select_formats<'a>(
338        &self,
339        formats: &'a [Format],
340        selection: &FormatSelection,
341    ) -> Option<Vec<&'a Format>> {
342        interpret_selection(self, formats, selection)
343    }
344}