gstreamer-rs/gstreamer/src/format/generic.rs
François Laignel dcf6d16496 gst/format: new panicking constructors and some Percent fixes
Previous proposition for constructing specific formatted values was
to use an operation such as `42 * Default::ONE` which, in retrospect,
doesn't seem idiomatic.

This commit adds `from_u64` and `from_usize` constructors for most
formatted values. Having `from_usize` is convenient when dealing with
quantities related to containers indices or length.

This also fixes the `Percent` from float constructors from which was
derived the `ONE` constant as well as previous display implementation.

Also removed the `pub` specifier for `Undefined` inner value. It wasn't
removed in a previous commit as `Undefined` can use the full range of
the inner type. But now, it seems preferable not to expose the inner
value for proper encapsulation and so as to reduce the differences with
other formatted values (kind of least astonishment principle).
2022-10-11 13:25:53 +02:00

481 lines
16 KiB
Rust

// 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 std::fmt;
use crate::utils::Displayable;
use super::{
Buffers, Bytes, ClockTime, Default, Format, FormattedValueError, Percent, Signed, Undefined,
};
use super::{
CompatibleFormattedValue, FormattedValue, FormattedValueFullRange, FormattedValueIntrinsic,
FormattedValueNoneBuilder, SignedIntrinsic, UnsignedIntoSigned,
};
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)]
pub struct Other(u64);
impl Other {
pub const MAX: Self = Self(u64::MAX - 1);
}
impl Other {
// rustdoc-stripper-ignore-next
/// Builds a new `Other` value with the provided quantity.
///
/// # Panics
///
/// Panics if the provided quantity equals `u64::MAX`,
/// which is reserved for `None` in C.
#[track_caller]
pub fn from_u64(quantity: u64) -> Self {
Other::try_from(quantity).expect("`Other` value out of range")
}
// rustdoc-stripper-ignore-next
/// Builds a new `Other` value with the provided quantity.
///
/// # Panics
///
/// Panics if the provided quantity equals `u64::MAX`,
/// which is reserved for `None` in C.
#[track_caller]
pub fn from_usize(quantity: usize) -> Self {
Other::from_u64(quantity.try_into().unwrap())
}
}
impl_common_ops_for_newtype_uint!(Other, u64);
impl_signed_div_mul!(Other, u64);
option_glib_newtype_from_to!(Other, u64::MAX);
glib_newtype_display!(Other, DisplayableOptionOther);
impl TryFrom<u64> for Other {
type Error = GlibNoneError;
fn try_from(val: u64) -> Result<Self, GlibNoneError> {
skip_assert_initialized!();
unsafe { Self::try_from_glib(val) }
}
}
impl TryFromGlib<i64> for Other {
type Error = GlibNoneError;
#[inline]
unsafe fn try_from_glib(val: i64) -> Result<Self, GlibNoneError> {
skip_assert_initialized!();
Self::try_from_glib(val as u64)
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GenericFormattedValue {
Undefined(Undefined),
Default(Option<Default>),
Bytes(Option<Bytes>),
Time(Option<ClockTime>),
Buffers(Option<Buffers>),
Percent(Option<Percent>),
Other(Format, Option<Other>),
}
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) => {
val.display().fmt(f)?;
fmt::Write::write_char(f, ' ')?;
fmt::Display::fmt(&format, f)
}
}
}
}
impl Displayable for GenericFormattedValue {
type DisplayImpl = Self;
fn display(self) -> Self {
self
}
}
impl GenericFormattedValue {
pub fn new(format: Format, value: i64) -> Self {
skip_assert_initialized!();
match format {
Format::Undefined => Self::Undefined(value.into()),
Format::Default => Self::Default(unsafe { FromGlib::from_glib(value) }),
Format::Bytes => Self::Bytes(unsafe { FromGlib::from_glib(value) }),
Format::Time => Self::Time(unsafe { FromGlib::from_glib(value) }),
Format::Buffers => Self::Buffers(unsafe { FromGlib::from_glib(value) }),
Format::Percent => Self::Percent(unsafe { FromGlib::from_glib(value) }),
Format::__Unknown(_) => Self::Other(format, unsafe { FromGlib::from_glib(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,
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.into_glib() as i64,
}
}
}
}
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(_, v) => v.is_some(),
}
}
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 SignedIntrinsic 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),
unknown => Self::Other(unknown, Other::NONE),
}
}
}
impl UnsignedIntoSigned for GenericFormattedValue {
type Signed = GenericSignedFormattedValue;
#[track_caller]
fn into_positive(self) -> Self::Signed {
use Signed::Positive;
match self {
Self::Undefined(_) => {
unimplemented!("`GenericFormattedValue::Undefined` is already signed")
}
Self::Default(val) => Self::Signed::Default(val.map(Positive)),
Self::Bytes(val) => Self::Signed::Bytes(val.map(Positive)),
Self::Time(val) => Self::Signed::Time(val.map(Positive)),
Self::Buffers(val) => Self::Signed::Buffers(val.map(Positive)),
Self::Percent(val) => Self::Signed::Percent(val.map(Positive)),
Self::Other(format, val) => Self::Signed::Other(format, val.map(Positive)),
}
}
#[track_caller]
fn into_negative(self) -> Self::Signed {
use Signed::Negative;
match self {
Self::Undefined(_) => {
unimplemented!("`GenericFormattedValue::Undefined` is already signed")
}
Self::Default(val) => Self::Signed::Default(val.map(Negative)),
Self::Bytes(val) => Self::Signed::Bytes(val.map(Negative)),
Self::Time(val) => Self::Signed::Time(val.map(Negative)),
Self::Buffers(val) => Self::Signed::Buffers(val.map(Negative)),
Self::Percent(val) => Self::Signed::Percent(val.map(Negative)),
Self::Other(format, val) => Self::Signed::Other(format, val.map(Negative)),
}
}
}
impl CompatibleFormattedValue<GenericFormattedValue> for GenericFormattedValue {
type Original = Self;
fn try_into_checked(self, other: GenericFormattedValue) -> Result<Self, FormattedValueError> {
skip_assert_initialized!();
if self.format() == other.format() {
Ok(self)
} else {
Err(FormattedValueError(self.format()))
}
}
fn try_into_checked_explicit(
self,
format: Format,
) -> Result<Self::Original, FormattedValueError> {
skip_assert_initialized!();
if self.format() == format {
Ok(self)
} else {
Err(FormattedValueError(self.format()))
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GenericSignedFormattedValue {
Default(Option<Signed<Default>>),
Bytes(Option<Signed<Bytes>>),
Time(Option<Signed<ClockTime>>),
Buffers(Option<Signed<Buffers>>),
Percent(Option<Signed<Percent>>),
Other(Format, Option<Signed<Other>>),
}
impl GenericSignedFormattedValue {
#[doc(alias = "get_format")]
pub fn format(&self) -> Format {
match *self {
Self::Default(_) => Format::Default,
Self::Bytes(_) => Format::Bytes,
Self::Time(_) => Format::Time,
Self::Buffers(_) => Format::Buffers,
Self::Percent(_) => Format::Percent,
Self::Other(format, _) => format,
}
}
pub fn abs(self) -> GenericFormattedValue {
use GenericFormattedValue as Unsigned;
match self {
Self::Default(opt_signed) => Unsigned::Default(opt_signed.map(Signed::abs)),
Self::Bytes(opt_signed) => Unsigned::Bytes(opt_signed.map(Signed::abs)),
Self::Time(opt_signed) => Unsigned::Time(opt_signed.map(Signed::abs)),
Self::Buffers(opt_signed) => Unsigned::Buffers(opt_signed.map(Signed::abs)),
Self::Percent(opt_signed) => Unsigned::Percent(opt_signed.map(Signed::abs)),
Self::Other(format, opt_signed) => Unsigned::Other(format, opt_signed.map(Signed::abs)),
}
}
pub fn is_some(&self) -> bool {
match self {
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(_, v) => v.is_some(),
}
}
pub fn is_none(&self) -> bool {
!self.is_some()
}
#[track_caller]
pub fn none_for_format(format: Format) -> Self {
skip_assert_initialized!();
match format {
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::Undefined => {
panic!("`Undefined` is already signed, use `GenericFormattedValue`")
}
other => Self::Other(other, None),
}
}
}
macro_rules! impl_gsfv_fn_opt_ret(
($fn:ident(self) -> Option<$ret_ty:ty>) => {
pub fn $fn(self) -> Option<$ret_ty> {
match self {
Self::Default(opt_signed) => opt_signed.map(|signed| signed.$fn()),
Self::Bytes(opt_signed) => opt_signed.map(|signed| signed.$fn()),
Self::Time(opt_signed) => opt_signed.map(|signed| signed.$fn()),
Self::Buffers(opt_signed) => opt_signed.map(|signed| signed.$fn()),
Self::Percent(opt_signed) => opt_signed.map(|signed| signed.$fn()),
Self::Other(_, opt_signed) => opt_signed.map(|signed| signed.$fn()),
}
}
};
);
impl GenericSignedFormattedValue {
impl_gsfv_fn_opt_ret!(is_positive(self) -> Option<bool>);
impl_gsfv_fn_opt_ret!(is_negative(self) -> Option<bool>);
impl_gsfv_fn_opt_ret!(signum(self) -> Option<i32>);
}
impl std::ops::Neg for GenericSignedFormattedValue {
type Output = Self;
fn neg(self) -> Self {
use std::ops::Neg;
match self {
Self::Default(opt_signed) => Self::Default(opt_signed.map(Neg::neg)),
Self::Bytes(opt_signed) => Self::Bytes(opt_signed.map(Neg::neg)),
Self::Time(opt_signed) => Self::Time(opt_signed.map(Neg::neg)),
Self::Buffers(opt_signed) => Self::Buffers(opt_signed.map(Neg::neg)),
Self::Percent(opt_signed) => Self::Percent(opt_signed.map(Neg::neg)),
Self::Other(format, opt_signed) => Self::Other(format, opt_signed.map(Neg::neg)),
}
}
}
impl fmt::Display for GenericSignedFormattedValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Default(opt_signed) => opt_signed.display().fmt(f),
Self::Bytes(opt_signed) => opt_signed.display().fmt(f),
Self::Time(opt_signed) => opt_signed.display().fmt(f),
Self::Buffers(opt_signed) => opt_signed.display().fmt(f),
Self::Percent(opt_signed) => opt_signed.display().fmt(f),
Self::Other(format, opt_signed) => {
opt_signed.display().fmt(f)?;
fmt::Write::write_char(f, ' ')?;
fmt::Display::fmt(&format, f)
}
}
}
}
impl Displayable for GenericSignedFormattedValue {
type DisplayImpl = Self;
fn display(self) -> Self::DisplayImpl {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::eq_op, clippy::op_ref)]
fn other() {
// Check a few ops on `Other`, better converage for
// the macro ops impl ensured as part of the `clock_time` module.
use opt_ops::prelude::*;
let other_none: Option<Other> = Other::try_from(u64::MAX).ok();
assert!(other_none.is_none());
let other_10 = Other::from_u64(10);
let other_20 = Other::from_usize(20);
let other_30 = Other::from_u64(30);
assert_eq!(other_10 + other_20, other_30);
assert_eq!(other_30 - other_20, other_10);
assert!(other_10 < Other::MAX);
assert_eq!(Some(other_10).opt_add(other_20), Some(other_30));
}
#[test]
#[allow(clippy::eq_op, clippy::op_ref)]
fn generic_other() {
let gen_other_42: GenericFormattedValue =
GenericFormattedValue::new(Format::__Unknown(128), 42);
assert_eq!(
gen_other_42,
GenericFormattedValue::Other(Format::__Unknown(128), Other::try_from(42).ok())
);
assert_eq!(gen_other_42.format(), Format::__Unknown(128));
assert_eq!(gen_other_42.value(), 42);
assert!(gen_other_42.is_some());
let other_none: Option<Other> = Other::NONE;
assert!(other_none.is_none());
let gen_other_none: GenericFormattedValue =
GenericFormattedValue::none_for_format(Format::__Unknown(128));
assert!(gen_other_none.is_none());
assert_eq!(
gen_other_none,
GenericFormattedValue::Other(Format::__Unknown(128), None)
);
}
#[test]
#[allow(clippy::eq_op, clippy::op_ref)]
fn generic_signed_other() {
let gen_other_42: GenericFormattedValue =
GenericFormattedValue::new(Format::__Unknown(128), 42);
let p_gen_other_42 = gen_other_42.into_positive();
assert_eq!(
p_gen_other_42,
GenericSignedFormattedValue::Other(
Format::__Unknown(128),
Some(Signed::Positive(Other::from_u64(42))),
),
);
let n_gen_other_42 = gen_other_42.into_negative();
assert_eq!(
n_gen_other_42,
GenericSignedFormattedValue::Other(
Format::__Unknown(128),
Some(Signed::Negative(Other::from_u64(42))),
),
);
}
}