1use crate::error::{FFTError, FFTResult};
10use scirs2_core::numeric::Complex64;
11use std::collections::HashMap;
12use std::sync::{Arc, Mutex, OnceLock};
13
14pub trait FftBackend: Send + Sync {
16 fn name(&self) -> &str;
18
19 fn description(&self) -> &str;
21
22 fn is_available(&self) -> bool;
24
25 fn fft(&self, input: &[Complex64], output: &mut [Complex64]) -> FFTResult<()>;
27
28 fn ifft(&self, input: &[Complex64], output: &mut [Complex64]) -> FFTResult<()>;
30
31 fn fft_sized(
33 &self,
34 input: &[Complex64],
35 output: &mut [Complex64],
36 size: usize,
37 ) -> FFTResult<()>;
38
39 fn ifft_sized(
41 &self,
42 input: &[Complex64],
43 output: &mut [Complex64],
44 size: usize,
45 ) -> FFTResult<()>;
46
47 fn supports_feature(&self, feature: &str) -> bool;
49}
50
51pub struct BackendManager {
53 backends: Arc<Mutex<HashMap<String, Arc<dyn FftBackend>>>>,
54 current_backend: Arc<Mutex<String>>,
55}
56
57impl BackendManager {
58 pub fn new() -> Self {
60 let backends = HashMap::new();
61 let default_backend = "none".to_string();
62
63 Self {
64 backends: Arc::new(Mutex::new(backends)),
65 current_backend: Arc::new(Mutex::new(default_backend)),
66 }
67 }
68
69 pub fn register_backend(&self, name: String, backend: Arc<dyn FftBackend>) -> FFTResult<()> {
71 let mut backends = self.backends.lock().expect("Operation failed");
72 if backends.contains_key(&name) {
73 return Err(FFTError::ValueError(format!(
74 "Backend '{name}' already exists"
75 )));
76 }
77 backends.insert(name, backend);
78 Ok(())
79 }
80
81 pub fn list_backends(&self) -> Vec<String> {
83 let backends = self.backends.lock().expect("Operation failed");
84 backends.keys().cloned().collect()
85 }
86
87 pub fn set_backend(&self, name: &str) -> FFTResult<()> {
89 let backends = self.backends.lock().expect("Operation failed");
90 if !backends.contains_key(name) {
91 if backends.is_empty() || name == "none" {
93 return Ok(());
94 }
95 return Err(FFTError::ValueError(format!("Backend '{name}' not found")));
96 }
97
98 if let Some(backend) = backends.get(name) {
100 if !backend.is_available() {
101 return Err(FFTError::ValueError(format!(
102 "Backend '{name}' is not available"
103 )));
104 }
105 }
106
107 *self.current_backend.lock().expect("Operation failed") = name.to_string();
108 Ok(())
109 }
110
111 pub fn get_backend_name(&self) -> String {
113 self.current_backend
114 .lock()
115 .expect("Operation failed")
116 .clone()
117 }
118
119 #[allow(dead_code)]
121 pub fn get_backend(&self) -> Option<Arc<dyn FftBackend>> {
122 let current_name = self.current_backend.lock().expect("Operation failed");
123 let backends = self.backends.lock().expect("Operation failed");
124 backends.get(&*current_name).cloned()
125 }
126
127 pub fn get_backend_info(&self, name: &str) -> Option<BackendInfo> {
129 let backends = self.backends.lock().expect("Operation failed");
130 backends.get(name).map(|backend| BackendInfo {
131 name: backend.name().to_string(),
132 description: backend.description().to_string(),
133 available: backend.is_available(),
134 })
135 }
136}
137
138impl Default for BackendManager {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct BackendInfo {
147 pub name: String,
148 pub description: String,
149 pub available: bool,
150}
151
152impl std::fmt::Display for BackendInfo {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 write!(
155 f,
156 "{} - {} ({})",
157 self.name,
158 self.description,
159 if self.available {
160 "available"
161 } else {
162 "not available"
163 }
164 )
165 }
166}
167
168static GLOBAL_BACKEND_MANAGER: OnceLock<BackendManager> = OnceLock::new();
170
171#[allow(dead_code)]
173pub fn get_backend_manager() -> &'static BackendManager {
174 GLOBAL_BACKEND_MANAGER.get_or_init(BackendManager::new)
175}
176
177#[allow(dead_code)]
179pub fn init_backend_manager(manager: BackendManager) -> Result<(), &'static str> {
180 GLOBAL_BACKEND_MANAGER
181 .set(manager)
182 .map_err(|_| "Global backend _manager already initialized")
183}
184
185#[allow(dead_code)]
187pub fn list_backends() -> Vec<String> {
188 get_backend_manager().list_backends()
189}
190
191#[allow(dead_code)]
193pub fn set_backend(name: &str) -> FFTResult<()> {
194 get_backend_manager().set_backend(name)
195}
196
197#[allow(dead_code)]
199pub fn get_backend_name() -> String {
200 get_backend_manager().get_backend_name()
201}
202
203#[allow(dead_code)]
205pub fn get_backend_info(name: &str) -> Option<BackendInfo> {
206 get_backend_manager().get_backend_info(name)
207}
208
209pub struct BackendContext {
211 previous_backend: String,
212 manager: &'static BackendManager,
213}
214
215impl BackendContext {
216 pub fn new(_backendname: &str) -> FFTResult<Self> {
221 let manager = get_backend_manager();
222 let previous_backend = manager.get_backend_name();
223
224 let _ = manager.set_backend(_backendname);
226
227 Ok(Self {
228 previous_backend,
229 manager,
230 })
231 }
232}
233
234impl Drop for BackendContext {
235 fn drop(&mut self) {
236 let _ = self.manager.set_backend(&self.previous_backend);
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_backend_manager_new() {
247 let manager = BackendManager::new();
248 assert_eq!(manager.get_backend_name(), "none");
249 assert!(manager.list_backends().is_empty());
250 }
251
252 #[test]
253 fn test_backend_context_unknown() {
254 let ctx = BackendContext::new("oxifft");
256 assert!(ctx.is_ok());
257 }
258}