1use 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#[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#[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#[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}