gstreamer-rs/gstreamer/src/format/compatible.rs

183 lines
6.1 KiB
Rust

// Take a look at the license at the top of the repository in the LICENSE file.
use super::{
Format, FormattedValue, FormattedValueError, GenericFormattedValue, 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::format::{ClockTime, CompatibleFormattedValue, FormattedValue, GenericFormattedValue};
/// fn with_compatible_formats<V: FormattedValue>(
/// arg1: V,
/// arg2: impl CompatibleFormattedValue<V>,
/// ) {
/// // 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<ClockTime> 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<ClockTime>`).
/// 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<V: FormattedValue>(
/// # arg1: V,
/// # arg2: impl CompatibleFormattedValue<V>,
/// # ) {}
/// // 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<V: FormattedValue>(
/// arg1: V,
/// arg2: impl CompatibleFormattedValue<V>,
/// ) {
/// // This doesn't compile because arg2 hasn't been checked:
/// let _format = arg2.format();
/// }
/// ```
pub trait CompatibleFormattedValue<V: FormattedValue> {
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<Self::Original, FormattedValueError>;
// 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 misuse.
///
/// 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 performed
/// 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<Self::Original, FormattedValueError>;
}
impl<T, V> CompatibleFormattedValue<V> for T
where
V: SpecificFormattedValue,
T: SpecificFormattedValue<FullRange = V::FullRange>,
{
type Original = Self;
#[inline]
fn try_into_checked(self, _other: V) -> Result<Self, FormattedValueError> {
skip_assert_initialized!();
Ok(self)
}
#[inline]
fn try_into_checked_explicit(
self,
_format: Format,
) -> Result<Self::Original, FormattedValueError> {
skip_assert_initialized!();
Ok(self)
}
}
impl<T: SpecificFormattedValue> CompatibleFormattedValue<GenericFormattedValue> for T {
type Original = Self;
#[inline]
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()))
}
}
#[inline]
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()))
}
}
}
impl<V: SpecificFormattedValue> CompatibleFormattedValue<V> for GenericFormattedValue {
type Original = Self;
#[inline]
fn try_into_checked(self, _other: V) -> Result<Self, FormattedValueError> {
skip_assert_initialized!();
if self.format() == V::default_format() {
Ok(self)
} else {
Err(FormattedValueError(self.format()))
}
}
#[inline]
fn try_into_checked_explicit(
self,
_format: Format,
) -> Result<Self::Original, FormattedValueError> {
skip_assert_initialized!();
if self.format() == V::default_format() {
Ok(self)
} else {
Err(FormattedValueError(self.format()))
}
}
}