1pub 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#[derive(Debug, Copy, Clone, Eq, PartialEq)]
40pub struct Id(pub u32);
41
42#[derive(Debug, Copy, Clone, Eq, PartialEq)]
44#[repr(transparent)]
45pub struct Fd(pub i64);
46
47#[derive(Debug, Eq, PartialEq, Clone)]
48pub struct Choice<T: CanonicalFixedSizedPod>(pub ChoiceFlags, pub ChoiceEnum<T>);
50
51bitflags! {
52 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
54 pub struct ChoiceFlags: u32 {
55 #[doc(hidden)]
57 const _FAKE = 1;
58 }
59}
60
61#[derive(Debug, PartialEq, Eq, Clone)]
62pub enum ChoiceEnum<T: CanonicalFixedSizedPod> {
64 None(T),
66 Range {
68 default: T,
70 min: T,
72 max: T,
74 },
75 Step {
77 default: T,
79 min: T,
81 max: T,
83 step: T,
85 },
86 Enum {
88 default: T,
90 alternatives: Vec<T>,
92 },
93 Flags {
95 default: T,
97 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 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 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 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 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 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 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 pub fn from_raw(raw: c_uint) -> Self {
163 Self(raw)
164 }
165
166 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}