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, FormatPredicate,
5    FormatSelection, InfoRecord, InfoValue, Media, ProcessingProgram, ProcessingStep, ProcessorId, UrlReference,
6    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(&self, id: impl Into<String>, kind: ArtifactKind, metadata: Self::Metadata) -> Self::Artifact {
113        Artifact::new(id.into(), kind, metadata)
114    }
115}
116
117impl ExtractionAlg for MediaSyntax {
118    fn media(
119        &self,
120        id: impl Into<String>,
121        metadata: Self::Metadata,
122        formats: impl IntoIterator<Item = Self::Format>,
123    ) -> Self::Extraction {
124        Extraction::Media(Media::new(id.into(), metadata, formats.into_iter().collect()))
125    }
126
127    fn url_reference(
128        &self,
129        url: impl Into<String>,
130        extractor: Option<Self::Extractor>,
131        transparent: bool,
132    ) -> Self::Extraction {
133        Extraction::Url(UrlReference::new(url.into(), extractor, transparent))
134    }
135
136    fn extraction_collection(
137        &self,
138        id: Option<String>,
139        kind: CollectionKind,
140        metadata: Self::Metadata,
141        entries: impl IntoIterator<Item = Self::Extraction>,
142    ) -> Self::Extraction {
143        Extraction::Collection(Collection::new(id, kind, metadata, entries.into_iter().collect()))
144    }
145}
146
147impl ExtractionViewAlg for MediaSyntax {
148    fn as_media(&self, extraction: Extraction) -> Option<Media> {
149        match extraction {
150            Extraction::Media(media) => Some(media),
151            Extraction::Url(_) | Extraction::Collection(_) => None,
152        }
153    }
154}
155
156impl MediaViewAlg for MediaSyntax {
157    fn media_id<'a>(&self, media: &'a Media) -> &'a str {
158        &media.id
159    }
160
161    fn media_title<'a>(&self, media: &'a Media) -> Option<&'a str> {
162        media.metadata.get("title").and_then(InfoValue::as_text)
163    }
164
165    fn media_formats<'a>(&self, media: &'a Media) -> &'a [Format] {
166        &media.formats
167    }
168}
169
170impl FormatPredicateAlg for MediaSyntax {
171    fn any_format(&self) -> Self::Predicate {
172        FormatPredicate::Any
173    }
174    fn format_id(&self, id: impl Into<String>) -> Self::Predicate {
175        FormatPredicate::Id(id.into())
176    }
177    fn observed_format(&self, key: impl Into<String>) -> Self::Predicate {
178        FormatPredicate::Observed(key.into())
179    }
180    fn text_format(&self, key: impl Into<String>, value: impl Into<String>) -> Self::Predicate {
181        FormatPredicate::Text { key: key.into(), value: value.into() }
182    }
183    fn flag_format(&self, key: impl Into<String>, value: bool) -> Self::Predicate {
184        FormatPredicate::Flag { key: key.into(), value }
185    }
186    fn number_format(&self, key: impl Into<String>, comparison: FormatComparison, value: f64) -> Self::Predicate {
187        FormatPredicate::Number { key: key.into(), comparison, value }
188    }
189    fn all_formats(&self, left: Self::Predicate, right: Self::Predicate) -> Self::Predicate {
190        FormatPredicate::All(Box::new(left), Box::new(right))
191    }
192    fn not_format(&self, predicate: Self::Predicate) -> Self::Predicate {
193        FormatPredicate::Not(Box::new(predicate))
194    }
195}
196
197impl FormatSelectionAlg for MediaSyntax {
198    fn best_format(&self, predicate: Self::Predicate) -> Self::Selection {
199        FormatSelection::Best(predicate)
200    }
201    fn worst_format(&self, predicate: Self::Predicate) -> Self::Selection {
202        FormatSelection::Worst(predicate)
203    }
204    fn merge_formats(&self, selections: impl IntoIterator<Item = Self::Selection>) -> Self::Selection {
205        FormatSelection::Merge(selections.into_iter().collect())
206    }
207    fn fallback_formats(&self, selections: impl IntoIterator<Item = Self::Selection>) -> Self::Selection {
208        FormatSelection::Fallback(selections.into_iter().collect())
209    }
210}
211
212impl OutputNameAlg for MediaSyntax {
213    fn output_name(&self, component: String) -> Self::Output {
214        PathBuf::from(component)
215    }
216}
217
218impl ProcessingProgramAlg for ProcessingSyntax {
219    fn processor(&self, id: impl Into<String>) -> Self::Processor {
220        ProcessorId(id.into())
221    }
222
223    fn processing_step(&self, stage: ProcessingStage, processor: Self::Processor) -> Self::Step {
224        ProcessingStep { stage, processor }
225    }
226
227    fn empty_processing(&self) -> Self::Program {
228        ProcessingProgram::default()
229    }
230
231    fn processing(&self, steps: impl IntoIterator<Item = Self::Step>) -> Self::Program {
232        ProcessingProgram::from_steps(steps)
233    }
234
235    fn then_processing(&self, first: Self::Program, next: Self::Program) -> Self::Program {
236        first.then(next)
237    }
238}
239
240impl ProcessingProgramViewAlg for ProcessingSyntax {
241    fn processing_steps<'a>(program: &'a Self::Program) -> impl Iterator<Item = &'a Self::Step>
242    where
243        Self::Step: 'a,
244    {
245        program.steps()
246    }
247
248    fn processing_stage(step: &Self::Step) -> ProcessingStage {
249        step.stage
250    }
251}
252
253/// Reifies processing programs as ordered inspectable syntax.
254#[derive(Clone, Copy, Debug, Default)]
255pub struct ProcessingSyntax;
256
257impl ProcessingSorts for ProcessingSyntax {
258    type Processor = ProcessorId;
259    type Step = ProcessingStep;
260    type Program = ProcessingProgram;
261}
262
263/// Reifies download observations as inspectable values.
264#[derive(Clone, Copy, Debug, Default)]
265pub struct DownloadSyntax;
266
267impl DownloadObservationAlg for DownloadSyntax {
268    type Event = DownloadEvent;
269    type Progress = DownloadProgress;
270
271    fn download_progress(&self, destination: &Path, downloaded: u64, total: Option<u64>) -> Self::Progress {
272        DownloadProgress::new(destination.to_owned(), downloaded, total)
273    }
274
275    fn download_succeeded(&self, destination: &Path, bytes: u64) -> Self::Event {
276        DownloadEvent::Succeeded { destination: destination.to_owned(), bytes }
277    }
278
279    fn download_failed(&self, destination: &Path, message: String) -> Self::Event {
280        DownloadEvent::Failed { destination: destination.to_owned(), message }
281    }
282}
283
284impl FormatPredicateMatchAlg for MediaSyntax {
285    fn format_matches(&self, predicate: &FormatPredicate, format: &Format) -> bool {
286        predicate_accepts(predicate, format)
287    }
288}
289
290impl FormatSelectionApplyAlg for MediaSyntax {
291    fn select_formats<'a>(&self, formats: &'a [Format], selection: &FormatSelection) -> Option<Vec<&'a Format>> {
292        interpret_selection(self, formats, selection)
293    }
294}