diff --git a/gstreamer/src/format.rs b/gstreamer/src/format.rs index 0277244e9..8f4a5cd0e 100644 --- a/gstreamer/src/format.rs +++ b/gstreamer/src/format.rs @@ -10,6 +10,83 @@ 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 + /// 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 + /// 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, + } + } +} + #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] #[cfg_attr(feature = "ser_de", derive(serde::Serialize, serde::Deserialize))] pub enum GenericFormattedValue { @@ -81,6 +158,10 @@ pub trait FormattedValue: Copy + Clone + Sized + Into + ' /// Type which allows building a `FormattedValue` of this format from any raw value. type FullRange: FormattedValueFullRange + From; + // rustdoc-stripper-ignore-next + /// The `Signed` type for this `FormattedValue`. + type Signed; + #[doc(alias = "get_default_format")] fn default_format() -> Format; @@ -88,6 +169,17 @@ pub trait FormattedValue: Copy + Clone + Sized + Into + ' fn format(&self) -> Format; unsafe fn into_raw_value(self) -> i64; + + fn into_positive(self) -> Self::Signed; + fn into_negative(self) -> Self::Signed; + + fn into_signed(self, sign: i32) -> Self::Signed { + if sign < 0 { + self.into_negative() + } else { + self.into_positive() + } + } } // rustdoc-stripper-ignore-next @@ -323,6 +415,11 @@ impl CompatibleFormattedValue for GenericFormattedValue { impl FormattedValue for GenericFormattedValue { type FullRange = 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 Signed = Signed; + fn default_format() -> Format { Format::Undefined } @@ -334,6 +431,14 @@ impl FormattedValue for GenericFormattedValue { unsafe fn into_raw_value(self) -> i64 { self.value() } + + fn into_positive(self) -> Signed { + Signed::Positive(self) + } + + fn into_negative(self) -> Signed { + Signed::Negative(self) + } } impl FormattedValueFullRange for GenericFormattedValue { @@ -385,6 +490,30 @@ impl GenericFormattedValue { } } } + + pub 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, + } + } + + pub fn is_none(&self) -> bool { + match *self { + Self::Undefined(_) => false, + Self::Default(v) => v.is_none(), + Self::Bytes(v) => v.is_none(), + Self::Time(v) => v.is_none(), + Self::Buffers(v) => v.is_none(), + Self::Percent(v) => v.is_none(), + Self::Other(..) => false, + } + } } impl FormattedValueIntrinsic for GenericFormattedValue {} @@ -408,6 +537,7 @@ option_glib_newtype_display!(Buffers, "buffers"); impl FormattedValue for Undefined { type FullRange = Undefined; + type Signed = Signed; fn default_format() -> Format { Format::Undefined @@ -420,6 +550,14 @@ impl FormattedValue for Undefined { unsafe fn into_raw_value(self) -> i64 { self.0 } + + fn into_positive(self) -> Signed { + Signed::Positive(self) + } + + fn into_negative(self) -> Signed { + Signed::Negative(self) + } } impl FormattedValueFullRange for Undefined { @@ -512,6 +650,7 @@ option_glib_newtype_display!(Percent, "%"); impl FormattedValue for Option { type FullRange = Option; + type Signed = Option>; fn default_format() -> Format { Format::Percent @@ -524,6 +663,14 @@ impl FormattedValue for Option { unsafe fn into_raw_value(self) -> i64 { self.map_or(-1, |v| v.0 as i64) } + + fn into_positive(self) -> Option> { + Some(Signed::Positive(self?)) + } + + fn into_negative(self) -> Option> { + Some(Signed::Negative(self?)) + } } impl FormattedValueFullRange for Option { @@ -549,6 +696,7 @@ impl From for GenericFormattedValue { impl FormattedValue for Percent { type FullRange = Option; + type Signed = Signed; fn default_format() -> Format { Format::Percent @@ -561,6 +709,14 @@ impl FormattedValue for Percent { unsafe fn into_raw_value(self) -> i64 { self.0 as i64 } + + fn into_positive(self) -> Signed { + Signed::Positive(self) + } + + fn into_negative(self) -> Signed { + Signed::Negative(self) + } } impl TryFrom for Percent { @@ -665,10 +821,7 @@ impl TryFrom for Percent { #[cfg(test)] mod tests { - use super::{ - Buffers, CompatibleFormattedValue, Format, FormattedValue, FormattedValueError, - GenericFormattedValue, - }; + use super::*; use crate::ClockTime; fn with_compatible_formats( @@ -775,4 +928,112 @@ mod tests { ) .unwrap_err(); } + + #[test] + fn signed_optional() { + let ct_1 = Some(ClockTime::SECOND); + + let signed = ct_1.into_positive().unwrap(); + assert_eq!(signed, Signed::Positive(ClockTime::SECOND)); + assert!(signed.is_positive()); + assert_eq!(signed.positive_or(()).unwrap(), ClockTime::SECOND); + assert_eq!(signed.positive_or_else(|_| ()).unwrap(), ClockTime::SECOND); + signed.negative_or(()).unwrap_err(); + assert_eq!( + signed.negative_or_else(|val| val).unwrap_err(), + ClockTime::SECOND + ); + + let signed = ct_1.into_negative().unwrap(); + assert_eq!(signed, Signed::Negative(ClockTime::SECOND)); + assert!(signed.is_negative()); + assert_eq!(signed.negative_or(()).unwrap(), ClockTime::SECOND); + assert_eq!(signed.negative_or_else(|_| ()).unwrap(), ClockTime::SECOND); + signed.positive_or(()).unwrap_err(); + assert_eq!( + signed.positive_or_else(|val| val).unwrap_err(), + ClockTime::SECOND + ); + + let ct_none = ClockTime::NONE; + assert!(ct_none.into_positive().is_none()); + assert!(ct_none.into_negative().is_none()); + } + + #[test] + fn signed_mandatory() { + let ct_1 = ClockTime::SECOND; + + let signed = ct_1.into_positive(); + assert_eq!(signed, Signed::Positive(ct_1)); + assert!(signed.is_positive()); + + let signed = ct_1.into_negative(); + assert_eq!(signed, Signed::Negative(ct_1)); + assert!(signed.is_negative()); + + let und = Undefined(1); + + let signed = und.into_positive(); + assert_eq!(signed, Signed::Positive(und)); + assert!(signed.is_positive()); + + let signed = und.into_negative(); + assert_eq!(signed, Signed::Negative(und)); + assert!(signed.is_negative()); + } + + #[test] + fn signed_generic() { + let ct_1 = GenericFormattedValue::Time(Some(ClockTime::SECOND)); + assert!(ct_1.is_some()); + + let signed = ct_1.into_positive(); + assert_eq!(signed, Signed::Positive(ct_1)); + assert!(signed.is_positive()); + + let signed = ct_1.into_negative(); + assert_eq!(signed, Signed::Negative(ct_1)); + assert!(signed.is_negative()); + + let ct_none = GenericFormattedValue::Time(ClockTime::NONE); + assert!(ct_none.is_none()); + + let signed = ct_none.into_positive(); + assert_eq!(signed, Signed::Positive(ct_none)); + assert!(signed.is_positive()); + + let signed = ct_none.into_negative(); + assert_eq!(signed, Signed::Negative(ct_none)); + assert!(signed.is_negative()); + } + + #[test] + fn signed_roundtrip() { + let ct_1 = Some(ClockTime::SECOND); + let raw_ct_1 = unsafe { ct_1.into_raw_value() }; + + let signed = unsafe { Option::::from_raw(Format::Time, raw_ct_1) } + .into_signed(1) + .unwrap(); + assert_eq!(signed, Signed::Positive(ClockTime::SECOND)); + assert!(signed.is_positive()); + + let signed = unsafe { Option::::from_raw(Format::Time, raw_ct_1) } + .into_signed(-1) + .unwrap(); + assert_eq!(signed, Signed::Negative(ClockTime::SECOND)); + assert!(signed.is_negative()); + + let ct_none = ClockTime::NONE; + let raw_ct_none = unsafe { ct_none.into_raw_value() }; + + let signed = + unsafe { Option::::from_raw(Format::Time, raw_ct_none) }.into_signed(1); + assert!(signed.is_none()); + + let signed = + unsafe { Option::::from_raw(Format::Time, raw_ct_none) }.into_signed(-1); + assert!(signed.is_none()); + } } diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 1dfe9f0fc..322adf302 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -227,7 +227,7 @@ mod typefind_factory; pub mod format; pub use crate::format::{ CompatibleFormattedValue, FormattedValue, FormattedValueFullRange, FormattedValueIntrinsic, - GenericFormattedValue, SpecificFormattedValue, SpecificFormattedValueFullRange, + GenericFormattedValue, Signed, SpecificFormattedValue, SpecificFormattedValueFullRange, SpecificFormattedValueIntrinsic, }; #[cfg(feature = "ser_de")] diff --git a/gstreamer/src/macros.rs b/gstreamer/src/macros.rs index cdf448ba4..ee7907963 100644 --- a/gstreamer/src/macros.rs +++ b/gstreamer/src/macros.rs @@ -376,6 +376,7 @@ macro_rules! impl_format_value_traits( ($name:ident, $format:ident, $format_value:ident, $inner_type:ty) => { impl FormattedValue for Option<$name> { type FullRange = Option<$name>; + type Signed = Option>; fn default_format() -> Format { Format::$format @@ -388,6 +389,14 @@ macro_rules! impl_format_value_traits( unsafe fn into_raw_value(self) -> i64 { IntoGlib::into_glib(self) as i64 } + + fn into_positive(self) -> Option> { + Some(Signed::Positive(self?)) + } + + fn into_negative(self) -> Option> { + Some(Signed::Negative(self?)) + } } impl FormattedValueFullRange for Option<$name> { @@ -412,6 +421,7 @@ macro_rules! impl_format_value_traits( } impl FormattedValue for $name { type FullRange = Option<$name>; + type Signed = Signed<$name>; fn default_format() -> Format { Format::$format @@ -424,6 +434,14 @@ macro_rules! impl_format_value_traits( unsafe fn into_raw_value(self) -> i64 { IntoGlib::into_glib(self) as i64 } + + fn into_positive(self) -> Signed<$name> { + Signed::Positive(self) + } + + fn into_negative(self) -> Signed<$name> { + Signed::Negative(self) + } } impl SpecificFormattedValue for Option<$name> {}