quantacore/
library.rs

1//! Library initialization and management.
2//!
3//! This module provides functions to initialize and clean up the library,
4//! enumerate devices, and open device handles.
5
6use 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
16/// Global initialization state
17static 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
24/// Initialize the QUAC 100 library with default flags.
25///
26/// This must be called before any other library functions.
27/// The library can only be initialized once.
28pub fn initialize() -> Result<()> {
29    initialize_with_flags(InitFlags::DEFAULT)
30}
31
32/// Initialize the QUAC 100 library with custom flags.
33pub fn initialize_with_flags(flags: InitFlags) -> Result<()> {
34    let _guard = init_lock().lock();
35
36    if INITIALIZED.load(Ordering::SeqCst) {
37        return Ok(()); // Already initialized
38    }
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
47/// Clean up the QUAC 100 library.
48pub fn cleanup() -> Result<()> {
49    let _guard = init_lock().lock();
50
51    if !INITIALIZED.load(Ordering::SeqCst) {
52        return Ok(()); // Not initialized
53    }
54
55    let result = unsafe { ffi::quac_cleanup() };
56    check_error(result)?;
57
58    INITIALIZED.store(false, Ordering::SeqCst);
59    Ok(())
60}
61
62/// Check if the library is initialized.
63pub fn is_initialized() -> bool {
64    INITIALIZED.load(Ordering::SeqCst)
65}
66
67/// Get the library version string.
68pub 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
80/// Get the library build information.
81pub 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
93/// Get the number of available QUAC 100 devices.
94pub 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
103/// Enumerate all available QUAC 100 devices.
104pub 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
117/// Get information about a specific device.
118fn 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
125/// Open a QUAC 100 device by index.
126pub 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
142/// Open the first available QUAC 100 device.
143pub 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
150/// RAII guard for library initialization.
151pub struct LibraryContext {
152    _private: (),
153}
154
155impl LibraryContext {
156    /// Create a new library context with default flags.
157    pub fn new() -> Result<Self> {
158        initialize()?;
159        Ok(Self { _private: () })
160    }
161
162    /// Create a new library context with custom flags.
163    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}