Skip to main content

altium_format/api/
document.rs

1//! AltiumDocument - unified entry point for Altium files.
2
3use std::collections::HashMap;
4use std::fs::File;
5use std::io::{Read, Seek};
6use std::path::Path;
7
8use crate::api::cfb::{AltiumCfb, AltiumFileType};
9use crate::api::generic::{BinaryContainer, ParamsContainer};
10use crate::error::Result;
11
12/// Unified entry point for working with Altium files.
13///
14/// Provides ergonomic access to any Altium file type through a common
15/// interface, with methods for accessing different abstraction layers.
16pub struct AltiumDocument<R: Read + Seek> {
17    /// Low-level CFB wrapper
18    cfb: AltiumCfb<R>,
19    /// Cached parameter containers by stream path
20    params_containers: HashMap<String, ParamsContainer>,
21    /// Cached binary containers by stream path
22    binary_containers: HashMap<String, BinaryContainer>,
23}
24
25impl AltiumDocument<File> {
26    /// Opens a file by path.
27    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
28        let cfb = AltiumCfb::open_file(path)?;
29        Ok(AltiumDocument {
30            cfb,
31            params_containers: HashMap::new(),
32            binary_containers: HashMap::new(),
33        })
34    }
35}
36
37impl<R: Read + Seek> AltiumDocument<R> {
38    /// Opens from a reader.
39    pub fn from_reader(reader: R) -> Result<Self> {
40        let cfb = AltiumCfb::open(reader)?;
41        Ok(AltiumDocument {
42            cfb,
43            params_containers: HashMap::new(),
44            binary_containers: HashMap::new(),
45        })
46    }
47
48    // --- Layer 1: CFB Access ---
49
50    /// Returns the CFB wrapper for low-level operations.
51    pub fn cfb(&mut self) -> &mut AltiumCfb<R> {
52        &mut self.cfb
53    }
54
55    /// Returns the detected file type.
56    pub fn file_type(&self) -> AltiumFileType {
57        self.cfb.file_type()
58    }
59
60    // --- Layer 2: Generic Access ---
61
62    /// Gets records from a stream as a parameter container.
63    ///
64    /// Loads and caches the container on first access.
65    pub fn params(&mut self, stream_path: &str) -> Result<&ParamsContainer> {
66        if !self.params_containers.contains_key(stream_path) {
67            let blocks = self.cfb.read_blocks(stream_path)?;
68            let container = ParamsContainer::from_blocks(stream_path, &blocks);
69            self.params_containers
70                .insert(stream_path.to_string(), container);
71        }
72        Ok(self.params_containers.get(stream_path).unwrap())
73    }
74
75    /// Gets mutable records from a stream.
76    pub fn params_mut(&mut self, stream_path: &str) -> Result<&mut ParamsContainer> {
77        if !self.params_containers.contains_key(stream_path) {
78            let blocks = self.cfb.read_blocks(stream_path)?;
79            let container = ParamsContainer::from_blocks(stream_path, &blocks);
80            self.params_containers
81                .insert(stream_path.to_string(), container);
82        }
83        Ok(self.params_containers.get_mut(stream_path).unwrap())
84    }
85
86    /// Gets binary records from a stream.
87    pub fn binary(&mut self, stream_path: &str) -> Result<&BinaryContainer> {
88        if !self.binary_containers.contains_key(stream_path) {
89            let blocks = self.cfb.read_blocks(stream_path)?;
90            let container = BinaryContainer::from_blocks(stream_path, &blocks);
91            self.binary_containers
92                .insert(stream_path.to_string(), container);
93        }
94        Ok(self.binary_containers.get(stream_path).unwrap())
95    }
96
97    /// Gets mutable binary records from a stream.
98    pub fn binary_mut(&mut self, stream_path: &str) -> Result<&mut BinaryContainer> {
99        if !self.binary_containers.contains_key(stream_path) {
100            let blocks = self.cfb.read_blocks(stream_path)?;
101            let container = BinaryContainer::from_blocks(stream_path, &blocks);
102            self.binary_containers
103                .insert(stream_path.to_string(), container);
104        }
105        Ok(self.binary_containers.get_mut(stream_path).unwrap())
106    }
107
108    // --- File-Type Helpers ---
109
110    /// For libraries: lists component/footprint names.
111    pub fn component_names(&mut self) -> Result<Vec<String>> {
112        self.cfb.list_components()
113    }
114
115    /// Resolves a component name to its storage path.
116    pub fn resolve_component(&mut self, name: &str) -> Result<String> {
117        self.cfb.resolve_section(name)
118    }
119
120    /// Clears all cached containers.
121    pub fn clear_cache(&mut self) {
122        self.params_containers.clear();
123        self.binary_containers.clear();
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    // Integration tests would go here with actual test files
130}