gstreamer/format/
specific.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::translate::{FromGlib, GlibNoneError, IntoGlib, OptionIntoGlib, TryFromGlib};
4
5use super::{
6    Format, FormattedValue, FormattedValueError, FormattedValueFullRange, FormattedValueIntrinsic,
7    FormattedValueNoneBuilder, GenericFormattedValue,
8};
9use crate::ffi;
10
11pub trait SpecificFormattedValue: FormattedValue {}
12
13pub trait SpecificFormattedValueFullRange: FormattedValueFullRange {}
14
15// rustdoc-stripper-ignore-next
16/// A trait implemented on the intrinsic type of a `SpecificFormattedValue`.
17///
18/// # Examples
19///
20/// - `Undefined` is the intrinsic type for `Undefined`.
21/// - `Bytes` is the intrinsic type for `Option<Bytes>`.
22pub trait SpecificFormattedValueIntrinsic: TryFromGlib<i64> + FormattedValueIntrinsic {}
23
24#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)]
25pub struct Buffers(u64);
26impl Buffers {
27    #[doc(alias = "GST_BUFFER_OFFSET_NONE")]
28    pub const OFFSET_NONE: u64 = ffi::GST_BUFFER_OFFSET_NONE;
29    pub const MAX: Self = Self(Self::OFFSET_NONE - 1);
30}
31
32impl Buffers {
33    // rustdoc-stripper-ignore-next
34    /// Builds a new `Buffers` formatted value with the provided buffers count.
35    ///
36    /// # Panics
37    ///
38    /// Panics if the provided count equals `u64::MAX`,
39    /// which is reserved for `None` in C.
40    #[track_caller]
41    #[inline]
42    pub const fn from_u64(buffers: u64) -> Self {
43        if buffers == ffi::GST_BUFFER_OFFSET_NONE {
44            panic!("`Buffers` value out of range");
45        }
46
47        Buffers(buffers)
48    }
49
50    // rustdoc-stripper-ignore-next
51    /// Builds a new `Buffers` formatted value with the provided buffers count.
52    ///
53    /// # Panics
54    ///
55    /// Panics if the provided count equals `u64::MAX`,
56    /// which is reserved for `None` in C.
57    #[track_caller]
58    #[inline]
59    pub fn from_usize(buffers: usize) -> Self {
60        Buffers::from_u64(buffers.try_into().unwrap())
61    }
62}
63
64impl_common_ops_for_newtype_uint!(Buffers, u64);
65impl_signed_div_mul!(Buffers, u64);
66impl_signed_int_into_signed!(Buffers, u64);
67impl_format_value_traits!(Buffers, Buffers, Buffers, u64);
68option_glib_newtype_from_to!(Buffers, Buffers::OFFSET_NONE);
69glib_newtype_display!(Buffers, DisplayableOptionBuffers, Format::Buffers);
70
71impl TryFrom<Buffers> for usize {
72    type Error = std::num::TryFromIntError;
73
74    fn try_from(value: Buffers) -> Result<Self, Self::Error> {
75        value.0.try_into()
76    }
77}
78
79// FIXME `functions in traits cannot be const` (rustc 1.64.0)
80// rustdoc-stripper-ignore-next
81/// `Buffers` formatted value constructor trait.
82pub trait BuffersFormatConstructor {
83    // rustdoc-stripper-ignore-next
84    /// Builds a `Buffers` formatted value from `self`.
85    fn buffers(self) -> Buffers;
86}
87
88impl BuffersFormatConstructor for u64 {
89    #[track_caller]
90    #[inline]
91    fn buffers(self) -> Buffers {
92        Buffers::from_u64(self)
93    }
94}
95
96#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)]
97pub struct Bytes(u64);
98impl Bytes {
99    #[allow(non_upper_case_globals)]
100    // rustdoc-stripper-ignore-next
101    /// 1 kibibyte (1024).
102    #[allow(non_upper_case_globals)]
103    pub const KiB: Self = Self(1024);
104    // rustdoc-stripper-ignore-next
105    /// 1 mebibyte (1024 * 1024).
106    #[allow(non_upper_case_globals)]
107    pub const MiB: Self = Self(1024 * 1024);
108    // rustdoc-stripper-ignore-next
109    /// 1 gibibyte (1024 * 1024 * 1024).
110    #[allow(non_upper_case_globals)]
111    pub const GiB: Self = Self(1024 * 1024 * 1024);
112    pub const MAX: Self = Self(u64::MAX - 1);
113}
114
115impl Bytes {
116    // rustdoc-stripper-ignore-next
117    /// Builds a new `Bytes` formatted value with the provided bytes count.
118    ///
119    /// # Panics
120    ///
121    /// Panics if the provided count equals `u64::MAX`,
122    /// which is reserved for `None` in C.
123    #[track_caller]
124    #[inline]
125    pub const fn from_u64(bytes: u64) -> Self {
126        if bytes == u64::MAX {
127            panic!("`Bytes` value out of range");
128        }
129
130        Bytes(bytes)
131    }
132
133    // rustdoc-stripper-ignore-next
134    /// Builds a new `Bytes` formatted value with the provided bytes count.
135    ///
136    /// # Panics
137    ///
138    /// Panics if the provided count equals `u64::MAX`,
139    /// which is reserved for `None` in C.
140    #[track_caller]
141    #[inline]
142    pub fn from_usize(bytes: usize) -> Self {
143        // FIXME can't use `try_into` in `const` (rustc 1.64.0)
144        Bytes::from_u64(bytes.try_into().unwrap())
145    }
146}
147
148impl_common_ops_for_newtype_uint!(Bytes, u64);
149impl_signed_div_mul!(Bytes, u64);
150impl_signed_int_into_signed!(Bytes, u64);
151impl_format_value_traits!(Bytes, Bytes, Bytes, u64);
152option_glib_newtype_from_to!(Bytes, u64::MAX);
153glib_newtype_display!(Bytes, DisplayableOptionBytes, Format::Bytes);
154
155impl TryFrom<Bytes> for usize {
156    type Error = std::num::TryFromIntError;
157
158    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
159        value.0.try_into()
160    }
161}
162
163// FIXME `functions in traits cannot be const` (rustc 1.64.0)
164// rustdoc-stripper-ignore-next
165/// `Bytes` formatted value constructor trait.
166///
167/// These constructors use the [unambiguous conventions] for byte units.
168///
169/// [unambiguous conventions]: https://en.wikipedia.org/wiki/Byte#Multiple-byte_units
170pub trait BytesFormatConstructor {
171    // rustdoc-stripper-ignore-next
172    /// Builds a `Bytes` formatted value from `self`.
173    fn bytes(self) -> Bytes;
174
175    // rustdoc-stripper-ignore-next
176    /// Builds a `Bytes` formatted value from `self` interpreted as kibibytes (1024).
177    fn kibibytes(self) -> Bytes;
178
179    // rustdoc-stripper-ignore-next
180    /// Builds a `Bytes` formatted value from `self` interpreted as mebibytes (1024²).
181    fn mebibytes(self) -> Bytes;
182
183    // rustdoc-stripper-ignore-next
184    /// Builds a `Bytes` formatted value from `self` interpreted as gibibytes (1024³).
185    fn gibibytes(self) -> Bytes;
186}
187
188impl BytesFormatConstructor for u64 {
189    #[track_caller]
190    #[inline]
191    fn bytes(self) -> Bytes {
192        Bytes::from_u64(self)
193    }
194
195    #[track_caller]
196    #[inline]
197    fn kibibytes(self) -> Bytes {
198        Bytes::from_u64(self * 1024)
199    }
200
201    #[track_caller]
202    #[inline]
203    fn mebibytes(self) -> Bytes {
204        Bytes::from_u64(self * 1024 * 1024)
205    }
206
207    #[track_caller]
208    #[inline]
209    fn gibibytes(self) -> Bytes {
210        Bytes::from_u64(self * 1024 * 1024 * 1024)
211    }
212}
213
214#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)]
215pub struct Default(u64);
216impl Default {
217    pub const MAX: Self = Self(u64::MAX - 1);
218}
219
220impl Default {
221    // rustdoc-stripper-ignore-next
222    /// Builds a new `Default` formatted value with the provided quantity.
223    ///
224    /// # Panics
225    ///
226    /// Panics if the provided quantity equals `u64::MAX`,
227    /// which is reserved for `None` in C.
228    #[track_caller]
229    #[inline]
230    pub const fn from_u64(quantity: u64) -> Self {
231        if quantity == u64::MAX {
232            panic!("`Default` value out of range");
233        }
234
235        Default(quantity)
236    }
237
238    // rustdoc-stripper-ignore-next
239    /// Builds a new `Default` formatted value with the provided quantity.
240    ///
241    /// # Panics
242    ///
243    /// Panics if the provided quantity equals `u64::MAX`,
244    /// which is reserved for `None` in C.
245    #[track_caller]
246    #[inline]
247    pub fn from_usize(quantity: usize) -> Self {
248        // FIXME can't use `try_into` in `const` (rustc 1.64.0)
249        Default::from_u64(quantity.try_into().unwrap())
250    }
251}
252
253impl_common_ops_for_newtype_uint!(Default, u64);
254impl_signed_div_mul!(Default, u64);
255impl_signed_int_into_signed!(Default, u64);
256impl_format_value_traits!(Default, Default, Default, u64);
257option_glib_newtype_from_to!(Default, u64::MAX);
258glib_newtype_display!(Default, DisplayableOptionDefault, Format::Default);
259
260impl TryFrom<Default> for usize {
261    type Error = std::num::TryFromIntError;
262
263    fn try_from(value: Default) -> Result<Self, Self::Error> {
264        value.0.try_into()
265    }
266}
267
268// FIXME `functions in traits cannot be const` (rustc 1.64.0)
269// rustdoc-stripper-ignore-next
270/// `Default` formatted value constructor trait.
271pub trait DefaultFormatConstructor {
272    // rustdoc-stripper-ignore-next
273    /// Builds a `Default` formatted value from `self`.
274    fn default_format(self) -> Default;
275}
276
277impl DefaultFormatConstructor for u64 {
278    #[track_caller]
279    #[inline]
280    fn default_format(self) -> Default {
281        Default::from_u64(self)
282    }
283}
284
285pub type Time = super::ClockTime;
286
287#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)]
288pub struct Percent(u32);
289impl Percent {
290    #[doc(alias = "GST_FORMAT_PERCENT_MAX")]
291    pub const MAX: Self = Self(ffi::GST_FORMAT_PERCENT_MAX as u32);
292    #[doc(alias = "GST_FORMAT_PERCENT_SCALE")]
293    pub const SCALE: Self = Self(ffi::GST_FORMAT_PERCENT_SCALE as u32);
294
295    // rustdoc-stripper-ignore-next
296    /// Builds a new `Percent` with the provided percent value.
297    ///
298    /// # Panics
299    ///
300    /// Panics if the provided value is larger than 100.
301    #[track_caller]
302    #[inline]
303    pub const fn from_percent(percent: u32) -> Self {
304        if percent > 100 {
305            panic!("`Percent` value out of range");
306        }
307
308        Percent(ffi::GST_FORMAT_PERCENT_SCALE as u32 * percent)
309    }
310
311    // rustdoc-stripper-ignore-next
312    /// Builds a new `Percent` with the provided parts per million value.
313    ///
314    /// # Panics
315    ///
316    /// Panics if the provided value is larger than [`Self::MAX`].
317    #[track_caller]
318    #[inline]
319    pub const fn from_ppm(ppm: u32) -> Self {
320        if ppm > ffi::GST_FORMAT_PERCENT_MAX as u32 {
321            panic!("`Percent` ppm value out of range");
322        }
323
324        Percent(ppm)
325    }
326
327    // rustdoc-stripper-ignore-next
328    /// Builds a new `Percent` with the provided ratio.
329    ///
330    /// # Panics
331    ///
332    /// Panics if the provided radio is out of the range [0.0, 1.0].
333    #[track_caller]
334    #[inline]
335    pub fn from_ratio(ratio: f32) -> Self {
336        // FIXME floating point arithmetic is not allowed in constant functions (rustc 1.64.0)
337        Percent::try_from(ratio).expect("`Percent` ratio out of range")
338    }
339
340    // rustdoc-stripper-ignore-next
341    /// The percent value in the range [0, 100].
342    #[track_caller]
343    #[inline]
344    pub fn percent(&self) -> u32 {
345        self.0 / ffi::GST_FORMAT_PERCENT_SCALE as u32
346    }
347
348    // rustdoc-stripper-ignore-next
349    /// The per million value in the range [0, 1_000_000].
350    #[track_caller]
351    #[inline]
352    pub fn ppm(&self) -> u32 {
353        self.0
354    }
355
356    // rustdoc-stripper-ignore-next
357    /// The ratio value in the range [0.0, 1.0].
358    #[track_caller]
359    #[inline]
360    pub fn ratio(&self) -> f32 {
361        self.0 as f32 / ffi::GST_FORMAT_PERCENT_MAX as f32
362    }
363}
364
365impl_common_ops_for_newtype_uint!(Percent, u32, one: ffi::GST_FORMAT_PERCENT_SCALE as u32);
366impl_signed_div_mul!(Percent, u32);
367impl_signed_int_into_signed!(Percent, u32);
368
369impl FormattedValue for Option<Percent> {
370    type FullRange = Option<Percent>;
371
372    #[inline]
373    fn default_format() -> Format {
374        Format::Percent
375    }
376
377    #[inline]
378    fn format(&self) -> Format {
379        Format::Percent
380    }
381
382    #[inline]
383    fn is_some(&self) -> bool {
384        Option::is_some(self)
385    }
386
387    #[inline]
388    unsafe fn into_raw_value(self) -> i64 {
389        self.map_or(-1, |v| v.0 as i64)
390    }
391}
392
393impl FormattedValueFullRange for Option<Percent> {
394    #[inline]
395    unsafe fn from_raw(format: Format, value: i64) -> Self {
396        debug_assert_eq!(format, Format::Percent);
397        Percent::try_from_glib(value).ok()
398    }
399}
400
401impl From<Option<Percent>> for GenericFormattedValue {
402    #[inline]
403    fn from(v: Option<Percent>) -> Self {
404        skip_assert_initialized!();
405        GenericFormattedValue::Percent(v)
406    }
407}
408
409impl From<Percent> for GenericFormattedValue {
410    #[inline]
411    fn from(v: Percent) -> Self {
412        skip_assert_initialized!();
413        GenericFormattedValue::Percent(Some(v))
414    }
415}
416
417impl FormattedValue for Percent {
418    type FullRange = Option<Percent>;
419
420    #[inline]
421    fn default_format() -> Format {
422        Format::Percent
423    }
424
425    #[inline]
426    fn format(&self) -> Format {
427        Format::Percent
428    }
429
430    #[inline]
431    fn is_some(&self) -> bool {
432        true
433    }
434
435    #[inline]
436    unsafe fn into_raw_value(self) -> i64 {
437        self.0 as i64
438    }
439}
440
441impl TryFrom<u64> for Percent {
442    type Error = GlibNoneError;
443
444    #[inline]
445    fn try_from(v: u64) -> Result<Percent, GlibNoneError> {
446        skip_assert_initialized!();
447        unsafe { Self::try_from_glib(v as i64) }
448    }
449}
450
451impl TryFromGlib<i64> for Percent {
452    type Error = GlibNoneError;
453    #[inline]
454    unsafe fn try_from_glib(value: i64) -> Result<Self, Self::Error> {
455        skip_assert_initialized!();
456        if value < 0 || value > ffi::GST_FORMAT_PERCENT_MAX {
457            Err(GlibNoneError)
458        } else {
459            Ok(Percent(value as u32))
460        }
461    }
462}
463
464impl TryFrom<u32> for Percent {
465    type Error = FormattedValueError;
466
467    #[inline]
468    fn try_from(value: u32) -> Result<Self, Self::Error> {
469        skip_assert_initialized!();
470        if value > ffi::GST_FORMAT_PERCENT_MAX as u32 {
471            Err(FormattedValueError(Format::Percent))
472        } else {
473            Ok(Percent(value))
474        }
475    }
476}
477
478impl TryFrom<GenericFormattedValue> for Option<Percent> {
479    type Error = FormattedValueError;
480
481    #[inline]
482    fn try_from(v: GenericFormattedValue) -> Result<Option<Percent>, Self::Error> {
483        skip_assert_initialized!();
484        if let GenericFormattedValue::Percent(v) = v {
485            Ok(v)
486        } else {
487            Err(FormattedValueError(v.format()))
488        }
489    }
490}
491
492impl FormattedValueIntrinsic for Percent {}
493impl SpecificFormattedValue for Option<Percent> {}
494impl SpecificFormattedValueFullRange for Option<Percent> {}
495impl SpecificFormattedValueIntrinsic for Percent {}
496impl FormattedValueNoneBuilder for Option<Percent> {
497    #[inline]
498    fn none() -> Option<Percent> {
499        None
500    }
501}
502
503#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
504#[error("value out of range")]
505pub struct TryPercentFromFloatError(());
506
507impl TryFrom<f64> for Percent {
508    type Error = TryPercentFromFloatError;
509
510    #[inline]
511    fn try_from(v: f64) -> Result<Self, Self::Error> {
512        skip_assert_initialized!();
513        if v < 0.0 || v > 1.0 {
514            Err(TryPercentFromFloatError(()))
515        } else {
516            Ok(Percent(
517                (v * ffi::GST_FORMAT_PERCENT_MAX as f64).round() as u32
518            ))
519        }
520    }
521}
522
523impl TryFrom<f32> for Percent {
524    type Error = TryPercentFromFloatError;
525
526    #[inline]
527    fn try_from(v: f32) -> Result<Self, Self::Error> {
528        skip_assert_initialized!();
529        if v < 0.0 || v > 1.0 {
530            Err(TryPercentFromFloatError(()))
531        } else {
532            Ok(Percent(
533                (v * ffi::GST_FORMAT_PERCENT_MAX as f32).round() as u32
534            ))
535        }
536    }
537}
538
539// FIXME `functions in traits cannot be const` (rustc 1.64.0)
540// rustdoc-stripper-ignore-next
541/// `Percent` formatted value from integer constructor trait.
542pub trait PercentFormatIntegerConstructor {
543    // rustdoc-stripper-ignore-next
544    /// Builds a `Percent` formatted value from `self` interpreted as a percent.
545    fn percent(self) -> Percent;
546
547    // rustdoc-stripper-ignore-next
548    /// Builds a `Percent` formatted value from `self` interpreted as parts per million.
549    fn ppm(self) -> Percent;
550}
551
552impl PercentFormatIntegerConstructor for u32 {
553    #[track_caller]
554    #[inline]
555    fn percent(self) -> Percent {
556        Percent::from_percent(self)
557    }
558
559    #[track_caller]
560    #[inline]
561    fn ppm(self) -> Percent {
562        Percent::from_ppm(self)
563    }
564}
565
566// FIXME `functions in traits cannot be const` (rustc 1.64.0)
567// rustdoc-stripper-ignore-next
568/// `Percent` formatted value from float constructor trait.
569pub trait PercentFormatFloatConstructor {
570    // rustdoc-stripper-ignore-next
571    /// Builds a `Percent` formatted value from `self` interpreted as a ratio.
572    fn percent_ratio(self) -> Percent;
573}
574
575impl PercentFormatFloatConstructor for f32 {
576    #[track_caller]
577    #[inline]
578    fn percent_ratio(self) -> Percent {
579        Percent::try_from(self).unwrap()
580    }
581}
582
583impl std::fmt::Display for Percent {
584    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
585        std::fmt::Display::fmt(&(self.0 as f32 / (*Percent::SCALE) as f32), f)?;
586        f.write_str(" %")
587    }
588}
589
590impl crate::utils::Displayable for Percent {
591    type DisplayImpl = Self;
592    fn display(self) -> Self {
593        self
594    }
595}
596pub struct DisplayableOptionPercent(Option<Percent>);
597
598impl std::fmt::Display for DisplayableOptionPercent {
599    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
600        if let Some(val) = self.0.as_ref() {
601            std::fmt::Display::fmt(val, f)
602        } else {
603            f.write_str("undef. %")
604        }
605    }
606}
607
608impl crate::utils::Displayable for Option<Percent> {
609    type DisplayImpl = DisplayableOptionPercent;
610    fn display(self) -> Self::DisplayImpl {
611        DisplayableOptionPercent(self)
612    }
613}