pub mod kicad;
use std::path::Path;
use thiserror::Error;
use super::{Circuit, UnifiedCircuitSchema, SourceCAD};
#[derive(Debug, Error)]
pub enum AdapterError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(String),
#[error("Unsupported format: {0}")]
UnsupportedFormat(String),
#[error("Missing required field: {0}")]
MissingField(String),
#[error("Invalid data: {0}")]
InvalidData(String),
}
pub trait CircuitAdapter {
fn source_cad(&self) -> SourceCAD;
fn supported_extensions(&self) -> &[&str];
fn can_handle(&self, path: &Path) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.map(|ext| {
self.supported_extensions()
.iter()
.any(|&supported| supported.eq_ignore_ascii_case(ext))
})
.unwrap_or(false)
}
fn parse_file(&self, path: &Path) -> Result<UnifiedCircuitSchema, AdapterError>;
fn parse_string(&self, content: &str, filename: &str) -> Result<UnifiedCircuitSchema, AdapterError>;
fn parse_to_circuit(&self, path: &Path) -> Result<Circuit, AdapterError> {
let ucs = self.parse_file(path)?;
Ok(Circuit::from_ucs(ucs))
}
fn parse_string_to_circuit(&self, content: &str, filename: &str) -> Result<Circuit, AdapterError> {
let ucs = self.parse_string(content, filename)?;
Ok(Circuit::from_ucs(ucs))
}
}
pub struct AdapterRegistry {
adapters: Vec<Box<dyn CircuitAdapter + Send + Sync>>,
}
impl AdapterRegistry {
pub fn new() -> Self {
let mut registry = Self {
adapters: Vec::new(),
};
registry.register(Box::new(kicad::KicadAdapter::new()));
registry
}
pub fn register(&mut self, adapter: Box<dyn CircuitAdapter + Send + Sync>) {
self.adapters.push(adapter);
}
pub fn find_adapter(&self, path: &Path) -> Option<&(dyn CircuitAdapter + Send + Sync)> {
self.adapters
.iter()
.find(|a| a.can_handle(path))
.map(|a| a.as_ref())
}
pub fn parse_file(&self, path: &Path) -> Result<UnifiedCircuitSchema, AdapterError> {
let adapter = self.find_adapter(path)
.ok_or_else(|| {
let ext = path.extension()
.and_then(|e| e.to_str())
.unwrap_or("unknown");
AdapterError::UnsupportedFormat(format!("No adapter for .{} files", ext))
})?;
adapter.parse_file(path)
}
pub fn parse_to_circuit(&self, path: &Path) -> Result<Circuit, AdapterError> {
let adapter = self.find_adapter(path)
.ok_or_else(|| {
let ext = path.extension()
.and_then(|e| e.to_str())
.unwrap_or("unknown");
AdapterError::UnsupportedFormat(format!("No adapter for .{} files", ext))
})?;
adapter.parse_to_circuit(path)
}
pub fn supported_extensions(&self) -> Vec<&str> {
self.adapters
.iter()
.flat_map(|a| a.supported_extensions().iter().copied())
.collect()
}
}
impl Default for AdapterRegistry {
fn default() -> Self {
Self::new()
}
}
pub use kicad::KicadAdapter;
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_registry_creation() {
let registry = AdapterRegistry::new();
assert!(!registry.supported_extensions().is_empty());
}
#[test]
fn test_find_adapter() {
let registry = AdapterRegistry::new();
let kicad_path = PathBuf::from("test.kicad_sch");
assert!(registry.find_adapter(&kicad_path).is_some());
let unknown_path = PathBuf::from("test.unknown");
assert!(registry.find_adapter(&unknown_path).is_none());
}
}