use crate::{
prelude::*,
pw::{PipewireObject, Properties},
registry::{ConstraintType, ConstraintVerb, InterestMatch, InterestMatchFlags, ObjectInterest},
};
impl ObjectInterest {
#[doc(alias = "wp_object_interest_matches")]
pub fn matches_object<O: IsA<GObject>>(&self, object: &O) -> bool {
let object = object.as_ref();
self.matches(InterestMatchFlags::CHECK_ALL, object.type_(), Some(object), None, None) == InterestMatch::all()
}
#[doc(alias = "wp_object_interest_matches")]
pub fn matches_props(&self, props: &Properties) -> bool {
self.matches(
InterestMatchFlags::CHECK_ALL,
Properties::static_type(),
None::<&GObject>,
Some(props),
None,
) == InterestMatch::all()
}
#[doc(alias = "wp_object_interest_matches")]
pub fn matches_pw_object<O: IsA<PipewireObject>>(&self, object: &O) -> bool {
self.matches_props(&object.as_ref().properties().unwrap())
}
}
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct Interest<T: StaticType> {
interest: ObjectInterest,
_type: PhantomData<T>,
}
impl<T: StaticType> Interest<T> {
pub fn new() -> Self {
unsafe { Self::wrap_unchecked(ObjectInterest::new(T::static_type())) }
}
pub unsafe fn wrap_unchecked(interest: ObjectInterest) -> Self {
Self {
interest,
_type: PhantomData,
}
}
pub fn inner(&self) -> &ObjectInterest {
&self.interest
}
pub fn into_inner(self) -> ObjectInterest {
self.interest
}
pub fn matches_object<O: IsA<GObject>>(&self, object: &O) -> bool
where
T: IsA<O>,
{
self.interest.matches_object(object)
}
pub fn constrain<'o, O: IsA<GObject>>(&self, object: &'o O) -> Option<&'o T>
where
T: IsA<O>,
{
if self.matches_object(object) {
Some(unsafe { object.unsafe_cast_ref() })
} else {
None
}
}
pub fn filter<C: InterestContainer<T>>(self, container: &C) -> IntoValueIterator<T> {
container.filter(self)
}
pub fn lookup<C: InterestContainer<T>>(self, container: &C) -> Option<T> {
container.lookup(self)
}
}
impl<T: StaticType> From<Interest<T>> for ObjectInterest {
fn from(i: Interest<T>) -> Self {
i.into_inner()
}
}
pub trait InterestContainer<T: StaticType> {
fn filter(&self, interest: Interest<T>) -> IntoValueIterator<T>;
fn lookup(&self, interest: Interest<T>) -> Option<T>;
}
impl<T: StaticType> Deref for Interest<T> {
type Target = ObjectInterest;
fn deref(&self) -> &Self::Target {
self.inner()
}
}
impl<C: Borrow<Constraint>, T: StaticType> Extend<C> for Interest<T> {
fn extend<I: IntoIterator<Item = C>>(&mut self, iter: I) {
for constraint in iter {
constraint.borrow().add_to(&self)
}
}
}
impl<C: Borrow<Constraint>, T: StaticType> FromIterator<C> for Interest<T> {
fn from_iter<I: IntoIterator<Item = C>>(iter: I) -> Self {
let mut interest = Self::new();
interest.extend(iter);
interest
}
}
#[must_use]
#[derive(Clone, Debug, Variant)]
pub struct Constraint {
pub type_: ConstraintType,
pub subject: String,
pub verb: ConstraintVerb,
pub value: Option<Variant>,
}
impl Constraint {
pub fn has<S: Into<String>>(type_: ConstraintType, subject: S, present: bool) -> Self {
Self {
type_,
subject: subject.into(),
verb: if present {
ConstraintVerb::IsPresent
} else {
ConstraintVerb::IsAbsent
},
value: None,
}
}
pub fn compare<S: Into<String>, V: ToVariant>(type_: ConstraintType, subject: S, value: V, equal: bool) -> Self {
Self {
type_,
subject: subject.into(),
verb: if equal {
ConstraintVerb::Equals
} else {
ConstraintVerb::NotEquals
},
value: Some(value.to_variant()),
}
}
pub fn matches<S: Into<String>>(type_: ConstraintType, subject: S, pattern: &str) -> Self {
Self {
type_,
subject: subject.into(),
verb: ConstraintVerb::Matches,
value: Some(pattern.to_variant()),
}
}
pub fn in_range<S: Into<String>, V: ToVariant>(type_: ConstraintType, subject: S, low: V, high: V) -> Self {
Self {
type_,
subject: subject.into(),
verb: ConstraintVerb::InRange,
value: Some((low, high).to_variant()),
}
}
pub fn in_list<S: Into<String>, V: ToVariant, I: Iterator<Item = V>>(
type_: ConstraintType,
subject: S,
one_of: I,
) -> Self {
let values = one_of.map(|v| v.to_variant());
Self {
type_,
subject: subject.into(),
verb: ConstraintVerb::InRange,
value: Some(Variant::tuple_from_iter(values)),
}
}
pub fn add_to(&self, interest: &ObjectInterest) {
interest.add_constraint(self.type_, &self.subject, self.verb, self.value.as_ref())
}
}
impl FromStr for ConstraintVerb {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"equals" | "=" => ConstraintVerb::Equals,
"not-equals" | "!" => ConstraintVerb::NotEquals,
"in-list" | "c" => ConstraintVerb::InList,
"in-range" | "~" => ConstraintVerb::InRange,
"matches" | "#" => ConstraintVerb::Matches,
"is-present" | "+" => ConstraintVerb::IsPresent,
"is-absent" | "-" => ConstraintVerb::IsAbsent,
_ =>
return Err(Error::new(
LibraryErrorEnum::InvalidArgument,
&format!("unknown constraint verb {s}"),
)),
})
}
}
impl TryFrom<char> for ConstraintVerb {
type Error = <Self as FromStr>::Err;
fn try_from(value: char) -> Result<Self, Self::Error> {
Ok(match value {
'=' => ConstraintVerb::Equals,
'!' => ConstraintVerb::NotEquals,
'c' => ConstraintVerb::InList,
'~' => ConstraintVerb::InRange,
'#' => ConstraintVerb::Matches,
'+' => ConstraintVerb::IsPresent,
'-' => ConstraintVerb::IsAbsent,
_ =>
return Err(Error::new(
LibraryErrorEnum::InvalidArgument,
&format!("unknown constraint verb {value}"),
)),
})
}
}
impl StaticVariantType for ConstraintVerb {
fn static_variant_type() -> Cow<'static, VariantTy> {
<<Self as IntoGlib>::GlibType as StaticVariantType>::static_variant_type()
}
}
impl FromVariant for ConstraintVerb {
fn from_variant(variant: &Variant) -> Option<Self> {
match variant.classify() {
VariantClass::String => variant.get::<String>().and_then(|s| Self::from_str(&s).ok()),
_ => unsafe { Some(from_glib(variant.get()?)) },
}
}
}
impl ToVariant for ConstraintVerb {
fn to_variant(&self) -> Variant {
str::from_utf8(&[self.symbol() as u8]).unwrap().to_variant()
}
}
impl From<ConstraintVerb> for Variant {
fn from(v: ConstraintVerb) -> Self {
v.to_variant()
}
}
impl ConstraintVerb {
#[rustfmt::skip]
pub fn value_type(&self) -> Option<()> {
match self {
ConstraintVerb::__Unknown(_) => panic!("unknown constraint verb"),
ConstraintVerb::IsPresent | ConstraintVerb::IsAbsent => None,
ConstraintVerb::Equals | ConstraintVerb::NotEquals => Some(()),
ConstraintVerb::Matches => Some(()),
ConstraintVerb::InRange => Some(()),
ConstraintVerb::InList => Some(()),
}
}
pub fn nickname(&self) -> &'static str {
match self {
ConstraintVerb::Equals => "equals",
ConstraintVerb::NotEquals => "not-equals",
ConstraintVerb::InList => "in-list",
ConstraintVerb::InRange => "in-range",
ConstraintVerb::Matches => "matches",
ConstraintVerb::IsPresent => "is-present",
ConstraintVerb::IsAbsent => "is-absent",
ConstraintVerb::__Unknown(_) => panic!("unknown constraint verb"),
}
}
pub fn symbol(&self) -> char {
match self {
ConstraintVerb::__Unknown(_) => panic!("unknown constraint verb"),
_ => self.into_glib() as u8 as char,
}
}
}
impl Into<char> for ConstraintVerb {
fn into(self) -> char {
self.symbol()
}
}
impl fmt::Display for ConstraintVerb {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if f.alternate() {
fmt::Display::fmt(&self.nickname(), f)
} else {
fmt::Display::fmt(&self.symbol(), f)
}
}
}
impl Default for ConstraintType {
fn default() -> Self {
ConstraintType::PwProperty
}
}
impl fmt::Display for ConstraintType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.name())
}
}
impl FromStr for ConstraintType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"pw-global" => ConstraintType::PwGlobalProperty,
"pw" => ConstraintType::PwProperty,
"gobject" => ConstraintType::GProperty,
_ =>
return Err(Error::new(
LibraryErrorEnum::InvalidArgument,
&format!("unknown constraint type {s}"),
)),
})
}
}
impl StaticVariantType for ConstraintType {
fn static_variant_type() -> Cow<'static, VariantTy> {
<<Self as IntoGlib>::GlibType as StaticVariantType>::static_variant_type()
}
}
impl FromVariant for ConstraintType {
fn from_variant(variant: &Variant) -> Option<Self> {
match variant.classify() {
VariantClass::String => variant.get::<String>().and_then(|s| Self::from_str(&s).ok()),
_ => unsafe { Some(from_glib(variant.get()?)) },
}
}
}
impl ToVariant for ConstraintType {
fn to_variant(&self) -> Variant {
self.name().to_variant()
}
}
impl From<ConstraintType> for Variant {
fn from(t: ConstraintType) -> Self {
t.to_variant()
}
}
impl ConstraintType {
pub fn name(&self) -> &'static str {
match self {
ConstraintType::PwProperty => "pw",
ConstraintType::PwGlobalProperty => "pw-global",
ConstraintType::GProperty => "gobject",
ConstraintType::None => panic!("no constraint type"),
ConstraintType::__Unknown(_) => panic!("unknown constraint type"),
}
}
}
#[cfg(feature = "serde")]
mod impl_serde {
#[cfg(feature = "lua")]
use crate::lua::{LuaError, LuaVariant};
#[cfg(feature = "v0_4_8")]
use crate::spa::json::{BuildError, ParseError, SpaJson};
use {
crate::{
prelude::*,
registry::{Constraint, ConstraintType, ConstraintVerb},
},
serde::{
de::{self, Error as _, MapAccess, SeqAccess, Unexpected, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
},
};
impl<'de> Deserialize<'de> for ConstraintVerb {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
<Cow<String>>::deserialize(deserializer).and_then(|s| Self::from_str(&s).map_err(D::Error::custom))
}
}
impl Serialize for ConstraintVerb {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.symbol().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ConstraintType {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
<Cow<String>>::deserialize(deserializer).and_then(|s| Self::from_str(&s).map_err(D::Error::custom))
}
}
impl Serialize for ConstraintType {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.name().serialize(serializer)
}
}
impl Serialize for Constraint {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let compact = !serializer.is_human_readable();
let mut state = serializer.serialize_struct("Constraint", 4)?;
state.serialize_field("type", &self.type_)?;
state.serialize_field("subject", &self.subject)?;
state.serialize_field("verb", &self.verb)?;
#[allow(unreachable_patterns)]
match &self.value {
None => state.serialize_field("value", &None::<()>),
#[cfg(feature = "lua")]
Some(value) => state.serialize_field(
"value",
&Some(LuaVariant::convert_from(value).map_err(LuaError::serde_error_ser)?),
),
#[cfg(feature = "v0_4_8")]
Some(value) => state.serialize_field(
"value",
&Some(SpaJson::try_from_variant(value).map_err(BuildError::into_ser_error)?),
),
#[cfg(not(any(feature = "lua", feature = "v0_4_8")))]
Some(..) => panic!("Constraint value serialization requires the lua or v0_4_8 build feature"),
}?;
state.end()
}
}
#[rustfmt::skip]
impl<'de> Deserialize<'de> for Constraint {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
enum Field { Type, Subject, Verb, Value }
impl<'de> Deserialize<'de> for Field {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Field, D::Error> {
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("`type` or `subject` or `verb` or `value`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Field, E> {
match value {
"type" => Ok(Field::Type),
"subject" => Ok(Field::Subject),
"verb" => Ok(Field::Verb),
"value" => Ok(Field::Value),
_ => Err(E::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct ConstraintVisitor;
impl<'de> Visitor<'de> for ConstraintVisitor {
type Value = Constraint;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Constraint")
}
fn visit_seq<V: SeqAccess<'de>>(self, mut seq: V) -> Result<Self::Value, V::Error> {
let mut len = 0;
let mut subject: String = seq.next_element()?
.ok_or_else(|| V::Error::invalid_length(len, &self))?; len += 1;
let type_ = match ConstraintType::from_str(&subject) {
Ok(type_) => {
subject = seq.next_element()?
.ok_or_else(|| V::Error::invalid_length(len, &self))?; len += 1;
type_
},
Err(_) => ConstraintType::default(),
};
let verb = seq.next_element()?
.ok_or_else(|| V::Error::invalid_length(len, &self))?; len += 1;
#[allow(unreachable_patterns)]
let value = match verb {
ConstraintVerb::__Unknown(v) => return Err(V::Error::invalid_value(Unexpected::Signed(v.into()), &"constraint verb")),
ConstraintVerb::IsPresent | ConstraintVerb::IsAbsent => None,
#[cfg(any(feature = "lua", feature = "v0_4_8"))]
ConstraintVerb::Equals | ConstraintVerb::NotEquals => Some({
let value = match () {
#[cfg(feature = "lua")]
() => seq.next_element::<LuaVariant>()?
.map(Into::into),
#[cfg(feature = "v0_4_8")]
() => seq.next_element::<SpaJson>()?
.map(|v| v.parse_variant()).transpose().map_err(ParseError::into_serde_error)?,
};
value.ok_or_else(|| V::Error::invalid_length(len, &"constraint value"))?
}),
ConstraintVerb::Matches => Some(
seq.next_element::<&str>()?
.ok_or_else(|| V::Error::invalid_length(len, &"constraint match pattern"))?
.to_variant()
),
#[cfg(any(feature = "lua", feature = "v0_4_8"))]
ConstraintVerb::InRange => Some({
let (min, max) = match () {
#[cfg(feature = "lua")]
() => (
seq.next_element::<LuaVariant>()?
.map(LuaVariant::into_variant),
seq.next_element::<LuaVariant>()?
.map(LuaVariant::into_variant),
),
#[cfg(feature = "v0_4_8")]
() => (
seq.next_element::<SpaJson>()?
.as_ref().map(SpaJson::parse_variant)
.transpose().map_err(ParseError::into_serde_error)?,
seq.next_element::<SpaJson>()?
.as_ref().map(SpaJson::parse_variant)
.transpose().map_err(ParseError::into_serde_error)?,
),
};
Variant::tuple_from_iter([
min
.ok_or_else(|| V::Error::invalid_length(len, &"constraint range min"))?,
max
.ok_or_else(|| V::Error::invalid_length(len + 1, &"constraint range max"))?,
])
}),
#[cfg(any(feature = "lua", feature = "v0_4_8"))]
ConstraintVerb::InList => {
let mut values: Vec<Variant> = Vec::with_capacity(seq.size_hint().unwrap_or(0));
match () {
#[cfg(feature = "lua")]
() => while let Some(value) = seq.next_element::<LuaVariant>()? {
values.push(value.into());
},
#[cfg(feature = "v0_4_8")]
() => while let Some(value) = seq.next_element::<SpaJson>()? {
values.push(value.parse_variant().map_err(ParseError::into_serde_error)?);
},
}
Some(Variant::tuple_from_iter(values))
},
#[cfg(not(any(feature = "lua", feature = "v0_4_8")))]
verb => panic!("{verb:?} serialization requires the lua or v0_4_8 build feature"),
};
Ok(Constraint {
type_,
subject,
verb,
value,
})
}
fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
let mut type_ = None;
let mut subject = None;
let mut verb = None;
let mut value = None::<Option<Variant>>;
while let Some(key) = map.next_key()? {
#[allow(unreachable_patterns)]
match key {
Field::Type => {
if type_.is_some() {
return Err(V::Error::duplicate_field("type"))
}
type_ = Some(map.next_value()?);
},
Field::Subject => {
if subject.is_some() {
return Err(V::Error::duplicate_field("subject"))
}
subject = Some(map.next_value()?);
},
Field::Verb => {
if verb.is_some() {
return Err(V::Error::duplicate_field("verb"))
}
verb = Some(map.next_value()?);
},
Field::Value => {
if value.is_some() {
return Err(V::Error::duplicate_field("value"))
}
value = Some(match () {
#[cfg(feature = "lua")]
() => map.next_value::<Option<LuaVariant>>()?.map(Into::into),
#[cfg(feature = "v0_4_8")]
() => map.next_value::<Option<SpaJson>>()?.map(|v| v.parse_variant())
.transpose().map_err(ParseError::into_serde_error)?,
#[cfg(not(any(feature = "lua", feature = "v0_4_8")))]
() => match map.next_value::<Option<de::IgnoredAny>>() {
None => None,
Some(..) => panic!("Constraint value serialization requires the lua or v0_4_8 build feature"),
},
});
},
}
}
Ok(Constraint {
type_: type_.ok_or_else(|| V::Error::missing_field("type"))?,
subject: subject.ok_or_else(|| V::Error::missing_field("subject"))?,
verb: verb.ok_or_else(|| V::Error::missing_field("verb"))?,
value: value.unwrap_or_default().map(Into::into),
})
}
}
const FIELDS: &'static [&'static str] = &["type", "subject", "verb", "value"];
deserializer.deserialize_struct("Constraint", FIELDS, ConstraintVisitor)
}
}
}