libspa/utils/
mod.rs

1//! Miscellaneous and utility items.
2
3pub mod dict;
4mod direction;
5pub use direction::*;
6pub mod hook;
7pub mod list;
8pub mod result;
9
10use bitflags::bitflags;
11use std::{
12    ffi::CStr,
13    fmt::{self, Debug, Formatter, Write},
14    os::raw::c_uint,
15};
16
17pub(crate) fn fmt_pascal_case(f: &mut Formatter<'_>, s: &str) -> fmt::Result {
18    for part in s.split(|c: char| c == ':' || c.is_whitespace()) {
19        if part.is_empty() {
20            continue;
21        }
22
23        let mut chars = part.chars();
24        if let Some(first) = chars.next() {
25            f.write_char(first.to_ascii_uppercase())?;
26            f.write_str(chars.as_str())?;
27        }
28    }
29
30    Ok(())
31}
32
33pub use spa_sys::spa_fraction as Fraction;
34pub use spa_sys::spa_point as Point;
35pub use spa_sys::spa_rectangle as Rectangle;
36pub use spa_sys::spa_region as Region;
37
38use crate::pod::CanonicalFixedSizedPod;
39
40/// An enumerated value in a pod
41#[derive(Debug, Copy, Clone, Eq, PartialEq)]
42pub struct Id(pub u32);
43
44/// A file descriptor in a pod
45#[derive(Debug, Copy, Clone, Eq, PartialEq)]
46#[repr(transparent)]
47pub struct Fd(pub i64);
48
49#[derive(Debug, Eq, PartialEq, Clone)]
50/// the flags and choice of a choice pod.
51pub struct Choice<T: CanonicalFixedSizedPod>(pub ChoiceFlags, pub ChoiceEnum<T>);
52
53bitflags! {
54    /// [`Choice`] flags
55    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
56    pub struct ChoiceFlags: u32 {
57        // no flags defined yet but we need at least one to keep bitflags! happy
58        #[doc(hidden)]
59        const _FAKE = 1;
60    }
61}
62
63#[derive(Debug, PartialEq, Eq, Clone)]
64/// a choice in a pod.
65pub enum ChoiceEnum<T: CanonicalFixedSizedPod> {
66    /// no choice.
67    None(T),
68    /// range.
69    Range {
70        /// default value.
71        default: T,
72        /// minimum value.
73        min: T,
74        /// maximum value.
75        max: T,
76    },
77    /// range with step.
78    Step {
79        /// default value.
80        default: T,
81        /// minimum value.
82        min: T,
83        /// maximum value.
84        max: T,
85        /// step.
86        step: T,
87    },
88    /// list.
89    Enum {
90        /// default value.
91        default: T,
92        /// alternative values.
93        alternatives: Vec<T>,
94    },
95    /// flags.
96    Flags {
97        /// default value.
98        default: T,
99        /// possible flags.
100        flags: Vec<T>,
101    },
102}
103
104#[derive(Copy, Clone, PartialEq, Eq)]
105pub struct SpaTypes(pub c_uint);
106
107#[allow(non_upper_case_globals)]
108impl SpaTypes {
109    /* Basic types */
110    pub const None: Self = Self(spa_sys::SPA_TYPE_None);
111    pub const Bool: Self = Self(spa_sys::SPA_TYPE_Bool);
112    pub const Id: Self = Self(spa_sys::SPA_TYPE_Id);
113    pub const Int: Self = Self(spa_sys::SPA_TYPE_Int);
114    pub const Long: Self = Self(spa_sys::SPA_TYPE_Long);
115    pub const Float: Self = Self(spa_sys::SPA_TYPE_Float);
116    pub const Double: Self = Self(spa_sys::SPA_TYPE_Double);
117    pub const String: Self = Self(spa_sys::SPA_TYPE_String);
118    pub const Bytes: Self = Self(spa_sys::SPA_TYPE_Bytes);
119    pub const Rectangle: Self = Self(spa_sys::SPA_TYPE_Rectangle);
120    pub const Fraction: Self = Self(spa_sys::SPA_TYPE_Fraction);
121    pub const Bitmap: Self = Self(spa_sys::SPA_TYPE_Bitmap);
122    pub const Array: Self = Self(spa_sys::SPA_TYPE_Array);
123    pub const Struct: Self = Self(spa_sys::SPA_TYPE_Struct);
124    pub const Object: Self = Self(spa_sys::SPA_TYPE_Object);
125    pub const Sequence: Self = Self(spa_sys::SPA_TYPE_Sequence);
126    pub const Pointer: Self = Self(spa_sys::SPA_TYPE_Pointer);
127    pub const Fd: Self = Self(spa_sys::SPA_TYPE_Fd);
128    pub const Choice: Self = Self(spa_sys::SPA_TYPE_Choice);
129    pub const Pod: Self = Self(spa_sys::SPA_TYPE_Pod);
130
131    /* Pointers */
132    pub const PointerBuffer: Self = Self(spa_sys::SPA_TYPE_POINTER_Buffer);
133    pub const PointerMeta: Self = Self(spa_sys::SPA_TYPE_POINTER_Meta);
134    pub const PointerDict: Self = Self(spa_sys::SPA_TYPE_POINTER_Dict);
135
136    /* Events */
137    pub const EventDevice: Self = Self(spa_sys::SPA_TYPE_EVENT_Device);
138    pub const EventNode: Self = Self(spa_sys::SPA_TYPE_EVENT_Node);
139
140    /* Commands */
141    pub const CommandDevice: Self = Self(spa_sys::SPA_TYPE_COMMAND_Device);
142    pub const CommandNode: Self = Self(spa_sys::SPA_TYPE_COMMAND_Node);
143
144    /* Objects */
145    pub const ObjectParamPropInfo: Self = Self(spa_sys::SPA_TYPE_OBJECT_PropInfo);
146    pub const ObjectParamProps: Self = Self(spa_sys::SPA_TYPE_OBJECT_Props);
147    pub const ObjectParamFormat: Self = Self(spa_sys::SPA_TYPE_OBJECT_Format);
148    pub const ObjectParamBuffers: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamBuffers);
149    pub const ObjectParamMeta: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamMeta);
150    pub const ObjectParamIO: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamIO);
151    pub const ObjectParamProfile: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamProfile);
152    pub const ObjectParamPortConfig: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamPortConfig);
153    pub const ObjectParamRoute: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamRoute);
154    pub const ObjectProfiler: Self = Self(spa_sys::SPA_TYPE_OBJECT_Profiler);
155    pub const ObjectParamLatency: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamLatency);
156    pub const ObjectParamProcessLatency: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamProcessLatency);
157
158    /* vendor extensions */
159    pub const VendorPipeWire: Self = Self(spa_sys::SPA_TYPE_VENDOR_PipeWire);
160
161    pub const VendorOther: Self = Self(spa_sys::SPA_TYPE_VENDOR_Other);
162
163    /// Obtain a [`SpaTypes`] from a raw `c_uint` variant.
164    pub fn from_raw(raw: c_uint) -> Self {
165        Self(raw)
166    }
167
168    /// Get the raw [`c_uint`] representing this `SpaTypes`.
169    pub fn as_raw(&self) -> c_uint {
170        self.0
171    }
172}
173
174impl Debug for SpaTypes {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        match *self {
177            SpaTypes::VendorPipeWire => f.write_str("SpaTypes::VendorPipeWire"),
178            SpaTypes::VendorOther => f.write_str("SpaTypes::VendorOther"),
179            _ => {
180                let c_str = unsafe {
181                    let c_buf =
182                        spa_sys::spa_debug_type_find_name(spa_sys::spa_types, self.as_raw());
183                    if c_buf.is_null() {
184                        return f.write_str("Unknown");
185                    }
186                    CStr::from_ptr(c_buf)
187                };
188                let replaced = c_str
189                    .to_string_lossy()
190                    .replace("Spa:Pointer", "Pointer")
191                    .replace("Spa:Pod:Object:Event", "Event")
192                    .replace("Spa:Pod:Object:Command", "Command")
193                    .replace("Spa:Pod:Object", "Object")
194                    .replace("Spa:Pod:", "")
195                    .replace("Spa:", "")
196                    .replace(':', " ");
197                f.write_str("SpaTypes::")?;
198                fmt_pascal_case(f, &replaced)
199            }
200        }
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    fn test_fmt_pascal_case() {
210        fn format_pascal(s: &str) -> String {
211            struct PascalCase<'a>(&'a str);
212            impl<'a> std::fmt::Display for PascalCase<'a> {
213                fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
214                    fmt_pascal_case(f, self.0)
215                }
216            }
217            format!("{}", PascalCase(s))
218        }
219
220        assert_eq!(format_pascal("hello"), "Hello");
221        assert_eq!(format_pascal("hello:world"), "HelloWorld");
222        assert_eq!(format_pascal("hello world"), "HelloWorld");
223        assert_eq!(format_pascal("hello:world test"), "HelloWorldTest");
224        assert_eq!(format_pascal("Hello:World"), "HelloWorld");
225        assert_eq!(format_pascal("hello::world  test"), "HelloWorldTest");
226        assert_eq!(format_pascal(":hello world: "), "HelloWorld");
227        assert_eq!(format_pascal("a:b c"), "ABC");
228        assert_eq!(format_pascal("foo:bar:baz"), "FooBarBaz");
229        assert_eq!(format_pascal("fOo:bAr"), "FOoBAr");
230        assert_eq!(
231            format_pascal("Spa:Pod:Object:Param:Format"),
232            "SpaPodObjectParamFormat"
233        );
234        assert_eq!(format_pascal(""), "");
235        assert_eq!(format_pascal(":::   "), "");
236    }
237
238    #[test]
239    #[cfg_attr(miri, ignore)]
240    fn debug_format() {
241        assert_eq!("SpaTypes::None", format!("{:?}", SpaTypes::None));
242        assert_eq!(
243            "SpaTypes::PointerBuffer",
244            format!("{:?}", SpaTypes::PointerBuffer)
245        );
246        assert_eq!(
247            "SpaTypes::EventDevice",
248            format!("{:?}", SpaTypes::EventDevice)
249        );
250        assert_eq!(
251            "SpaTypes::CommandDevice",
252            format!("{:?}", SpaTypes::CommandDevice)
253        );
254        assert_eq!(
255            "SpaTypes::ObjectParamPropInfo",
256            format!("{:?}", SpaTypes::ObjectParamPropInfo)
257        );
258        assert_eq!(
259            "SpaTypes::ObjectProfiler",
260            format!("{:?}", SpaTypes::ObjectProfiler)
261        );
262        assert_eq!(
263            "SpaTypes::ObjectParamProcessLatency",
264            format!("{:?}", SpaTypes::ObjectParamProcessLatency)
265        );
266        assert_eq!(
267            "SpaTypes::VendorPipeWire",
268            format!("{:?}", SpaTypes::VendorPipeWire)
269        );
270        assert_eq!(
271            "SpaTypes::VendorOther",
272            format!("{:?}", SpaTypes::VendorOther)
273        );
274    }
275}