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