Skip to main content

rsynko_media/
selection.rs

1use crate::{FORMAT_EXTENSION, FORMAT_HAS_AUDIO, FORMAT_HAS_VIDEO, MediaSorts};
2use alux_ext::ext;
3use ambassador::delegatable_trait;
4
5/// Provides the carrier and primitive constructors for format predicates.
6#[delegatable_trait]
7pub trait FormatPredicateAlg: MediaSorts {
8    /// Defines the predicate accepting every format.
9    fn any_format(&self) -> Self::Predicate;
10    /// Defines the predicate accepting one format identity.
11    fn format_id(&self, id: impl Into<String>) -> Self::Predicate;
12    /// Defines the predicate accepting formats stating the named observation.
13    fn observed_format(&self, key: impl Into<String>) -> Self::Predicate;
14    /// Defines the predicate accepting one textual observation.
15    fn text_format(&self, key: impl Into<String>, value: impl Into<String>) -> Self::Predicate;
16    /// Defines the predicate accepting one stated truth.
17    fn flag_format(&self, key: impl Into<String>, value: bool) -> Self::Predicate;
18    /// Defines the predicate comparing one numeric observation.
19    fn number_format(&self, key: impl Into<String>, comparison: FormatComparison, value: f64) -> Self::Predicate;
20    /// Defines conjunction.
21    fn all_formats(&self, left: Self::Predicate, right: Self::Predicate) -> Self::Predicate;
22    /// Defines negation.
23    fn not_format(&self, predicate: Self::Predicate) -> Self::Predicate;
24}
25
26/// Provides the carrier and primitive constructors for selection programs.
27#[delegatable_trait]
28pub trait FormatSelectionAlg: MediaSorts {
29    /// Defines selection of the most preferred matching format.
30    fn best_format(&self, predicate: Self::Predicate) -> Self::Selection;
31    /// Defines selection of the least preferred matching format.
32    fn worst_format(&self, predicate: Self::Predicate) -> Self::Selection;
33    /// Defines ordered combination requiring every child selection.
34    fn merge_formats(&self, selections: impl IntoIterator<Item = Self::Selection>) -> Self::Selection;
35    /// Defines left-biased choice of the first successful child selection.
36    fn fallback_formats(&self, selections: impl IntoIterator<Item = Self::Selection>) -> Self::Selection;
37}
38
39/// Denotes how an observed number is compared against a stated one.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum FormatComparison {
42    /// Accepts an observation no greater than the stated number.
43    AtMost,
44    /// Accepts an observation equal to the stated number.
45    Exactly,
46    /// Accepts an observation no smaller than the stated number.
47    AtLeast,
48}
49
50/// Specifies interpretation of reified format predicates.
51///
52/// Matching is total over the reified predicate, so the derived meaning is the default and an
53/// interpreter implements this capability without restating it.
54#[delegatable_trait]
55pub trait FormatPredicateMatchAlg: MediaSorts {
56    /// Observes whether a format satisfies a predicate.
57    fn format_matches(&self, predicate: &Self::Predicate, format: &Self::Format) -> bool;
58}
59
60/// Specifies interpretation of a selection program against a preference-ordered catalog.
61///
62/// Interpreting reified syntax is an interpreter, not a derived operation: a program is walked, and
63/// what walking means belongs to whoever chose the selection sort.
64#[delegatable_trait]
65pub trait FormatSelectionApplyAlg: MediaSorts {
66    /// Chooses the formats one program denotes, in program order.
67    fn select_formats<'a>(
68        &self,
69        formats: &'a [Self::Format],
70        selection: &Self::Selection,
71    ) -> Option<Vec<&'a Self::Format>>;
72}
73
74/// Derives named and comparative predicates from primitive observation construction.
75#[ext(name = FormatPredicateExt)]
76pub impl<This> This
77where
78    This: FormatPredicateAlg,
79{
80    /// Defines formats containing audio.
81    fn has_audio(&self) -> This::Predicate {
82        self.flag_format(FORMAT_HAS_AUDIO, true)
83    }
84
85    /// Defines formats containing video.
86    fn has_video(&self) -> This::Predicate {
87        self.flag_format(FORMAT_HAS_VIDEO, true)
88    }
89
90    /// Defines formats stating one container or filename extension.
91    fn format_extension(&self, extension: impl Into<String>) -> This::Predicate {
92        self.text_format(FORMAT_EXTENSION, extension)
93    }
94
95    /// Defines formats whose observation is no greater than the stated number.
96    fn at_most(&self, key: impl Into<String>, value: f64) -> This::Predicate {
97        self.number_format(key, FormatComparison::AtMost, value)
98    }
99
100    /// Defines formats whose observation is no smaller than the stated number.
101    fn at_least(&self, key: impl Into<String>, value: f64) -> This::Predicate {
102        self.number_format(key, FormatComparison::AtLeast, value)
103    }
104
105    /// Defines formats whose observation equals the stated number.
106    fn exactly(&self, key: impl Into<String>, value: f64) -> This::Predicate {
107        self.number_format(key, FormatComparison::Exactly, value)
108    }
109
110    /// Defines formats whose observation is smaller than the stated number.
111    fn less_than(&self, key: impl Into<String>, value: f64) -> This::Predicate {
112        self.not_format(self.at_least(key, value))
113    }
114
115    /// Defines formats whose observation is greater than the stated number.
116    fn greater_than(&self, key: impl Into<String>, value: f64) -> This::Predicate {
117        self.not_format(self.at_most(key, value))
118    }
119
120    /// Defines formats containing both audio and video.
121    fn progressive_format(&self) -> This::Predicate {
122        self.all_formats(self.has_audio(), self.has_video())
123    }
124
125    /// Defines formats containing audio and no video.
126    fn audio_only_format(&self) -> This::Predicate {
127        self.all_formats(self.has_audio(), self.not_format(self.has_video()))
128    }
129
130    /// Defines formats containing video and no audio.
131    fn video_only_format(&self) -> This::Predicate {
132        self.all_formats(self.has_video(), self.not_format(self.has_audio()))
133    }
134}
135
136/// Derives common selection programs by equating predicate carriers.
137#[ext(name = FormatSelectionProgramExt)]
138pub impl<This, Predicate> This
139where
140    This: FormatPredicateAlg<Predicate = Predicate> + FormatSelectionAlg<Predicate = Predicate>,
141{
142    /// Defines the most preferred progressive format.
143    fn best_progressive_format(&self) -> This::Selection {
144        self.best_format(self.progressive_format())
145    }
146
147    /// Defines an exact format with a progressive fallback.
148    fn preferred_progressive_format(&self, id: impl Into<String>) -> This::Selection {
149        self.fallback_formats([self.best_format(self.format_id(id)), self.best_progressive_format()])
150    }
151}