1use std::collections::HashMap;
9use std::fs::File;
10use std::io::BufReader;
11use std::path::Path;
12
13use crate::io::IntLib;
14use crate::ops::output::*;
15
16fn open_intlib(path: &Path) -> Result<IntLib, Box<dyn std::error::Error>> {
17 let file = BufReader::new(File::open(path)?);
18 let intlib = IntLib::open(file)?;
19 Ok(intlib)
20}
21
22pub fn cmd_overview(path: &Path, full: bool) -> Result<IntLibOverview, Box<dyn std::error::Error>> {
23 let intlib = open_intlib(path)?;
24
25 let mut footprint_counts: HashMap<String, usize> = HashMap::new();
27 for xref in &intlib.cross_refs {
28 *footprint_counts.entry(xref.footprint.clone()).or_insert(0) += 1;
29 }
30
31 let mut footprint_usage: Vec<_> = footprint_counts.into_iter().collect();
32 footprint_usage.sort_by(|a, b| b.1.cmp(&a.1));
33
34 let component_list: Vec<ComponentCrossRef> = intlib
35 .cross_refs
36 .iter()
37 .map(|xref| ComponentCrossRef {
38 name: xref.name.clone(),
39 description: xref.description.clone(),
40 footprint: xref.footprint.clone(),
41 })
42 .collect();
43
44 let (symbols, footprints, parameters) = if full {
46 let symbols: Vec<SymbolSummary> = intlib
47 .schlib
48 .iter()
49 .map(|comp| SymbolSummary {
50 name: comp.name().to_string(),
51 description: comp.description().to_string(),
52 pin_count: comp.pin_count(),
53 })
54 .collect();
55
56 let footprints: Vec<FootprintSummary> = intlib
57 .pcblib
58 .iter()
59 .map(|comp| FootprintSummary {
60 name: comp.pattern.clone(),
61 description: comp.description.clone(),
62 pad_count: comp.pad_count(),
63 })
64 .collect();
65
66 let parameters: Vec<ComponentParameters> = intlib
67 .parameters
68 .iter()
69 .map(|p| {
70 let params = p
71 .params
72 .iter()
73 .map(|(k, v)| (k.to_string(), v.as_str().to_string()))
74 .collect();
75 ComponentParameters {
76 component_name: p.name.clone(),
77 params,
78 }
79 })
80 .collect();
81
82 (Some(symbols), Some(footprints), Some(parameters))
83 } else {
84 (None, None, None)
85 };
86
87 Ok(IntLibOverview {
88 path: path.display().to_string(),
89 version: intlib.version,
90 component_count: intlib.cross_refs.len(),
91 schematic_symbol_count: intlib.schematic_component_count(),
92 pcb_footprint_count: intlib.footprint_count(),
93 parameter_set_count: intlib.parameters.len(),
94 footprint_usage,
95 component_list,
96 symbols,
97 footprints,
98 parameters,
99 })
100}
101
102pub fn cmd_list(path: &Path) -> Result<IntLibComponentList, Box<dyn std::error::Error>> {
103 let intlib = open_intlib(path)?;
104
105 let components: Vec<ComponentCrossRef> = intlib
106 .cross_refs
107 .iter()
108 .map(|xref| ComponentCrossRef {
109 name: xref.name.clone(),
110 description: xref.description.clone(),
111 footprint: xref.footprint.clone(),
112 })
113 .collect();
114
115 Ok(IntLibComponentList { components })
116}
117
118pub fn cmd_search(
119 path: &Path,
120 query: &str,
121 limit: Option<usize>,
122) -> Result<IntLibSearchResults, Box<dyn std::error::Error>> {
123 let intlib = open_intlib(path)?;
124 let query_lower = query.to_lowercase();
125
126 let matches: Vec<ComponentCrossRef> = intlib
127 .cross_refs
128 .iter()
129 .filter(|xref| {
130 xref.name.to_lowercase().contains(&query_lower)
131 || xref.description.to_lowercase().contains(&query_lower)
132 || xref.footprint.to_lowercase().contains(&query_lower)
133 })
134 .map(|xref| ComponentCrossRef {
135 name: xref.name.clone(),
136 description: xref.description.clone(),
137 footprint: xref.footprint.clone(),
138 })
139 .collect();
140
141 let total_matches = matches.len();
142 let results = if let Some(limit) = limit {
143 matches.into_iter().take(limit).collect()
144 } else {
145 matches
146 };
147
148 Ok(IntLibSearchResults {
149 query: query.to_string(),
150 total_matches,
151 results,
152 })
153}
154
155pub fn cmd_info(path: &Path) -> Result<IntLibInfo, Box<dyn std::error::Error>> {
156 let intlib = open_intlib(path)?;
157
158 Ok(IntLibInfo {
159 path: path.display().to_string(),
160 version: intlib.version,
161 cross_ref_count: intlib.cross_refs.len(),
162 schematic_symbol_count: intlib.schematic_component_count(),
163 pcb_footprint_count: intlib.footprint_count(),
164 parameter_set_count: intlib.parameters.len(),
165 })
166}
167
168pub fn cmd_component(
169 path: &Path,
170 name: &str,
171 show_params: bool,
172) -> Result<IntLibComponentDetail, Box<dyn std::error::Error>> {
173 let intlib = open_intlib(path)?;
174
175 let xref = intlib
177 .cross_refs
178 .iter()
179 .find(|x| x.name == name)
180 .ok_or_else(|| format!("Component '{}' not found", name))?;
181
182 let symbol_info = intlib
184 .schlib
185 .iter()
186 .find(|c| c.name() == name)
187 .map(|comp| SymbolInfo {
188 pin_count: comp.pin_count(),
189 primitive_count: comp.primitive_count(),
190 });
191
192 let footprint_info = intlib
194 .pcblib
195 .iter()
196 .find(|c| c.pattern == xref.footprint)
197 .map(|comp| FootprintInfo {
198 pad_count: comp.pad_count(),
199 primitive_count: comp.primitives.len(),
200 });
201
202 let parameters = if show_params {
203 intlib
204 .parameters
205 .iter()
206 .find(|p| p.name == name)
207 .map(|params| {
208 params
209 .params
210 .iter()
211 .map(|(key, value)| (key.to_string(), value.as_str().to_string()))
212 .collect()
213 })
214 } else {
215 None
216 };
217
218 Ok(IntLibComponentDetail {
219 name: xref.name.clone(),
220 description: xref.description.clone(),
221 footprint: xref.footprint.clone(),
222 schlib_path: xref.schlib_path.clone(),
223 pcblib_path: xref.pcblib_path.clone(),
224 symbol_info,
225 footprint_info,
226 parameters,
227 })
228}
229
230pub fn cmd_crossrefs(
231 path: &Path,
232 footprint_filter: Option<&str>,
233) -> Result<IntLibComponentList, Box<dyn std::error::Error>> {
234 let intlib = open_intlib(path)?;
235
236 let refs: Vec<ComponentCrossRef> = intlib
237 .cross_refs
238 .iter()
239 .filter(|x| {
240 footprint_filter
241 .is_none_or(|filter| x.footprint.to_lowercase().contains(&filter.to_lowercase()))
242 })
243 .map(|xref| ComponentCrossRef {
244 name: xref.name.clone(),
245 description: xref.description.clone(),
246 footprint: xref.footprint.clone(),
247 })
248 .collect();
249
250 Ok(IntLibComponentList { components: refs })
251}
252
253pub fn cmd_symbols(path: &Path) -> Result<IntLibSymbolList, Box<dyn std::error::Error>> {
254 let intlib = open_intlib(path)?;
255
256 let symbols: Vec<SymbolSummary> = intlib
257 .schlib
258 .iter()
259 .map(|comp| SymbolSummary {
260 name: comp.name().to_string(),
261 description: comp.description().to_string(),
262 pin_count: comp.pin_count(),
263 })
264 .collect();
265
266 Ok(IntLibSymbolList { symbols })
267}
268
269pub fn cmd_footprints(path: &Path) -> Result<IntLibFootprintList, Box<dyn std::error::Error>> {
270 let intlib = open_intlib(path)?;
271
272 let footprints: Vec<FootprintSummary> = intlib
273 .pcblib
274 .iter()
275 .map(|comp| FootprintSummary {
276 name: comp.pattern.clone(),
277 description: comp.description.clone(),
278 pad_count: comp.pad_count(),
279 })
280 .collect();
281
282 Ok(IntLibFootprintList { footprints })
283}
284
285pub fn cmd_parameters(
286 path: &Path,
287 component_filter: Option<&str>,
288 keys_filter: Option<&str>,
289) -> Result<IntLibParameterList, Box<dyn std::error::Error>> {
290 let intlib = open_intlib(path)?;
291
292 let keys: Option<Vec<String>> =
293 keys_filter.map(|k| k.split(',').map(|s| s.trim().to_lowercase()).collect());
294
295 let params: Vec<ComponentParameters> = intlib
296 .parameters
297 .iter()
298 .filter(|p| {
299 component_filter
300 .is_none_or(|filter| p.name.to_lowercase().contains(&filter.to_lowercase()))
301 })
302 .map(|p| {
303 let filtered_params = p
304 .params
305 .iter()
306 .filter(|(key, _)| {
307 keys.as_ref()
308 .is_none_or(|k_vec| k_vec.iter().any(|k| key.to_lowercase().contains(k)))
309 })
310 .map(|(k, v)| (k.to_string(), v.as_str().to_string()))
311 .collect();
312 ComponentParameters {
313 component_name: p.name.clone(),
314 params: filtered_params,
315 }
316 })
317 .collect();
318
319 Ok(IntLibParameterList { parameters: params })
320}
321
322pub fn cmd_extract_schlib(
325 path: &Path,
326 output: &Path,
327) -> Result<String, Box<dyn std::error::Error>> {
328 let intlib = open_intlib(path)?;
329
330 intlib.schlib.save_to_file(output)?;
331
332 Ok(format!(
333 "Extracted SchLib to: {}\n Components: {}",
334 output.display(),
335 intlib.schematic_component_count()
336 ))
337}
338
339pub fn cmd_extract_pcblib(
342 path: &Path,
343 output: &Path,
344) -> Result<String, Box<dyn std::error::Error>> {
345 let intlib = open_intlib(path)?;
346
347 intlib.pcblib.save_to_file(output)?;
348
349 Ok(format!(
350 "Extracted PcbLib to: {}\n Footprints: {}",
351 output.display(),
352 intlib.footprint_count()
353 ))
354}