From eb4d997f0afb1c63d27f6fc698de465387f1bef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Laignel?= Date: Thu, 22 Sep 2022 23:35:07 +0200 Subject: [PATCH] gst/format: shuffle types and traits --- gstreamer/src/format/clock_time.rs | 107 ++- gstreamer/src/format/compatible.rs | 175 +++++ gstreamer/src/format/generic.rs | 202 +++++ gstreamer/src/format/macros.rs | 84 +- gstreamer/src/format/mod.rs | 1141 +--------------------------- gstreamer/src/format/signed.rs | 322 ++++++++ gstreamer/src/format/specific.rs | 263 +++++++ gstreamer/src/format/undefined.rs | 111 +++ 8 files changed, 1229 insertions(+), 1176 deletions(-) create mode 100644 gstreamer/src/format/compatible.rs create mode 100644 gstreamer/src/format/generic.rs create mode 100644 gstreamer/src/format/signed.rs create mode 100644 gstreamer/src/format/specific.rs create mode 100644 gstreamer/src/format/undefined.rs diff --git a/gstreamer/src/format/clock_time.rs b/gstreamer/src/format/clock_time.rs index d9a0347ae..e93bf4496 100644 --- a/gstreamer/src/format/clock_time.rs +++ b/gstreamer/src/format/clock_time.rs @@ -2,13 +2,16 @@ use glib::translate::*; use glib::StaticType; -use muldiv::MulDiv; -use num_integer::div_rem; -use opt_ops::prelude::*; + +use std::fmt; use std::io::{self, prelude::*}; -use std::ops; use std::time::Duration; -use std::{cmp, fmt, str}; + +use super::{Format, FormattedValueError, GenericFormattedValue, Signed}; +use super::{ + FormattedValue, FormattedValueFullRange, FormattedValueIntrinsic, FormattedValueNoneBuilder, + SpecificFormattedValue, SpecificFormattedValueFullRange, SpecificFormattedValueIntrinsic, +}; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] pub struct ClockTime(pub(crate) u64); @@ -115,6 +118,85 @@ impl ClockTime { } } +impl Signed { + // rustdoc-stripper-ignore-next + /// Returns the `self` in nanoseconds. + pub fn nseconds(self) -> Signed { + match self { + Signed::Positive(val) => Signed::Positive(val.nseconds()), + Signed::Negative(val) => Signed::Negative(val.nseconds()), + } + } + + // rustdoc-stripper-ignore-next + /// Creates new value from nanoseconds. + pub fn from_nseconds(val: Signed) -> Self { + skip_assert_initialized!(); + match val { + Signed::Positive(val) => Signed::Positive(ClockTime::from_nseconds(val)), + Signed::Negative(val) => Signed::Negative(ClockTime::from_nseconds(val)), + } + } + + // rustdoc-stripper-ignore-next + /// Returns the `self` in microseconds. + pub fn useconds(self) -> Signed { + match self { + Signed::Positive(val) => Signed::Positive(val.useconds()), + Signed::Negative(val) => Signed::Negative(val.useconds()), + } + } + + // rustdoc-stripper-ignore-next + /// Creates new value from microseconds. + pub fn from_useconds(val: Signed) -> Self { + skip_assert_initialized!(); + match val { + Signed::Positive(val) => Signed::Positive(ClockTime::from_useconds(val)), + Signed::Negative(val) => Signed::Negative(ClockTime::from_useconds(val)), + } + } + + // rustdoc-stripper-ignore-next + /// Returns the `self` in milliseconds. + pub fn mseconds(self) -> Signed { + match self { + Signed::Positive(val) => Signed::Positive(val.mseconds()), + Signed::Negative(val) => Signed::Negative(val.mseconds()), + } + } + + // rustdoc-stripper-ignore-next + /// Creates new value from milliseconds. + pub fn from_mseconds(val: Signed) -> Self { + skip_assert_initialized!(); + match val { + Signed::Positive(val) => Signed::Positive(ClockTime::from_mseconds(val)), + Signed::Negative(val) => Signed::Negative(ClockTime::from_mseconds(val)), + } + } + + // rustdoc-stripper-ignore-next + /// Returns the `self` in seconds. + pub fn seconds(self) -> Signed { + match self { + Signed::Positive(val) => Signed::Positive(val.seconds()), + Signed::Negative(val) => Signed::Negative(val.seconds()), + } + } + + // rustdoc-stripper-ignore-next + /// Creates new value from seconds. + pub fn from_seconds(val: Signed) -> Self { + skip_assert_initialized!(); + match val { + Signed::Positive(val) => Signed::Positive(ClockTime::from_seconds(val)), + Signed::Negative(val) => Signed::Negative(ClockTime::from_seconds(val)), + } + } +} + +impl_format_value_traits!(ClockTime, Time, Time, u64); option_glib_newtype_from_to!(ClockTime, ffi::GST_CLOCK_TIME_NONE); impl glib::value::ValueType for ClockTime { @@ -324,13 +406,13 @@ fn write_clocktime( precision: usize, ) -> io::Result<()> { skip_assert_initialized!(); - let precision = cmp::min(9, precision); + let precision = std::cmp::min(9, precision); if let Some(ns) = clocktime.map(ClockTime::nseconds) { // Split the time into parts - let (s, ns) = div_rem(ns, 1_000_000_000); - let (m, s) = div_rem(s, 60); - let (h, m) = div_rem(m, 60); + let (s, ns) = num_integer::div_rem(ns, 1_000_000_000); + let (m, s) = num_integer::div_rem(s, 60); + let (h, m) = num_integer::div_rem(m, 60); // Write HH:MM:SS write!(writer, "{}:{:02}:{:02}", h, m, s)?; @@ -340,7 +422,7 @@ fn write_clocktime( // The value is zero-padded so always 9 digits long let mut buf = [0u8; 9]; write!(&mut buf[..], "{:09}", ns).unwrap(); - let buf_str = str::from_utf8(&buf[..]).unwrap(); + let buf_str = std::str::from_utf8(&buf[..]).unwrap(); // Write decimal point and a prefix of the nanoseconds for more precision write!(writer, ".{:.p$}", buf_str, p = precision)?; @@ -372,7 +454,7 @@ fn fmt_opt_clock_time(ct: Option, f: &mut fmt::Formatter) -> fmt::Res let mut cursor = io::Cursor::new(&mut buf[..]); write_clocktime(&mut cursor, ct, precision).unwrap(); let pos = cursor.position() as usize; - let buf_str = str::from_utf8(&buf[..pos]).unwrap(); + let buf_str = std::str::from_utf8(&buf[..pos]).unwrap(); let sign = if ct.is_some() { Sign::NonNegative @@ -436,6 +518,7 @@ impl std::iter::Sum for ClockTime { mod tests { use super::*; use crate::{Signed, UnsignedIntoSigned}; + use opt_ops::prelude::*; const CT_1: ClockTime = ClockTime::from_nseconds(1); const CT_2: ClockTime = ClockTime::from_nseconds(2); @@ -729,6 +812,8 @@ mod tests { #[test] fn mul_div_ops() { + use muldiv::MulDiv; + assert_eq!(CT_1.mul_div_floor(7, 3), Some(CT_2)); assert_eq!(P_CT_1.mul_div_floor(7u64, 3), Some(P_CT_2)); diff --git a/gstreamer/src/format/compatible.rs b/gstreamer/src/format/compatible.rs new file mode 100644 index 000000000..0fef7d53d --- /dev/null +++ b/gstreamer/src/format/compatible.rs @@ -0,0 +1,175 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use super::{Format, FormattedValueError, GenericFormattedValue}; +use super::{FormattedValue, SpecificFormattedValue}; + +// rustdoc-stripper-ignore-next +/// A trait implemented on types which can hold [`FormattedValue`]s compatible with parameter `F`. +/// +/// This trait is auto-implemented based on [`FormattedValue`]s additional traits +/// such as [`SpecificFormattedValue`]. +/// +/// # Example: +/// +/// Consider the following function: +/// +/// ```rust +/// # use gstreamer::{ClockTime, CompatibleFormattedValue, FormattedValue, GenericFormattedValue}; +/// fn with_compatible_formats( +/// arg1: V, +/// arg2: impl CompatibleFormattedValue, +/// ) { +/// // This is required to access arg2 as a FormattedValue: +/// let _arg2 = arg2.try_into_checked(arg1).unwrap(); +/// } +/// +/// // This is Ok because arg1 is a ClockTime and arg2 is +/// // an Option which are compatible format-wise. +/// with_compatible_formats(ClockTime::ZERO, ClockTime::NONE); +/// +/// // This is Ok because arg1 is a ClockTime and arg2 is +/// // a GenericFormattedValue which are compatible format-wise. +/// with_compatible_formats( +/// ClockTime::ZERO, +/// GenericFormattedValue::Time(None), +/// ); +/// ``` +/// +/// Users are able to call the function with arguments: +/// +/// 1. of the same type (e.g. `ClockTime`), +/// 2. of different types, but able to hold a value of the same [`Format`] +/// (e.g. `ClockTime` and `Option`). +/// 3. One of a Formatted Value (specific or generic), the other being +/// a `GenericFormattedValue`. +/// +/// Format compatibility for cases 1 and 2 is enforced by +/// the type system, while case 3 will be checked at runtime time. +/// +/// ```compile_fail +/// # use gstreamer::{ClockTime, CompatibleFormattedValue, FormattedValue, format::Bytes}; +/// # fn with_compatible_formats( +/// # arg1: V, +/// # arg2: impl CompatibleFormattedValue, +/// # ) {} +/// // This doesn't compile because the arguments are not compatible: +/// let _ = with_compatible_formats(ClockTime::ZERO, Bytes(Some(42))); +/// ``` +/// +/// Note: users will not be able use `arg2` directly unless format +/// check succeeds: +/// +/// ```compile_fail +/// # use gstreamer::{CompatibleFormattedValue, FormattedValue}; +/// fn with_compatible_formats( +/// arg1: V, +/// arg2: impl CompatibleFormattedValue, +/// ) { +/// // This doesn't compile because arg2 hasn't been checked: +/// let _format = arg2.format(); +/// } +/// ``` +pub trait CompatibleFormattedValue { + type Original: FormattedValue; + + // rustdoc-stripper-ignore-next + /// Returns `Ok(self)` with its type restored if it is compatible with the format of `other`. + /// + /// When used with compatible [`SpecificFormattedValue`]s, checks + /// are enforced by the type system, no runtime checks are performed. + /// + /// When used with [`FormattedValue`] / [`GenericFormattedValue`] and + /// vice versa, a runtime format check is performed. If the check fails, + /// `Err(FormattedValueError)` is returned. + fn try_into_checked(self, other: V) -> Result; + + // rustdoc-stripper-ignore-next + /// Returns `Ok(self)` with its type restored if it is compatible with the format of `V`. + /// + /// When possible, prefer using [`Self::try_into_checked`] which + /// reduces the risk of missuse. + /// + /// When used with compatible [`SpecificFormattedValue`]s, checks + /// are enforced by the type system, no runtime checks are performed. + /// + /// When used with [`SpecificFormattedValue`] as a parameter and + /// a [`GenericFormattedValue`] as `Self`, a runtime check is perfomed + /// against the default format of the parameter. If the check fails, + /// `Err(FormattedValueError)` is returned. + /// + /// When used with [`GenericFormattedValue`] as a parameter and + /// a [`SpecificFormattedValue`] as `Self`, the `format` argument + /// used. If the check fails, `Err(FormattedValueError)` is returned. + fn try_into_checked_explicit( + self, + format: Format, + ) -> Result; +} + +impl CompatibleFormattedValue for T +where + V: SpecificFormattedValue, + T: SpecificFormattedValue, +{ + type Original = Self; + fn try_into_checked(self, _other: V) -> Result { + skip_assert_initialized!(); + Ok(self) + } + + fn try_into_checked_explicit( + self, + _format: Format, + ) -> Result { + skip_assert_initialized!(); + Ok(self) + } +} + +impl CompatibleFormattedValue for T { + type Original = Self; + fn try_into_checked(self, other: GenericFormattedValue) -> Result { + skip_assert_initialized!(); + if self.format() == other.format() { + Ok(self) + } else { + Err(FormattedValueError(self.format())) + } + } + + fn try_into_checked_explicit( + self, + format: Format, + ) -> Result { + skip_assert_initialized!(); + if self.format() == format { + Ok(self) + } else { + Err(FormattedValueError(self.format())) + } + } +} + +impl CompatibleFormattedValue for GenericFormattedValue { + type Original = Self; + fn try_into_checked(self, _other: V) -> Result { + skip_assert_initialized!(); + if self.format() == V::default_format() { + Ok(self) + } else { + Err(FormattedValueError(self.format())) + } + } + + fn try_into_checked_explicit( + self, + _format: Format, + ) -> Result { + skip_assert_initialized!(); + if self.format() == V::default_format() { + Ok(self) + } else { + Err(FormattedValueError(self.format())) + } + } +} diff --git a/gstreamer/src/format/generic.rs b/gstreamer/src/format/generic.rs new file mode 100644 index 000000000..d9d5e7848 --- /dev/null +++ b/gstreamer/src/format/generic.rs @@ -0,0 +1,202 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::FromGlib; +use std::fmt; + +use crate::utils::Displayable; + +use super::{ + Buffers, Bytes, ClockTime, Default, Format, FormattedValueError, Percent, Signed, Undefined, +}; +use super::{ + CompatibleFormattedValue, FormattedValue, FormattedValueFullRange, FormattedValueIntrinsic, + FormattedValueNoneBuilder, UnsignedIntoSigned, +}; + +#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum GenericFormattedValue { + Undefined(Undefined), + Default(Option), + Bytes(Option), + Time(Option), + Buffers(Option), + Percent(Option), + Other(Format, i64), +} + +impl fmt::Display for GenericFormattedValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Undefined(val) => val.fmt(f), + Self::Default(val) => val.display().fmt(f), + Self::Bytes(val) => val.display().fmt(f), + Self::Time(val) => val.display().fmt(f), + Self::Buffers(val) => val.display().fmt(f), + Self::Percent(val) => val.display().fmt(f), + Self::Other(format, val) => write!(f, "{} ({:?})", val, format), + } + } +} + +impl GenericFormattedValue { + pub fn new(format: Format, value: i64) -> Self { + skip_assert_initialized!(); + match format { + Format::Undefined => Self::Undefined(Undefined(value)), + Format::Default => Self::Default(unsafe { FromGlib::from_glib(value as u64) }), + Format::Bytes => Self::Bytes(unsafe { FromGlib::from_glib(value as u64) }), + Format::Time => Self::Time(unsafe { FromGlib::from_glib(value as u64) }), + Format::Buffers => Self::Buffers(unsafe { FromGlib::from_glib(value as u64) }), + Format::Percent => { + Self::Percent(unsafe { FormattedValueFullRange::from_raw(format, value) }) + } + Format::__Unknown(_) => Self::Other(format, value), + } + } + + #[doc(alias = "get_format")] + pub fn format(&self) -> Format { + match *self { + Self::Undefined(_) => Format::Undefined, + Self::Default(_) => Format::Default, + Self::Bytes(_) => Format::Bytes, + Self::Time(_) => Format::Time, + Self::Buffers(_) => Format::Buffers, + Self::Percent(_) => Format::Percent, + Self::Other(f, _) => f, + } + } + + #[doc(alias = "get_value")] + pub fn value(&self) -> i64 { + unsafe { + match *self { + Self::Undefined(v) => v.0, + Self::Default(v) => v.into_raw_value(), + Self::Bytes(v) => v.into_raw_value(), + Self::Time(v) => v.into_raw_value(), + Self::Buffers(v) => v.into_raw_value(), + Self::Percent(v) => v.into_raw_value(), + Self::Other(_, v) => v, + } + } + } +} + +impl FormattedValue for GenericFormattedValue { + // The intrinsic value for `GenericFormattedValue` is also + // `GenericFormattedValue`. We can't dissociate the `Option` + // from the variants' inner type since they are not all `Option`s. + type FullRange = GenericFormattedValue; + + fn default_format() -> Format { + Format::Undefined + } + + fn format(&self) -> Format { + self.format() + } + + fn is_some(&self) -> bool { + match self { + Self::Undefined(_) => true, + Self::Default(v) => v.is_some(), + Self::Bytes(v) => v.is_some(), + Self::Time(v) => v.is_some(), + Self::Buffers(v) => v.is_some(), + Self::Percent(v) => v.is_some(), + Self::Other(..) => true, + } + } + + unsafe fn into_raw_value(self) -> i64 { + self.value() + } +} + +impl FormattedValueFullRange for GenericFormattedValue { + unsafe fn from_raw(format: Format, value: i64) -> Self { + GenericFormattedValue::new(format, value) + } +} + +impl FormattedValueIntrinsic for GenericFormattedValue {} + +impl FormattedValueNoneBuilder for GenericFormattedValue { + #[track_caller] + fn none() -> Self { + panic!(concat!( + "`GenericFormattedValue` can't build `None` without knowing", + "the target format. Use `GenericFormattedValue::none_for_format`", + )); + } + + #[track_caller] + fn none_for_format(format: Format) -> Self { + skip_assert_initialized!(); + match format { + Format::Undefined => panic!("`None` can't be represented by `Undefined`"), + Format::Default => Self::Default(None), + Format::Bytes => Self::Bytes(None), + Format::Time => Self::Time(None), + Format::Buffers => Self::Buffers(None), + Format::Percent => Self::Percent(None), + Format::__Unknown(_) => panic!("`None` can't be represented by `__Unknown`"), + } + } +} + +impl UnsignedIntoSigned for GenericFormattedValue { + type Signed = Signed; + + #[track_caller] + fn into_positive(self) -> Signed { + match self { + GenericFormattedValue::Undefined(_) => { + unimplemented!("`GenericFormattedValue::Undefined` is already signed") + } + GenericFormattedValue::Other(..) => { + unimplemented!("`GenericFormattedValue::Other` is already signed") + } + unsigned_inner => Signed::Positive(unsigned_inner), + } + } + + #[track_caller] + fn into_negative(self) -> Signed { + match self { + GenericFormattedValue::Undefined(_) => { + unimplemented!("`GenericFormattedValue::Undefined` is already signed") + } + GenericFormattedValue::Other(..) => { + unimplemented!("`GenericFormattedValue::Other` is already signed") + } + unsigned_inner => Signed::Negative(unsigned_inner), + } + } +} + +impl CompatibleFormattedValue for GenericFormattedValue { + type Original = Self; + fn try_into_checked(self, other: GenericFormattedValue) -> Result { + skip_assert_initialized!(); + if self.format() == other.format() { + Ok(self) + } else { + Err(FormattedValueError(self.format())) + } + } + + fn try_into_checked_explicit( + self, + format: Format, + ) -> Result { + skip_assert_initialized!(); + if self.format() == format { + Ok(self) + } else { + Err(FormattedValueError(self.format())) + } + } +} diff --git a/gstreamer/src/format/macros.rs b/gstreamer/src/format/macros.rs index cc9a08359..376360908 100644 --- a/gstreamer/src/format/macros.rs +++ b/gstreamer/src/format/macros.rs @@ -2,7 +2,7 @@ macro_rules! impl_trait_op_same( ($name:ident, $op:ident, $op_name:ident, $op_assign:ident, $op_assign_name:ident) => { - impl ops::$op<$name> for $name { + impl std::ops::$op<$name> for $name { type Output = Self; fn $op_name(self, rhs: $name) -> Self::Output { @@ -10,7 +10,7 @@ macro_rules! impl_trait_op_same( } } - impl ops::$op_assign<$name> for $name { + impl std::ops::$op_assign<$name> for $name { fn $op_assign_name(&mut self, rhs: $name) { self.0.$op_assign_name(rhs.0) } @@ -98,7 +98,7 @@ macro_rules! impl_non_trait_op_same( macro_rules! impl_trait_op_inner_type( ($name:ident, $inner_type:ty, $op:ident, $op_name:ident, $op_assign:ident, $op_assign_name:ident) => { - impl ops::$op<$inner_type> for $name { + impl std::ops::$op<$inner_type> for $name { type Output = $name; fn $op_name(self, rhs: $inner_type) -> Self::Output { @@ -106,7 +106,7 @@ macro_rules! impl_trait_op_inner_type( } } - impl ops::$op<$name> for $inner_type { + impl std::ops::$op<$name> for $inner_type { type Output = $name; fn $op_name(self, rhs: $name) -> $name { @@ -114,7 +114,7 @@ macro_rules! impl_trait_op_inner_type( } } - impl ops::$op_assign<$inner_type> for $name { + impl std::ops::$op_assign<$inner_type> for $name { fn $op_assign_name(&mut self, rhs: $inner_type) { self.0.$op_assign_name(rhs) } @@ -250,7 +250,7 @@ macro_rules! impl_common_ops_for_newtype_uint( impl_unsigned_int_into_signed!($name); impl_signed_ops!($name, $inner_type, $name::ZERO); - impl MulDiv<$inner_type> for $name { + impl muldiv::MulDiv<$inner_type> for $name { type Output = $name; fn mul_div_floor(self, num: $inner_type, denom: $inner_type) -> Option { @@ -272,9 +272,9 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionOperations for $name {} + impl opt_ops::OptionOperations for $name {} - impl OptionCheckedAdd for $name { + impl opt_ops::OptionCheckedAdd for $name { type Output = Self; fn opt_checked_add( self, @@ -286,14 +286,14 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionSaturatingAdd for $name { + impl opt_ops::OptionSaturatingAdd for $name { type Output = Self; fn opt_saturating_add(self, rhs: Self) -> Option { Some(self.saturating_add(rhs)) } } - impl OptionOverflowingAdd for $name { + impl opt_ops::OptionOverflowingAdd for $name { type Output = Self; fn opt_overflowing_add(self, rhs: Self) -> Option<(Self::Output, bool)> { let res = self.overflowing_add(rhs); @@ -301,14 +301,14 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionWrappingAdd for $name { + impl opt_ops::OptionWrappingAdd for $name { type Output = Self; fn opt_wrapping_add(self, rhs: Self) -> Option { Some(self.wrapping_add(rhs)) } } - impl OptionCheckedDiv<$inner_type> for $name { + impl opt_ops::OptionCheckedDiv<$inner_type> for $name { type Output = Self; fn opt_checked_div(self, rhs: $inner_type) -> Result, opt_ops::Error> { if rhs == 0 { @@ -321,7 +321,7 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionCheckedMul<$inner_type> for $name { + impl opt_ops::OptionCheckedMul<$inner_type> for $name { type Output = Self; fn opt_checked_mul( self, @@ -333,14 +333,14 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionSaturatingMul<$inner_type> for $name { + impl opt_ops::OptionSaturatingMul<$inner_type> for $name { type Output = Self; fn opt_saturating_mul(self, rhs: $inner_type) -> Option { Some(self.saturating_mul(rhs)) } } - impl OptionOverflowingMul<$inner_type> for $name { + impl opt_ops::OptionOverflowingMul<$inner_type> for $name { type Output = Self; fn opt_overflowing_mul(self, rhs: $inner_type) -> Option<(Self::Output, bool)> { let res = self.overflowing_mul(rhs); @@ -348,14 +348,14 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionWrappingMul<$inner_type> for $name { + impl opt_ops::OptionWrappingMul<$inner_type> for $name { type Output = Self; fn opt_wrapping_mul(self, rhs: $inner_type) -> Option { Some(self.wrapping_mul(rhs)) } } - impl OptionCheckedRem<$inner_type> for $name { + impl opt_ops::OptionCheckedRem<$inner_type> for $name { type Output = Self; fn opt_checked_rem(self, rhs: $inner_type) -> Result, opt_ops::Error> { if rhs == 0 { @@ -368,7 +368,7 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionCheckedSub for $name { + impl opt_ops::OptionCheckedSub for $name { type Output = Self; fn opt_checked_sub( self, @@ -380,14 +380,14 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionSaturatingSub for $name { + impl opt_ops::OptionSaturatingSub for $name { type Output = Self; fn opt_saturating_sub(self, rhs: Self) -> Option { Some(self.saturating_sub(rhs)) } } - impl OptionOverflowingSub for $name { + impl opt_ops::OptionOverflowingSub for $name { type Output = Self; fn opt_overflowing_sub(self, rhs: Self) -> Option<(Self::Output, bool)> { let res = self.overflowing_sub(rhs); @@ -395,7 +395,7 @@ macro_rules! impl_common_ops_for_newtype_uint( } } - impl OptionWrappingSub for $name { + impl opt_ops::OptionWrappingSub for $name { type Output = Self; fn opt_wrapping_sub(self, rhs: Self) -> Option { Some(self.wrapping_sub(rhs)) @@ -577,25 +577,25 @@ macro_rules! impl_signed_ops( } } - impl PartialOrd> for crate::Signed<$type> { + impl std::cmp::PartialOrd> for crate::Signed<$type> { fn partial_cmp(&self, other: &crate::Signed<$type>) -> Option { Some(self.cmp(other)) } } - impl PartialEq<$type> for crate::Signed<$type> { + impl std::cmp::PartialEq<$type> for crate::Signed<$type> { fn eq(&self, other: &$type) -> bool { self.eq(&crate::Signed::Positive(*other)) } } - impl PartialOrd<$type> for crate::Signed<$type> { + impl std::cmp::PartialOrd<$type> for crate::Signed<$type> { fn partial_cmp(&self, other: &$type) -> Option { Some(self.cmp(&crate::Signed::Positive(*other))) } } - impl Ord for crate::Signed<$type> { + impl std::cmp::Ord for crate::Signed<$type> { fn cmp(&self, other: &crate::Signed<$type>) -> std::cmp::Ordering { use crate::Signed::*; match (self, other) { @@ -607,9 +607,9 @@ macro_rules! impl_signed_ops( } } - impl OptionOperations for crate::Signed<$type> {} + impl opt_ops::OptionOperations for crate::Signed<$type> {} - impl OptionCheckedAdd for crate::Signed<$type> { + impl opt_ops::OptionCheckedAdd for crate::Signed<$type> { type Output = Self; fn opt_checked_add( self, @@ -621,14 +621,14 @@ macro_rules! impl_signed_ops( } } - impl OptionSaturatingAdd for crate::Signed<$type> { + impl opt_ops::OptionSaturatingAdd for crate::Signed<$type> { type Output = Self; fn opt_saturating_add(self, rhs: Self) -> Option { Some(self.saturating_add(rhs)) } } - impl OptionCheckedSub for crate::Signed<$type> { + impl opt_ops::OptionCheckedSub for crate::Signed<$type> { type Output = Self; fn opt_checked_sub( self, @@ -640,7 +640,7 @@ macro_rules! impl_signed_ops( } } - impl OptionSaturatingSub for crate::Signed<$type> { + impl opt_ops::OptionSaturatingSub for crate::Signed<$type> { type Output = Self; fn opt_saturating_sub(self, rhs: Self) -> Option { Some(self.saturating_sub(rhs)) @@ -890,7 +890,7 @@ macro_rules! impl_signed_div_mul( } } - impl OptionCheckedDiv<$signed_rhs> for crate::Signed<$type> { + impl opt_ops::OptionCheckedDiv<$signed_rhs> for crate::Signed<$type> { type Output = Self; fn opt_checked_div(self, rhs: $signed_rhs) -> Result, opt_ops::Error> { if rhs == 0 { @@ -902,7 +902,7 @@ macro_rules! impl_signed_div_mul( } } - impl OptionCheckedMul<$signed_rhs> for crate::Signed<$type> { + impl opt_ops::OptionCheckedMul<$signed_rhs> for crate::Signed<$type> { type Output = Self; fn opt_checked_mul(self, rhs: $signed_rhs) -> Result, opt_ops::Error> { self.checked_mul(rhs) @@ -911,14 +911,14 @@ macro_rules! impl_signed_div_mul( } } - impl OptionSaturatingMul<$signed_rhs> for crate::Signed<$type> { + impl opt_ops::OptionSaturatingMul<$signed_rhs> for crate::Signed<$type> { type Output = Self; fn opt_saturating_mul(self, rhs: $signed_rhs) -> Option { Some(self.saturating_mul(rhs)) } } - impl OptionCheckedRem<$signed_rhs> for crate::Signed<$type> { + impl opt_ops::OptionCheckedRem<$signed_rhs> for crate::Signed<$type> { type Output = Self; fn opt_checked_rem(self, rhs: $signed_rhs) -> Result, opt_ops::Error> { if rhs == 0 { @@ -930,7 +930,7 @@ macro_rules! impl_signed_div_mul( } } - impl OptionCheckedDiv<$inner_type> for crate::Signed<$type> { + impl opt_ops::OptionCheckedDiv<$inner_type> for crate::Signed<$type> { type Output = Self; fn opt_checked_div(self, rhs: $inner_type) -> Result, opt_ops::Error> { if rhs == 0 { @@ -942,7 +942,7 @@ macro_rules! impl_signed_div_mul( } } - impl OptionCheckedMul<$inner_type> for crate::Signed<$type> { + impl opt_ops::OptionCheckedMul<$inner_type> for crate::Signed<$type> { type Output = Self; fn opt_checked_mul(self, rhs: $inner_type) -> Result, opt_ops::Error> { self.checked_mul_unsigned(rhs) @@ -951,14 +951,14 @@ macro_rules! impl_signed_div_mul( } } - impl OptionSaturatingMul<$inner_type> for crate::Signed<$type> { + impl opt_ops::OptionSaturatingMul<$inner_type> for crate::Signed<$type> { type Output = Self; fn opt_saturating_mul(self, rhs: $inner_type) -> Option { Some(self.saturating_mul_unsigned(rhs)) } } - impl OptionCheckedRem<$inner_type> for crate::Signed<$type> { + impl opt_ops::OptionCheckedRem<$inner_type> for crate::Signed<$type> { type Output = Self; fn opt_checked_rem(self, rhs: $inner_type) -> Result, opt_ops::Error> { if rhs == 0 { @@ -997,7 +997,7 @@ macro_rules! impl_signed_div_mul_trait( } } - impl MulDiv<$signed_rhs> for crate::Signed<$type> { + impl muldiv::MulDiv<$signed_rhs> for crate::Signed<$type> { type Output = crate::Signed<$type>; fn mul_div_floor(self, num: $signed_rhs, denom: $signed_rhs) -> Option { @@ -1049,7 +1049,7 @@ macro_rules! impl_signed_div_mul_trait( } } - impl MulDiv<$inner_type> for crate::Signed<$type> { + impl muldiv::MulDiv<$inner_type> for crate::Signed<$type> { type Output = crate::Signed<$type>; fn mul_div_floor(self, num: $inner_type, denom: $inner_type) -> Option { @@ -1209,7 +1209,7 @@ macro_rules! impl_format_value_traits( } } - impl ops::Deref for $name { + impl std::ops::Deref for $name { type Target = $inner_type; fn deref(&self) -> &$inner_type { @@ -1217,7 +1217,7 @@ macro_rules! impl_format_value_traits( } } - impl ops::DerefMut for $name { + impl std::ops::DerefMut for $name { fn deref_mut(&mut self) -> &mut $inner_type { &mut self.0 } diff --git a/gstreamer/src/format/mod.rs b/gstreamer/src/format/mod.rs index e64279dcd..3d56f9468 100644 --- a/gstreamer/src/format/mod.rs +++ b/gstreamer/src/format/mod.rs @@ -1,436 +1,31 @@ // Take a look at the license at the top of the repository in the LICENSE file. +use thiserror::Error; + #[macro_use] mod macros; -use crate::utils::Displayable; - mod clock_time; pub use clock_time::ClockTime; - #[cfg(feature = "serde")] mod clock_time_serde; +mod compatible; +pub use compatible::*; + +mod generic; +pub use generic::*; + +mod signed; +pub use signed::*; + +mod specific; +pub use specific::*; + +mod undefined; +pub use undefined::*; + use crate::Format; -use glib::translate::{FromGlib, GlibNoneError, IntoGlib, OptionIntoGlib, TryFromGlib}; -use muldiv::MulDiv; -use opt_ops::prelude::*; -use std::fmt; -use std::ops; -use thiserror::Error; - -// rustdoc-stripper-ignore-next -/// A signed wrapper. -/// -/// This wrapper allows representing a signed value from a type -/// which is originaly unsigned. In C APIs, this is represented -/// by a tuple with a signed integer positive or negative and -/// the absolute value. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Signed { - Negative(T), - Positive(T), -} - -impl Signed { - pub fn is_positive(self) -> bool { - matches!(self, Signed::Positive(_)) - } - - // rustdoc-stripper-ignore-next - /// Returns `Some(value)`, where `value` is the inner value, - /// if `self` is positive. - pub fn positive(self) -> Option { - match self { - Signed::Positive(val) => Some(val), - Signed::Negative(_) => None, - } - } - - // rustdoc-stripper-ignore-next - /// Transforms the `Signed` into a `Result`, - /// mapping `Positive(v)` to `Ok(v)` and `Negative(_)` to `Err(err)`. - pub fn positive_or(self, err: E) -> Result { - match self { - Signed::Positive(val) => Ok(val), - Signed::Negative(_) => Err(err), - } - } - - // rustdoc-stripper-ignore-next - /// Transforms the `Signed` into a `Result`, - /// mapping `Positive(v)` to `Ok(v)` and `Negative(v)` to `Err(err(v))`. - pub fn positive_or_else E>(self, err: F) -> Result { - match self { - Signed::Positive(val) => Ok(val), - Signed::Negative(val) => Err(err(val)), - } - } - - pub fn is_negative(self) -> bool { - matches!(self, Signed::Negative(_)) - } - - // rustdoc-stripper-ignore-next - /// Returns `Some(value)`, where `value` is the inner value, - /// if `self` is negative. - pub fn negative(self) -> Option { - match self { - Signed::Negative(val) => Some(val), - Signed::Positive(_) => None, - } - } - - // rustdoc-stripper-ignore-next - /// Transforms the `Signed` into a `Result`, - /// mapping `Negative(v)` to `Ok(v)` and `Positive(_)` to `Err(err)`. - pub fn negative_or(self, err: E) -> Result { - match self { - Signed::Negative(val) => Ok(val), - Signed::Positive(_) => Err(err), - } - } - - // rustdoc-stripper-ignore-next - /// Transforms the `Signed` into a `Result`, - /// mapping `Negative(v)` to `Ok(v)` and `Positive(_)` to `Err(err(v))`. - pub fn negative_or_else E>(self, err: F) -> Result { - match self { - Signed::Negative(val) => Ok(val), - Signed::Positive(val) => Err(err(val)), - } - } - - // rustdoc-stripper-ignore-next - /// Returns the multiplication factor for this `Signed`. - /// - /// Returns: - /// - /// - `1` if the value must be considered as positive. - /// - `-1` if the value must be considered as negative. - pub fn factor(self) -> i32 { - match self { - Signed::Positive(_) => 1i32, - Signed::Negative(_) => -1i32, - } - } - - // rustdoc-stripper-ignore-next - /// Returns the absolute value of `self`. - pub fn abs(self) -> T { - match self { - Signed::Positive(val) | Signed::Negative(val) => val, - } - } -} - -impl std::ops::Neg for Signed { - type Output = Signed; - - fn neg(self) -> Self { - match self { - Signed::Positive(val) => Signed::Negative(val), - Signed::Negative(val) => Signed::Positive(val), - } - } -} - -impl fmt::Display for Signed -where - T: fmt::Display + FormattedValueIntrinsic, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use std::fmt::Write; - - let (sign, val) = match self { - Signed::Positive(val) => ('+', val), - Signed::Negative(val) => ('-', val), - }; - - f.write_char(sign)?; - fmt::Display::fmt(&val, f) - } -} - -impl Displayable for Signed -where - T: fmt::Display + FormattedValueIntrinsic, -{ - type DisplayImpl = Signed; - - fn display(self) -> Self::DisplayImpl { - self - } -} - -impl Signed> { - // rustdoc-stripper-ignore-next - /// Transposes a `Signed` `Option` into an `Option` of a `Signed`. - /// - /// Note that if the inner value was `None`, the sign is lost. - pub fn transpose(self) -> Option> { - use Signed::*; - - match self { - Positive(Some(val)) => Some(Positive(val)), - Negative(Some(val)) => Some(Negative(val)), - _ => None, - } - } -} - -pub struct DisplayableOptionSigned(Option>); - -impl fmt::Display for DisplayableOptionSigned -where - T: fmt::Display + FormattedValueIntrinsic, - Option: Displayable, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - Some(ref signed) => fmt::Display::fmt(signed, f), - None => fmt::Display::fmt(&Option::::None.display(), f), - } - } -} - -impl Displayable for Option> -where - T: fmt::Display + FormattedValueIntrinsic, - Option: Displayable, -{ - type DisplayImpl = DisplayableOptionSigned; - - fn display(self) -> Self::DisplayImpl { - DisplayableOptionSigned(self) - } -} - -impl Displayable for Signed> -where - T: fmt::Display + FormattedValueIntrinsic, - Option: Displayable, -{ - type DisplayImpl = DisplayableOptionSigned; - - fn display(self) -> Self::DisplayImpl { - DisplayableOptionSigned(self.transpose()) - } -} - -// rustdoc-stripper-ignore-next -/// A trait implemented on unsigned types which can be converted into [`crate::Signed`]s. -pub trait UnsignedIntoSigned: Copy + Sized { - type Signed; - - // rustdoc-stripper-ignore-next - /// Converts `self` into a `Signed` matching the given `sign`. - fn into_signed(self, sign: i32) -> Self::Signed { - if sign.is_positive() { - self.into_positive() - } else { - self.into_negative() - } - } - - // rustdoc-stripper-ignore-next - /// Converts `self` into a `Signed::Positive`. - fn into_positive(self) -> Self::Signed; - - // rustdoc-stripper-ignore-next - /// Converts `self` into a `Signed::Negative`. - fn into_negative(self) -> Self::Signed; -} - -impl_unsigned_int_into_signed!(u64); -impl_signed_ops!(u64); -impl_signed_div_mul!(u64); - -impl_unsigned_int_into_signed!(usize); -impl_signed_ops!(usize); -impl_signed_div_mul!(usize); - -impl_unsigned_int_into_signed!(u32); -impl_signed_ops!(u32); -impl_signed_div_mul!(u32); - -impl From for Signed { - fn from(val: i64) -> Signed { - skip_assert_initialized!(); - match val { - positive if positive.is_positive() => Signed::Positive(positive as u64), - i64::MIN => { - // `i64::MIN.abs()` can't be represented as an `i64` - Signed::Negative((-(i64::MIN as i128)) as u64) - } - negative => Signed::Negative((-negative) as u64), - } - } -} - -impl From for Signed { - fn from(val: isize) -> Signed { - skip_assert_initialized!(); - match val { - positive if positive.is_positive() => Signed::Positive(positive as usize), - isize::MIN => { - // `isize::MIN.abs()` can't be represented as an `isize` - Signed::Negative((-(isize::MIN as i128)) as usize) - } - negative => Signed::Negative((-negative) as usize), - } - } -} - -// `i32::MIN.abs()` can't be represented as an `i32` -impl From for Signed { - fn from(val: i32) -> Signed { - skip_assert_initialized!(); - if val.is_positive() { - Signed::Positive(val as u32) - } else { - Signed::Negative((-(val as i64)) as u32) - } - } -} - -impl Signed { - // rustdoc-stripper-ignore-next - /// Returns the `self` in nanoseconds. - pub fn nseconds(self) -> Signed { - match self { - Signed::Positive(val) => Signed::Positive(val.nseconds()), - Signed::Negative(val) => Signed::Negative(val.nseconds()), - } - } - - // rustdoc-stripper-ignore-next - /// Creates new value from nanoseconds. - pub fn from_nseconds(val: Signed) -> Self { - skip_assert_initialized!(); - match val { - Signed::Positive(val) => Signed::Positive(ClockTime::from_nseconds(val)), - Signed::Negative(val) => Signed::Negative(ClockTime::from_nseconds(val)), - } - } - - // rustdoc-stripper-ignore-next - /// Returns the `self` in microseconds. - pub fn useconds(self) -> Signed { - match self { - Signed::Positive(val) => Signed::Positive(val.useconds()), - Signed::Negative(val) => Signed::Negative(val.useconds()), - } - } - - // rustdoc-stripper-ignore-next - /// Creates new value from microseconds. - pub fn from_useconds(val: Signed) -> Self { - skip_assert_initialized!(); - match val { - Signed::Positive(val) => Signed::Positive(ClockTime::from_useconds(val)), - Signed::Negative(val) => Signed::Negative(ClockTime::from_useconds(val)), - } - } - - // rustdoc-stripper-ignore-next - /// Returns the `self` in milliseconds. - pub fn mseconds(self) -> Signed { - match self { - Signed::Positive(val) => Signed::Positive(val.mseconds()), - Signed::Negative(val) => Signed::Negative(val.mseconds()), - } - } - - // rustdoc-stripper-ignore-next - /// Creates new value from milliseconds. - pub fn from_mseconds(val: Signed) -> Self { - skip_assert_initialized!(); - match val { - Signed::Positive(val) => Signed::Positive(ClockTime::from_mseconds(val)), - Signed::Negative(val) => Signed::Negative(ClockTime::from_mseconds(val)), - } - } - - // rustdoc-stripper-ignore-next - /// Returns the `self` in seconds. - pub fn seconds(self) -> Signed { - match self { - Signed::Positive(val) => Signed::Positive(val.seconds()), - Signed::Negative(val) => Signed::Negative(val.seconds()), - } - } - - // rustdoc-stripper-ignore-next - /// Creates new value from seconds. - pub fn from_seconds(val: Signed) -> Self { - skip_assert_initialized!(); - match val { - Signed::Positive(val) => Signed::Positive(ClockTime::from_seconds(val)), - Signed::Negative(val) => Signed::Negative(ClockTime::from_seconds(val)), - } - } -} - -#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum GenericFormattedValue { - Undefined(Undefined), - Default(Option), - Bytes(Option), - Time(Option), - Buffers(Option), - Percent(Option), - Other(Format, i64), -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Undefined(pub i64); - -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Default(pub u64); -impl Default { - pub const MAX: Self = Self(u64::MAX - 1); -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Bytes(pub u64); -impl Bytes { - pub const MAX: Self = Self(u64::MAX - 1); -} - -pub type Time = ClockTime; - -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Buffers(pub u64); -impl Buffers { - #[doc(alias = "GST_BUFFER_OFFSET_NONE")] - pub const OFFSET_NONE: u64 = ffi::GST_BUFFER_OFFSET_NONE; - pub const MAX: Self = Self(Self::OFFSET_NONE - 1); -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Percent(pub u32); -impl Percent { - #[doc(alias = "GST_FORMAT_PERCENT_MAX")] - pub const MAX: Self = Self(ffi::GST_FORMAT_PERCENT_MAX as u32); - #[doc(alias = "GST_FORMAT_PERCENT_SCALE")] - pub const SCALE: u32 = ffi::GST_FORMAT_PERCENT_SCALE as u32; -} - -impl fmt::Display for GenericFormattedValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Undefined(val) => val.fmt(f), - Self::Default(val) => val.display().fmt(f), - Self::Bytes(val) => val.display().fmt(f), - Self::Time(val) => val.display().fmt(f), - Self::Buffers(val) => val.display().fmt(f), - Self::Percent(val) => val.display().fmt(f), - Self::Other(format, val) => write!(f, "{} ({:?})", val, format), - } - } -} #[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] #[error("invalid formatted value format {:?}", .0)] @@ -482,19 +77,6 @@ pub trait FormattedValueFullRange: FormattedValue + TryFrom`. pub trait FormattedValueIntrinsic: FormattedValue {} -pub trait SpecificFormattedValue: FormattedValue {} - -pub trait SpecificFormattedValueFullRange: FormattedValueFullRange {} - -// rustdoc-stripper-ignore-next -/// A trait implemented on the intrinsic type of a `SpecificFormattedValue`. -/// -/// # Examples -/// -/// - `Undefined` is the intrinsic type for `Undefined`. -/// - `Bytes` is the intrinsic type for `Option`. -pub trait SpecificFormattedValueIntrinsic: TryFromGlib + FormattedValueIntrinsic {} - pub trait FormattedValueNoneBuilder: FormattedValueFullRange { // rustdoc-stripper-ignore-next /// Returns the `None` value for `Self` as a `FullRange` if such a value can be represented. @@ -535,697 +117,10 @@ pub trait FormattedValueNoneBuilder: FormattedValueFullRange { } } -pub trait NoneSignedBuilder: FormattedValueNoneBuilder { - type Signed; - - // rustdoc-stripper-ignore-next - /// Returns the `None` value for `Self` as a `Signed` if such a value can be represented. - /// - /// See details in [`FormattedValueNoneBuilder::none`]. - /// - /// # Panics - /// - /// Panics if `Self` is `GenericFormattedValue` in which case, the `Format` must be known. - fn none_signed() -> Self::Signed; - - // rustdoc-stripper-ignore-next - /// Returns the `None` value for `Self` as a `Signed`, if such a value can be represented. - /// - /// See details in [`FormattedValueNoneBuilder::none_for_format`]. - /// - /// # Panics - /// - /// Panics if `None` can't be represented by `Self` for `format` or by the requested - /// `GenericFormattedValue` variant. - fn none_signed_for_format(format: Format) -> Self::Signed; -} - -impl NoneSignedBuilder for T -where - T: UnsignedIntoSigned + FormattedValueNoneBuilder, -{ - type Signed = ::Signed; - - fn none_signed() -> Self::Signed { - Self::none().into_positive() - } - - fn none_signed_for_format(format: Format) -> Self::Signed { - skip_assert_initialized!(); - Self::none_for_format(format).into_positive() - } -} - -// rustdoc-stripper-ignore-next -/// A trait implemented on types which can hold [`FormattedValue`]s compatible with parameter `F`. -/// -/// This trait is auto-implemented based on [`FormattedValue`]s additional traits -/// such as [`SpecificFormattedValue`]. -/// -/// # Example: -/// -/// Consider the following function: -/// -/// ```rust -/// # use gstreamer::{ClockTime, CompatibleFormattedValue, FormattedValue, GenericFormattedValue}; -/// fn with_compatible_formats( -/// arg1: V, -/// arg2: impl CompatibleFormattedValue, -/// ) { -/// // This is required to access arg2 as a FormattedValue: -/// let _arg2 = arg2.try_into_checked(arg1).unwrap(); -/// } -/// -/// // This is Ok because arg1 is a ClockTime and arg2 is -/// // an Option which are compatible format-wise. -/// with_compatible_formats(ClockTime::ZERO, ClockTime::NONE); -/// -/// // This is Ok because arg1 is a ClockTime and arg2 is -/// // a GenericFormattedValue which are compatible format-wise. -/// with_compatible_formats( -/// ClockTime::ZERO, -/// GenericFormattedValue::Time(None), -/// ); -/// ``` -/// -/// Users are able to call the function with arguments: -/// -/// 1. of the same type (e.g. `ClockTime`), -/// 2. of different types, but able to hold a value of the same [`Format`] -/// (e.g. `ClockTime` and `Option`). -/// 3. One of a Formatted Value (specific or generic), the other being -/// a `GenericFormattedValue`. -/// -/// Format compatibility for cases 1 and 2 is enforced by -/// the type system, while case 3 will be checked at runtime time. -/// -/// ```compile_fail -/// # use gstreamer::{ClockTime, CompatibleFormattedValue, FormattedValue, format::Bytes}; -/// # fn with_compatible_formats( -/// # arg1: V, -/// # arg2: impl CompatibleFormattedValue, -/// # ) {} -/// // This doesn't compile because the arguments are not compatible: -/// let _ = with_compatible_formats(ClockTime::ZERO, Bytes(Some(42))); -/// ``` -/// -/// Note: users will not be able use `arg2` directly unless format -/// check succeeds: -/// -/// ```compile_fail -/// # use gstreamer::{CompatibleFormattedValue, FormattedValue}; -/// fn with_compatible_formats( -/// arg1: V, -/// arg2: impl CompatibleFormattedValue, -/// ) { -/// // This doesn't compile because arg2 hasn't been checked: -/// let _format = arg2.format(); -/// } -/// ``` -pub trait CompatibleFormattedValue { - type Original: FormattedValue; - - // rustdoc-stripper-ignore-next - /// Returns `Ok(self)` with its type restored if it is compatible with the format of `other`. - /// - /// When used with compatible [`SpecificFormattedValue`]s, checks - /// are enforced by the type system, no runtime checks are performed. - /// - /// When used with [`FormattedValue`] / [`GenericFormattedValue`] and - /// vice versa, a runtime format check is performed. If the check fails, - /// `Err(FormattedValueError)` is returned. - fn try_into_checked(self, other: V) -> Result; - - // rustdoc-stripper-ignore-next - /// Returns `Ok(self)` with its type restored if it is compatible with the format of `V`. - /// - /// When possible, prefer using [`Self::try_into_checked`] which - /// reduces the risk of missuse. - /// - /// When used with compatible [`SpecificFormattedValue`]s, checks - /// are enforced by the type system, no runtime checks are performed. - /// - /// When used with [`SpecificFormattedValue`] as a parameter and - /// a [`GenericFormattedValue`] as `Self`, a runtime check is perfomed - /// against the default format of the parameter. If the check fails, - /// `Err(FormattedValueError)` is returned. - /// - /// When used with [`GenericFormattedValue`] as a parameter and - /// a [`SpecificFormattedValue`] as `Self`, the `format` argument - /// used. If the check fails, `Err(FormattedValueError)` is returned. - fn try_into_checked_explicit( - self, - format: Format, - ) -> Result; -} - -impl CompatibleFormattedValue for T -where - V: SpecificFormattedValue, - T: SpecificFormattedValue, -{ - type Original = Self; - fn try_into_checked(self, _other: V) -> Result { - skip_assert_initialized!(); - Ok(self) - } - - fn try_into_checked_explicit( - self, - _format: Format, - ) -> Result { - skip_assert_initialized!(); - Ok(self) - } -} - -impl CompatibleFormattedValue for T { - type Original = Self; - fn try_into_checked(self, other: GenericFormattedValue) -> Result { - skip_assert_initialized!(); - if self.format() == other.format() { - Ok(self) - } else { - Err(FormattedValueError(self.format())) - } - } - - fn try_into_checked_explicit( - self, - format: Format, - ) -> Result { - skip_assert_initialized!(); - if self.format() == format { - Ok(self) - } else { - Err(FormattedValueError(self.format())) - } - } -} - -impl CompatibleFormattedValue for GenericFormattedValue { - type Original = Self; - fn try_into_checked(self, _other: V) -> Result { - skip_assert_initialized!(); - if self.format() == V::default_format() { - Ok(self) - } else { - Err(FormattedValueError(self.format())) - } - } - - fn try_into_checked_explicit( - self, - _format: Format, - ) -> Result { - skip_assert_initialized!(); - if self.format() == V::default_format() { - Ok(self) - } else { - Err(FormattedValueError(self.format())) - } - } -} - -impl CompatibleFormattedValue for GenericFormattedValue { - type Original = Self; - fn try_into_checked(self, other: GenericFormattedValue) -> Result { - skip_assert_initialized!(); - if self.format() == other.format() { - Ok(self) - } else { - Err(FormattedValueError(self.format())) - } - } - - fn try_into_checked_explicit( - self, - format: Format, - ) -> Result { - skip_assert_initialized!(); - if self.format() == format { - Ok(self) - } else { - Err(FormattedValueError(self.format())) - } - } -} - -impl FormattedValue for GenericFormattedValue { - // The intrinsic value for `GenericFormattedValue` is also - // `GenericFormattedValue`. We can't dissociate the `Option` - // from the variants' inner type since they are not all `Option`s. - type FullRange = GenericFormattedValue; - - fn default_format() -> Format { - Format::Undefined - } - - fn format(&self) -> Format { - self.format() - } - - fn is_some(&self) -> bool { - match self { - Self::Undefined(_) => true, - Self::Default(v) => v.is_some(), - Self::Bytes(v) => v.is_some(), - Self::Time(v) => v.is_some(), - Self::Buffers(v) => v.is_some(), - Self::Percent(v) => v.is_some(), - Self::Other(..) => true, - } - } - - unsafe fn into_raw_value(self) -> i64 { - self.value() - } -} - -impl FormattedValueFullRange for GenericFormattedValue { - unsafe fn from_raw(format: Format, value: i64) -> Self { - GenericFormattedValue::new(format, value) - } -} - -impl FormattedValueNoneBuilder for GenericFormattedValue { - #[track_caller] - fn none() -> Self { - panic!(concat!( - "`GenericFormattedValue` can't build `None` without knowing", - "the target format. Use `GenericFormattedValue::none_for_format`", - )); - } - - #[track_caller] - fn none_for_format(format: Format) -> Self { - skip_assert_initialized!(); - match format { - Format::Undefined => panic!("`None` can't be represented by `Undefined`"), - Format::Default => Self::Default(None), - Format::Bytes => Self::Bytes(None), - Format::Time => Self::Time(None), - Format::Buffers => Self::Buffers(None), - Format::Percent => Self::Percent(None), - Format::__Unknown(_) => panic!("`None` can't be represented by `__Unknown`"), - } - } -} - -impl GenericFormattedValue { - pub fn new(format: Format, value: i64) -> Self { - skip_assert_initialized!(); - match format { - Format::Undefined => Self::Undefined(Undefined(value)), - Format::Default => Self::Default(unsafe { FromGlib::from_glib(value as u64) }), - Format::Bytes => Self::Bytes(unsafe { FromGlib::from_glib(value as u64) }), - Format::Time => Self::Time(unsafe { FromGlib::from_glib(value as u64) }), - Format::Buffers => Self::Buffers(unsafe { FromGlib::from_glib(value as u64) }), - Format::Percent => { - Self::Percent(unsafe { FormattedValueFullRange::from_raw(format, value) }) - } - Format::__Unknown(_) => Self::Other(format, value), - } - } - - #[doc(alias = "get_format")] - pub fn format(&self) -> Format { - match *self { - Self::Undefined(_) => Format::Undefined, - Self::Default(_) => Format::Default, - Self::Bytes(_) => Format::Bytes, - Self::Time(_) => Format::Time, - Self::Buffers(_) => Format::Buffers, - Self::Percent(_) => Format::Percent, - Self::Other(f, _) => f, - } - } - - #[doc(alias = "get_value")] - pub fn value(&self) -> i64 { - unsafe { - match *self { - Self::Undefined(v) => v.0, - Self::Default(v) => v.into_raw_value(), - Self::Bytes(v) => v.into_raw_value(), - Self::Time(v) => v.into_raw_value(), - Self::Buffers(v) => v.into_raw_value(), - Self::Percent(v) => v.into_raw_value(), - Self::Other(_, v) => v, - } - } - } -} - -impl FormattedValueIntrinsic for GenericFormattedValue {} - -impl UnsignedIntoSigned for GenericFormattedValue { - type Signed = Signed; - - #[track_caller] - fn into_positive(self) -> Signed { - match self { - GenericFormattedValue::Undefined(_) => { - unimplemented!("`GenericFormattedValue::Undefined` is already signed") - } - GenericFormattedValue::Other(..) => { - unimplemented!("`GenericFormattedValue::Other` is already signed") - } - unsigned_inner => Signed::Positive(unsigned_inner), - } - } - - #[track_caller] - fn into_negative(self) -> Signed { - match self { - GenericFormattedValue::Undefined(_) => { - unimplemented!("`GenericFormattedValue::Undefined` is already signed") - } - GenericFormattedValue::Other(..) => { - unimplemented!("`GenericFormattedValue::Other` is already signed") - } - unsigned_inner => Signed::Negative(unsigned_inner), - } - } -} - -impl_common_ops_for_newtype_uint!(Default, u64); -impl_signed_div_mul!(Default, u64); -impl_format_value_traits!(Default, Default, Default, u64); -option_glib_newtype_from_to!(Default, u64::MAX); -glib_newtype_display!( - Default, - DisplayableDefault, - DisplayableOptionDefault, - "(Default)" -); - -impl_common_ops_for_newtype_uint!(Bytes, u64); -impl_signed_div_mul!(Bytes, u64); -impl_format_value_traits!(Bytes, Bytes, Bytes, u64); -option_glib_newtype_from_to!(Bytes, u64::MAX); -glib_newtype_display!(Bytes, DisplayableBytes, DisplayableOptionBytes, "bytes"); - -impl_format_value_traits!(ClockTime, Time, Time, u64); - -impl_common_ops_for_newtype_uint!(Buffers, u64); -impl_signed_div_mul!(Buffers, u64); -impl_format_value_traits!(Buffers, Buffers, Buffers, u64); -option_glib_newtype_from_to!(Buffers, Buffers::OFFSET_NONE); -glib_newtype_display!( - Buffers, - DisplayableBuffers, - DisplayableOptionBuffers, - "buffers" -); - -impl FormattedValue for Undefined { - type FullRange = Undefined; - - fn default_format() -> Format { - Format::Undefined - } - - fn format(&self) -> Format { - Format::Undefined - } - - fn is_some(&self) -> bool { - true - } - - unsafe fn into_raw_value(self) -> i64 { - self.0 - } -} - -impl FormattedValueFullRange for Undefined { - unsafe fn from_raw(format: Format, value: i64) -> Self { - debug_assert_eq!(format, Format::Undefined); - Undefined(value) - } -} - -impl From for GenericFormattedValue { - fn from(v: Undefined) -> Self { - skip_assert_initialized!(); - GenericFormattedValue::Undefined(v) - } -} - -impl TryFrom for Undefined { - type Error = FormattedValueError; - - fn try_from(v: GenericFormattedValue) -> Result { - skip_assert_initialized!(); - if let GenericFormattedValue::Undefined(v) = v { - Ok(v) - } else { - Err(FormattedValueError(v.format())) - } - } -} - -impl FormattedValueIntrinsic for Undefined {} - -impl TryFromGlib for Undefined { - type Error = std::convert::Infallible; - #[inline] - unsafe fn try_from_glib(v: i64) -> Result { - skip_assert_initialized!(); - Ok(Undefined(v)) - } -} - -impl From for Undefined { - fn from(v: i64) -> Self { - skip_assert_initialized!(); - Undefined(v) - } -} - -impl ops::Deref for Undefined { - type Target = i64; - - fn deref(&self) -> &i64 { - &self.0 - } -} - -impl ops::DerefMut for Undefined { - fn deref_mut(&mut self) -> &mut i64 { - &mut self.0 - } -} - -impl AsRef for Undefined { - fn as_ref(&self) -> &i64 { - &self.0 - } -} - -impl AsMut for Undefined { - fn as_mut(&mut self) -> &mut i64 { - &mut self.0 - } -} - -impl From for Signed { - fn from(val: Undefined) -> Signed { - skip_assert_initialized!(); - val.0.into() - } -} - -glib_newtype_display!(Undefined, DisplayableUndefined, "(Undefined)"); - -impl_common_ops_for_newtype_uint!(Percent, u32); -impl_signed_div_mul!(Percent, u32); -glib_newtype_display!(Percent, DisplayablePercent, DisplayableOptionPercent, "%"); - -impl FormattedValue for Option { - type FullRange = Option; - - fn default_format() -> Format { - Format::Percent - } - - fn format(&self) -> Format { - Format::Percent - } - - fn is_some(&self) -> bool { - Option::is_some(self) - } - - unsafe fn into_raw_value(self) -> i64 { - self.map_or(-1, |v| v.0 as i64) - } -} - -impl FormattedValueFullRange for Option { - unsafe fn from_raw(format: Format, value: i64) -> Self { - debug_assert_eq!(format, Format::Percent); - Percent::try_from_glib(value as i64).ok() - } -} - -impl From> for GenericFormattedValue { - fn from(v: Option) -> Self { - skip_assert_initialized!(); - GenericFormattedValue::Percent(v) - } -} - -impl From for GenericFormattedValue { - fn from(v: Percent) -> Self { - skip_assert_initialized!(); - GenericFormattedValue::Percent(Some(v)) - } -} - -impl FormattedValue for Percent { - type FullRange = Option; - - fn default_format() -> Format { - Format::Percent - } - - fn format(&self) -> Format { - Format::Percent - } - - fn is_some(&self) -> bool { - true - } - - unsafe fn into_raw_value(self) -> i64 { - self.0 as i64 - } -} - -impl TryFrom for Percent { - type Error = GlibNoneError; - - fn try_from(v: u64) -> Result { - skip_assert_initialized!(); - unsafe { Self::try_from_glib(v as i64) } - } -} - -impl TryFromGlib for Percent { - type Error = GlibNoneError; - #[inline] - unsafe fn try_from_glib(value: i64) -> Result { - skip_assert_initialized!(); - if value < 0 || value > ffi::GST_FORMAT_PERCENT_MAX { - Err(GlibNoneError) - } else { - Ok(Percent(value as u32)) - } - } -} - -impl TryFrom for Percent { - type Error = FormattedValueError; - - fn try_from(value: u32) -> Result { - skip_assert_initialized!(); - if value > ffi::GST_FORMAT_PERCENT_MAX as u32 { - Err(FormattedValueError(Format::Percent)) - } else { - Ok(Percent(value)) - } - } -} - -impl TryFrom for Option { - type Error = FormattedValueError; - - fn try_from(v: GenericFormattedValue) -> Result, Self::Error> { - skip_assert_initialized!(); - if let GenericFormattedValue::Percent(v) = v { - Ok(v) - } else { - Err(FormattedValueError(v.format())) - } - } -} - -impl FormattedValueIntrinsic for Percent {} -impl SpecificFormattedValue for Option {} -impl SpecificFormattedValueFullRange for Option {} -impl SpecificFormattedValueIntrinsic for Percent {} -impl FormattedValueNoneBuilder for Option { - fn none() -> Option { - None - } -} - -impl ops::Deref for Percent { - type Target = u32; - - fn deref(&self) -> &u32 { - &self.0 - } -} - -impl ops::DerefMut for Percent { - fn deref_mut(&mut self) -> &mut u32 { - &mut self.0 - } -} - -impl AsRef for Percent { - fn as_ref(&self) -> &u32 { - &self.0 - } -} - -impl AsMut for Percent { - fn as_mut(&mut self) -> &mut u32 { - &mut self.0 - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] -#[error("value out of range")] -pub struct TryPercentFromFloatError(()); - -impl TryFrom for Percent { - type Error = TryPercentFromFloatError; - - fn try_from(v: f64) -> Result { - skip_assert_initialized!(); - if v < 0.0 || v > 1.0 { - Err(TryPercentFromFloatError(())) - } else { - Ok(Percent( - (v * ffi::GST_FORMAT_PERCENT_SCALE as f64).round() as u32 - )) - } - } -} - -impl TryFrom for Percent { - type Error = TryPercentFromFloatError; - - fn try_from(v: f32) -> Result { - skip_assert_initialized!(); - if v < 0.0 || v > 1.0 { - Err(TryPercentFromFloatError(())) - } else { - Ok(Percent( - (v * ffi::GST_FORMAT_PERCENT_SCALE as f32).round() as u32 - )) - } - } -} - #[cfg(test)] mod tests { use super::*; - use crate::ClockTime; + use crate::utils::Displayable; fn with_compatible_formats( arg1: V1, diff --git a/gstreamer/src/format/signed.rs b/gstreamer/src/format/signed.rs new file mode 100644 index 000000000..026637186 --- /dev/null +++ b/gstreamer/src/format/signed.rs @@ -0,0 +1,322 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use std::fmt; + +use super::{Format, FormattedValueIntrinsic, FormattedValueNoneBuilder}; +use crate::utils::Displayable; + +// rustdoc-stripper-ignore-next +/// A signed wrapper. +/// +/// This wrapper allows representing a signed value from a type +/// which is originaly unsigned. In C APIs, this is represented +/// by a tuple with a signed integer positive or negative and +/// the absolute value. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Signed { + Negative(T), + Positive(T), +} + +impl Signed { + pub fn is_positive(self) -> bool { + matches!(self, Signed::Positive(_)) + } + + // rustdoc-stripper-ignore-next + /// Returns `Some(value)`, where `value` is the inner value, + /// if `self` is positive. + pub fn positive(self) -> Option { + match self { + Signed::Positive(val) => Some(val), + Signed::Negative(_) => None, + } + } + + // rustdoc-stripper-ignore-next + /// Transforms the `Signed` into a `Result`, + /// mapping `Positive(v)` to `Ok(v)` and `Negative(_)` to `Err(err)`. + pub fn positive_or(self, err: E) -> Result { + match self { + Signed::Positive(val) => Ok(val), + Signed::Negative(_) => Err(err), + } + } + + // rustdoc-stripper-ignore-next + /// Transforms the `Signed` into a `Result`, + /// mapping `Positive(v)` to `Ok(v)` and `Negative(v)` to `Err(err(v))`. + pub fn positive_or_else E>(self, err: F) -> Result { + match self { + Signed::Positive(val) => Ok(val), + Signed::Negative(val) => Err(err(val)), + } + } + + pub fn is_negative(self) -> bool { + matches!(self, Signed::Negative(_)) + } + + // rustdoc-stripper-ignore-next + /// Returns `Some(value)`, where `value` is the inner value, + /// if `self` is negative. + pub fn negative(self) -> Option { + match self { + Signed::Negative(val) => Some(val), + Signed::Positive(_) => None, + } + } + + // rustdoc-stripper-ignore-next + /// Transforms the `Signed` into a `Result`, + /// mapping `Negative(v)` to `Ok(v)` and `Positive(_)` to `Err(err)`. + pub fn negative_or(self, err: E) -> Result { + match self { + Signed::Negative(val) => Ok(val), + Signed::Positive(_) => Err(err), + } + } + + // rustdoc-stripper-ignore-next + /// Transforms the `Signed` into a `Result`, + /// mapping `Negative(v)` to `Ok(v)` and `Positive(_)` to `Err(err(v))`. + pub fn negative_or_else E>(self, err: F) -> Result { + match self { + Signed::Negative(val) => Ok(val), + Signed::Positive(val) => Err(err(val)), + } + } + + // rustdoc-stripper-ignore-next + /// Returns the multiplication factor for this `Signed`. + /// + /// Returns: + /// + /// - `1` if the value must be considered as positive. + /// - `-1` if the value must be considered as negative. + pub fn factor(self) -> i32 { + match self { + Signed::Positive(_) => 1i32, + Signed::Negative(_) => -1i32, + } + } + + // rustdoc-stripper-ignore-next + /// Returns the absolute value of `self`. + pub fn abs(self) -> T { + match self { + Signed::Positive(val) | Signed::Negative(val) => val, + } + } +} + +impl std::ops::Neg for Signed { + type Output = Signed; + + fn neg(self) -> Self { + match self { + Signed::Positive(val) => Signed::Negative(val), + Signed::Negative(val) => Signed::Positive(val), + } + } +} + +impl fmt::Display for Signed +where + T: fmt::Display + FormattedValueIntrinsic, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use std::fmt::Write; + + let (sign, val) = match self { + Signed::Positive(val) => ('+', val), + Signed::Negative(val) => ('-', val), + }; + + f.write_char(sign)?; + fmt::Display::fmt(&val, f) + } +} + +impl Displayable for Signed +where + T: fmt::Display + FormattedValueIntrinsic, +{ + type DisplayImpl = Signed; + + fn display(self) -> Self::DisplayImpl { + self + } +} + +impl Signed> { + // rustdoc-stripper-ignore-next + /// Transposes a `Signed` `Option` into an `Option` of a `Signed`. + /// + /// Note that if the inner value was `None`, the sign is lost. + pub fn transpose(self) -> Option> { + use Signed::*; + + match self { + Positive(Some(val)) => Some(Positive(val)), + Negative(Some(val)) => Some(Negative(val)), + _ => None, + } + } +} + +pub struct DisplayableOptionSigned(Option>); + +impl fmt::Display for DisplayableOptionSigned +where + T: fmt::Display + FormattedValueIntrinsic, + Option: Displayable, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + Some(ref signed) => fmt::Display::fmt(signed, f), + None => fmt::Display::fmt(&Option::::None.display(), f), + } + } +} + +impl Displayable for Option> +where + T: fmt::Display + FormattedValueIntrinsic, + Option: Displayable, +{ + type DisplayImpl = DisplayableOptionSigned; + + fn display(self) -> Self::DisplayImpl { + DisplayableOptionSigned(self) + } +} + +impl Displayable for Signed> +where + T: fmt::Display + FormattedValueIntrinsic, + Option: Displayable, +{ + type DisplayImpl = DisplayableOptionSigned; + + fn display(self) -> Self::DisplayImpl { + DisplayableOptionSigned(self.transpose()) + } +} + +// rustdoc-stripper-ignore-next +/// A trait implemented on unsigned types which can be converted into [`crate::Signed`]s. +pub trait UnsignedIntoSigned: Copy + Sized { + type Signed; + + // rustdoc-stripper-ignore-next + /// Converts `self` into a `Signed` matching the given `sign`. + fn into_signed(self, sign: i32) -> Self::Signed { + if sign.is_positive() { + self.into_positive() + } else { + self.into_negative() + } + } + + // rustdoc-stripper-ignore-next + /// Converts `self` into a `Signed::Positive`. + fn into_positive(self) -> Self::Signed; + + // rustdoc-stripper-ignore-next + /// Converts `self` into a `Signed::Negative`. + fn into_negative(self) -> Self::Signed; +} + +impl_unsigned_int_into_signed!(u64); +impl_signed_ops!(u64); +impl_signed_div_mul!(u64); + +impl_unsigned_int_into_signed!(usize); +impl_signed_ops!(usize); +impl_signed_div_mul!(usize); + +impl_unsigned_int_into_signed!(u32); +impl_signed_ops!(u32); +impl_signed_div_mul!(u32); + +impl From for Signed { + fn from(val: i64) -> Signed { + skip_assert_initialized!(); + match val { + positive if positive.is_positive() => Signed::Positive(positive as u64), + i64::MIN => { + // `i64::MIN.abs()` can't be represented as an `i64` + Signed::Negative((-(i64::MIN as i128)) as u64) + } + negative => Signed::Negative((-negative) as u64), + } + } +} + +impl From for Signed { + fn from(val: isize) -> Signed { + skip_assert_initialized!(); + match val { + positive if positive.is_positive() => Signed::Positive(positive as usize), + isize::MIN => { + // `isize::MIN.abs()` can't be represented as an `isize` + Signed::Negative((-(isize::MIN as i128)) as usize) + } + negative => Signed::Negative((-negative) as usize), + } + } +} + +// `i32::MIN.abs()` can't be represented as an `i32` +impl From for Signed { + fn from(val: i32) -> Signed { + skip_assert_initialized!(); + if val.is_positive() { + Signed::Positive(val as u32) + } else { + Signed::Negative((-(val as i64)) as u32) + } + } +} + +pub trait NoneSignedBuilder: FormattedValueNoneBuilder { + type Signed; + + // rustdoc-stripper-ignore-next + /// Returns the `None` value for `Self` as a `Signed` if such a value can be represented. + /// + /// See details in [`FormattedValueNoneBuilder::none`]. + /// + /// # Panics + /// + /// Panics if `Self` is `GenericFormattedValue` in which case, the `Format` must be known. + fn none_signed() -> Self::Signed; + + // rustdoc-stripper-ignore-next + /// Returns the `None` value for `Self` as a `Signed`, if such a value can be represented. + /// + /// See details in [`FormattedValueNoneBuilder::none_for_format`]. + /// + /// # Panics + /// + /// Panics if `None` can't be represented by `Self` for `format` or by the requested + /// `GenericFormattedValue` variant. + fn none_signed_for_format(format: Format) -> Self::Signed; +} + +impl NoneSignedBuilder for T +where + T: UnsignedIntoSigned + FormattedValueNoneBuilder, +{ + type Signed = ::Signed; + + fn none_signed() -> Self::Signed { + Self::none().into_positive() + } + + fn none_signed_for_format(format: Format) -> Self::Signed { + skip_assert_initialized!(); + Self::none_for_format(format).into_positive() + } +} diff --git a/gstreamer/src/format/specific.rs b/gstreamer/src/format/specific.rs new file mode 100644 index 000000000..fc924427a --- /dev/null +++ b/gstreamer/src/format/specific.rs @@ -0,0 +1,263 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{FromGlib, GlibNoneError, IntoGlib, OptionIntoGlib, TryFromGlib}; + +use super::{ + Format, FormattedValue, FormattedValueError, FormattedValueFullRange, FormattedValueIntrinsic, + FormattedValueNoneBuilder, GenericFormattedValue, +}; + +pub trait SpecificFormattedValue: FormattedValue {} + +pub trait SpecificFormattedValueFullRange: FormattedValueFullRange {} + +// rustdoc-stripper-ignore-next +/// A trait implemented on the intrinsic type of a `SpecificFormattedValue`. +/// +/// # Examples +/// +/// - `Undefined` is the intrinsic type for `Undefined`. +/// - `Bytes` is the intrinsic type for `Option`. +pub trait SpecificFormattedValueIntrinsic: TryFromGlib + FormattedValueIntrinsic {} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] +pub struct Buffers(pub u64); +impl Buffers { + #[doc(alias = "GST_BUFFER_OFFSET_NONE")] + pub const OFFSET_NONE: u64 = ffi::GST_BUFFER_OFFSET_NONE; + pub const MAX: Self = Self(Self::OFFSET_NONE - 1); +} + +impl_common_ops_for_newtype_uint!(Buffers, u64); +impl_signed_div_mul!(Buffers, u64); +impl_format_value_traits!(Buffers, Buffers, Buffers, u64); +option_glib_newtype_from_to!(Buffers, Buffers::OFFSET_NONE); +glib_newtype_display!( + Buffers, + DisplayableBuffers, + DisplayableOptionBuffers, + "buffers" +); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] +pub struct Bytes(pub u64); +impl Bytes { + pub const MAX: Self = Self(u64::MAX - 1); +} + +impl_common_ops_for_newtype_uint!(Bytes, u64); +impl_signed_div_mul!(Bytes, u64); +impl_format_value_traits!(Bytes, Bytes, Bytes, u64); +option_glib_newtype_from_to!(Bytes, u64::MAX); +glib_newtype_display!(Bytes, DisplayableBytes, DisplayableOptionBytes, "bytes"); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] +pub struct Default(pub u64); +impl Default { + pub const MAX: Self = Self(u64::MAX - 1); +} + +impl_common_ops_for_newtype_uint!(Default, u64); +impl_signed_div_mul!(Default, u64); +impl_format_value_traits!(Default, Default, Default, u64); +option_glib_newtype_from_to!(Default, u64::MAX); +glib_newtype_display!( + Default, + DisplayableDefault, + DisplayableOptionDefault, + "(Default)" +); + +pub type Time = super::ClockTime; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] +pub struct Percent(pub u32); +impl Percent { + #[doc(alias = "GST_FORMAT_PERCENT_MAX")] + pub const MAX: Self = Self(ffi::GST_FORMAT_PERCENT_MAX as u32); + #[doc(alias = "GST_FORMAT_PERCENT_SCALE")] + pub const SCALE: u32 = ffi::GST_FORMAT_PERCENT_SCALE as u32; +} + +impl_common_ops_for_newtype_uint!(Percent, u32); +impl_signed_div_mul!(Percent, u32); +glib_newtype_display!(Percent, DisplayablePercent, DisplayableOptionPercent, "%"); + +impl FormattedValue for Option { + type FullRange = Option; + + fn default_format() -> Format { + Format::Percent + } + + fn format(&self) -> Format { + Format::Percent + } + + fn is_some(&self) -> bool { + Option::is_some(self) + } + + unsafe fn into_raw_value(self) -> i64 { + self.map_or(-1, |v| v.0 as i64) + } +} + +impl FormattedValueFullRange for Option { + unsafe fn from_raw(format: Format, value: i64) -> Self { + debug_assert_eq!(format, Format::Percent); + Percent::try_from_glib(value as i64).ok() + } +} + +impl From> for GenericFormattedValue { + fn from(v: Option) -> Self { + skip_assert_initialized!(); + GenericFormattedValue::Percent(v) + } +} + +impl From for GenericFormattedValue { + fn from(v: Percent) -> Self { + skip_assert_initialized!(); + GenericFormattedValue::Percent(Some(v)) + } +} + +impl FormattedValue for Percent { + type FullRange = Option; + + fn default_format() -> Format { + Format::Percent + } + + fn format(&self) -> Format { + Format::Percent + } + + fn is_some(&self) -> bool { + true + } + + unsafe fn into_raw_value(self) -> i64 { + self.0 as i64 + } +} + +impl TryFrom for Percent { + type Error = GlibNoneError; + + fn try_from(v: u64) -> Result { + skip_assert_initialized!(); + unsafe { Self::try_from_glib(v as i64) } + } +} + +impl TryFromGlib for Percent { + type Error = GlibNoneError; + #[inline] + unsafe fn try_from_glib(value: i64) -> Result { + skip_assert_initialized!(); + if value < 0 || value > ffi::GST_FORMAT_PERCENT_MAX { + Err(GlibNoneError) + } else { + Ok(Percent(value as u32)) + } + } +} + +impl TryFrom for Percent { + type Error = FormattedValueError; + + fn try_from(value: u32) -> Result { + skip_assert_initialized!(); + if value > ffi::GST_FORMAT_PERCENT_MAX as u32 { + Err(FormattedValueError(Format::Percent)) + } else { + Ok(Percent(value)) + } + } +} + +impl TryFrom for Option { + type Error = FormattedValueError; + + fn try_from(v: GenericFormattedValue) -> Result, Self::Error> { + skip_assert_initialized!(); + if let GenericFormattedValue::Percent(v) = v { + Ok(v) + } else { + Err(FormattedValueError(v.format())) + } + } +} + +impl FormattedValueIntrinsic for Percent {} +impl SpecificFormattedValue for Option {} +impl SpecificFormattedValueFullRange for Option {} +impl SpecificFormattedValueIntrinsic for Percent {} +impl FormattedValueNoneBuilder for Option { + fn none() -> Option { + None + } +} + +impl std::ops::Deref for Percent { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +impl std::ops::DerefMut for Percent { + fn deref_mut(&mut self) -> &mut u32 { + &mut self.0 + } +} + +impl AsRef for Percent { + fn as_ref(&self) -> &u32 { + &self.0 + } +} + +impl AsMut for Percent { + fn as_mut(&mut self) -> &mut u32 { + &mut self.0 + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)] +#[error("value out of range")] +pub struct TryPercentFromFloatError(()); + +impl TryFrom for Percent { + type Error = TryPercentFromFloatError; + + fn try_from(v: f64) -> Result { + skip_assert_initialized!(); + if v < 0.0 || v > 1.0 { + Err(TryPercentFromFloatError(())) + } else { + Ok(Percent( + (v * ffi::GST_FORMAT_PERCENT_SCALE as f64).round() as u32 + )) + } + } +} + +impl TryFrom for Percent { + type Error = TryPercentFromFloatError; + + fn try_from(v: f32) -> Result { + skip_assert_initialized!(); + if v < 0.0 || v > 1.0 { + Err(TryPercentFromFloatError(())) + } else { + Ok(Percent( + (v * ffi::GST_FORMAT_PERCENT_SCALE as f32).round() as u32 + )) + } + } +} diff --git a/gstreamer/src/format/undefined.rs b/gstreamer/src/format/undefined.rs new file mode 100644 index 000000000..3517cc58d --- /dev/null +++ b/gstreamer/src/format/undefined.rs @@ -0,0 +1,111 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::TryFromGlib; +use std::ops::{Deref, DerefMut}; + +use super::{FormattedValue, FormattedValueFullRange, FormattedValueIntrinsic}; +use super::{FormattedValueError, GenericFormattedValue, Signed}; +use crate::Format; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] +pub struct Undefined(pub i64); + +impl FormattedValue for Undefined { + type FullRange = Undefined; + + fn default_format() -> Format { + Format::Undefined + } + + fn format(&self) -> Format { + Format::Undefined + } + + fn is_some(&self) -> bool { + true + } + + unsafe fn into_raw_value(self) -> i64 { + self.0 + } +} + +impl FormattedValueFullRange for Undefined { + unsafe fn from_raw(format: Format, value: i64) -> Self { + debug_assert_eq!(format, Format::Undefined); + Undefined(value) + } +} + +impl From for GenericFormattedValue { + fn from(v: Undefined) -> Self { + skip_assert_initialized!(); + GenericFormattedValue::Undefined(v) + } +} + +impl TryFrom for Undefined { + type Error = FormattedValueError; + + fn try_from(v: GenericFormattedValue) -> Result { + skip_assert_initialized!(); + if let GenericFormattedValue::Undefined(v) = v { + Ok(v) + } else { + Err(FormattedValueError(v.format())) + } + } +} + +impl FormattedValueIntrinsic for Undefined {} + +impl TryFromGlib for Undefined { + type Error = std::convert::Infallible; + #[inline] + unsafe fn try_from_glib(v: i64) -> Result { + skip_assert_initialized!(); + Ok(Undefined(v)) + } +} + +impl From for Undefined { + fn from(v: i64) -> Self { + skip_assert_initialized!(); + Undefined(v) + } +} + +impl Deref for Undefined { + type Target = i64; + + fn deref(&self) -> &i64 { + &self.0 + } +} + +impl DerefMut for Undefined { + fn deref_mut(&mut self) -> &mut i64 { + &mut self.0 + } +} + +impl AsRef for Undefined { + fn as_ref(&self) -> &i64 { + &self.0 + } +} + +impl AsMut for Undefined { + fn as_mut(&mut self) -> &mut i64 { + &mut self.0 + } +} + +impl From for Signed { + fn from(val: Undefined) -> Signed { + skip_assert_initialized!(); + val.0.into() + } +} + +glib_newtype_display!(Undefined, DisplayableUndefined, "(Undefined)");