1use crate::device::{Device, DeviceInfo};
7use crate::error::{check_error, QuacError, Result};
8use crate::ffi;
9use crate::types::InitFlags;
10
11use once_cell::sync::OnceCell;
12use parking_lot::Mutex;
13use std::ffi::CStr;
14use std::sync::atomic::{AtomicBool, Ordering};
15
16static INITIALIZED: AtomicBool = AtomicBool::new(false);
18static INIT_LOCK: OnceCell<Mutex<()>> = OnceCell::new();
19
20fn init_lock() -> &'static Mutex<()> {
21 INIT_LOCK.get_or_init(|| Mutex::new(()))
22}
23
24pub fn initialize() -> Result<()> {
29 initialize_with_flags(InitFlags::DEFAULT)
30}
31
32pub fn initialize_with_flags(flags: InitFlags) -> Result<()> {
34 let _guard = init_lock().lock();
35
36 if INITIALIZED.load(Ordering::SeqCst) {
37 return Ok(()); }
39
40 let result = unsafe { ffi::quac_init(flags.bits()) };
41 check_error(result)?;
42
43 INITIALIZED.store(true, Ordering::SeqCst);
44 Ok(())
45}
46
47pub fn cleanup() -> Result<()> {
49 let _guard = init_lock().lock();
50
51 if !INITIALIZED.load(Ordering::SeqCst) {
52 return Ok(()); }
54
55 let result = unsafe { ffi::quac_cleanup() };
56 check_error(result)?;
57
58 INITIALIZED.store(false, Ordering::SeqCst);
59 Ok(())
60}
61
62pub fn is_initialized() -> bool {
64 INITIALIZED.load(Ordering::SeqCst)
65}
66
67pub fn get_version() -> String {
69 unsafe {
70 let ptr = ffi::quac_get_version();
71 if ptr.is_null() {
72 return String::from("unknown");
73 }
74 CStr::from_ptr(ptr)
75 .to_string_lossy()
76 .into_owned()
77 }
78}
79
80pub fn get_build_info() -> String {
82 unsafe {
83 let ptr = ffi::quac_get_build_info();
84 if ptr.is_null() {
85 return String::from("unknown");
86 }
87 CStr::from_ptr(ptr)
88 .to_string_lossy()
89 .into_owned()
90 }
91}
92
93pub fn get_device_count() -> usize {
95 let count = unsafe { ffi::quac_get_device_count() };
96 if count < 0 {
97 0
98 } else {
99 count as usize
100 }
101}
102
103pub fn enumerate_devices() -> Vec<DeviceInfo> {
105 let count = get_device_count();
106 let mut devices = Vec::with_capacity(count);
107
108 for i in 0..count {
109 if let Ok(info) = get_device_info(i as u32) {
110 devices.push(info);
111 }
112 }
113
114 devices
115}
116
117fn get_device_info(index: u32) -> Result<DeviceInfo> {
119 let mut info = ffi::quac_device_info_t::default();
120 let result = unsafe { ffi::quac_get_device_info(index, &mut info) };
121 check_error(result)?;
122 Ok(DeviceInfo::from_ffi(&info))
123}
124
125pub fn open_device(index: u32) -> Result<Device> {
127 if !is_initialized() {
128 return Err(QuacError::NotInitialized);
129 }
130
131 let mut handle = std::ptr::null_mut();
132 let result = unsafe { ffi::quac_open_device(index, &mut handle) };
133 check_error(result)?;
134
135 if handle.is_null() {
136 return Err(QuacError::NullPointer);
137 }
138
139 Ok(Device::from_raw(handle, index))
140}
141
142pub fn open_first_device() -> Result<Device> {
144 if get_device_count() == 0 {
145 return Err(QuacError::DeviceNotFound("No QUAC 100 devices found".into()));
146 }
147 open_device(0)
148}
149
150pub struct LibraryContext {
152 _private: (),
153}
154
155impl LibraryContext {
156 pub fn new() -> Result<Self> {
158 initialize()?;
159 Ok(Self { _private: () })
160 }
161
162 pub fn with_flags(flags: InitFlags) -> Result<Self> {
164 initialize_with_flags(flags)?;
165 Ok(Self { _private: () })
166 }
167}
168
169impl Drop for LibraryContext {
170 fn drop(&mut self) {
171 let _ = cleanup();
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_get_version() {
181 let version = get_version();
182 assert!(!version.is_empty());
183 }
184}