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