1#![allow(clippy::unnecessary_wraps)]
2#![allow(clippy::needless_pass_by_value)]
3
4use std::borrow::ToOwned;
5use std::collections::HashMap;
6use std::fmt::Debug;
7use std::path::Path;
8
9use ini::{Ini, Properties};
10use lazy_static::lazy_static;
11use regex::Regex;
12use uuid::Uuid;
13
14use super::parse::{parse_bool, parse_int, parse_string, parse_unique_id};
15use crate::common::UniqueId;
16use crate::error::ErrorKind;
17use crate::Error;
18
19lazy_static! {
20 static ref DOC_RE: Regex = Regex::new(r"Document\d+").unwrap();
22}
23
24#[non_exhaustive]
26pub struct PrjPcb {
27 design: Design,
28 preferences: Option<Preferences>,
29 release: Option<Release>,
30 documents: Vec<Document>,
31 variants: Vec<Variant>,
32 parameters: Vec<HashMap<String, String>>,
33 configurations: Vec<Configuration>,
34 original: Ini,
35}
36
37impl PrjPcb {
38 fn design(&self) -> &Design {
39 &self.design
40 }
41
42 fn preferences(&self) -> Option<&Preferences> {
43 self.preferences.as_ref()
44 }
45
46 fn release(&self) -> Option<&Release> {
47 self.release.as_ref()
48 }
49
50 pub fn documents(&self) -> &[Document] {
52 &self.documents
53 }
54
55 pub fn from_file<P: AsRef<Path>>(filename: P) -> Result<Self, Error> {
57 let ini = Ini::load_from_file(filename)?;
58 Self::from_ini(ini)
59 }
60
61 pub fn from_string(s: &str) -> Result<Self, Error> {
63 let ini = Ini::load_from_str(s)?;
64 Self::from_ini(ini)
65 }
66
67 fn from_ini(ini: Ini) -> Result<Self, Error> {
68 for (i, s) in ini.iter().take(10).enumerate() {
69 eprintln!("{i}:\n{s:#?}\n");
70 }
71
72 Ok(Self {
73 design: Design::from_prj_ini(ini)?,
74 preferences: todo!(),
75 release: todo!(),
76 documents: todo!(),
77 variants: todo!(),
78 parameters: todo!(),
79 configurations: todo!(),
80 original: todo!(),
81 })
82 }
83}
84
85impl Debug for PrjPcb {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 f.debug_struct("PrjPcb")
88 .field("design", &self.design)
89 .field("preferences", &self.preferences)
90 .field("release", &self.release)
91 .field("documents", &self.documents)
92 .field("variants", &self.variants)
93 .field("parameters", &self.parameters)
94 .field("configurations", &self.configurations)
95 .finish_non_exhaustive()
96 }
97}
98
99#[non_exhaustive]
101#[derive(Debug, PartialEq)]
102pub struct Design {
103 original: Properties,
104}
105
106impl Design {
107 fn from_prj_ini(ini: Ini) -> Result<Self, ErrorKind> {
108 let sec = ini
109 .section(Some("Design"))
110 .ok_or(ErrorKind::MissingSection("Design".to_owned()))?;
111
112 Ok(Self {
113 original: sec.clone(),
114 })
115 }
116
117 fn to_ini(&self) -> &Properties {
118 &self.original
119 }
120}
121
122#[non_exhaustive]
123#[derive(Debug, PartialEq)]
124pub struct Preferences {
125 original: Properties,
126}
127
128impl Preferences {
129 fn from_prj_ini(ini: Ini) -> Result<Option<Self>, ErrorKind> {
130 Ok(ini.section(Some("Preferences")).map(|sec| Self {
131 original: sec.clone(),
132 }))
133 }
134}
135
136#[non_exhaustive]
137#[derive(Debug, PartialEq)]
138pub struct Release {
139 original: Properties,
140}
141
142impl Release {
143 fn from_prj_ini(ini: &Ini) -> Result<Self, ErrorKind> {
144 todo!()
145 }
146}
147
148#[non_exhaustive]
150#[derive(Debug, PartialEq)]
151pub struct Document {
152 document_path: String,
153 annotation_en: bool,
154 annotation_start_value: i32,
155 annotation_idx_ctrl_en: bool,
156 annotation_suffix: String,
157 annotate_order: i32,
158 do_libarary_update: bool,
159 do_database_update: bool,
160 class_gen_cc_auto_en: bool,
161 class_gen_cc_auto_room_en: bool,
162 class_gen_nc_auto_scope: String,
163 item_revision_guid: String,
164 generate_class_cluster: bool,
165 document_unique_id: UniqueId,
166}
167
168impl Document {
169 pub fn path(&self) -> &str {
171 &self.document_path
172 }
173
174 pub fn unique_id(&self) -> UniqueId {
176 self.document_unique_id
177 }
178
179 fn from_prj_ini(ini: &Ini) -> Result<Vec<Self>, Error> {
181 let mut doc_sections: Vec<&str> = ini
182 .sections()
183 .filter_map(|nameopt| {
184 nameopt.and_then(|name| {
185 if DOC_RE.is_match(name) {
186 Some(name)
187 } else {
188 None
189 }
190 })
191 })
192 .collect();
193
194 doc_sections.sort_by_key(|s| s.strip_prefix("Document").unwrap().parse::<i32>().unwrap());
195
196 let mut ret = Vec::new();
197 let sec_iter = doc_sections
198 .iter()
199 .map(|sec_name| ini.section(Some(*sec_name)).unwrap())
200 .map(Self::from_section);
201
202 for sec_opt in sec_iter {
203 ret.push(sec_opt?);
204 }
205
206 Ok(ret)
207 }
208
209 fn from_section(sec: &Properties) -> Result<Self, Error> {
211 Ok(Self {
212 document_path: parse_string(sec, "DocumentPath"),
213 annotation_en: parse_bool(sec, "AnnotationEnabled"),
214 annotation_start_value: parse_int(sec, "AnnotateStartValue"),
215 annotation_idx_ctrl_en: parse_bool(sec, "AnnotationIndexControlEnabled"),
216 annotation_suffix: parse_string(sec, "AnnotateSuffix"),
217 annotate_order: parse_int(sec, "AnnotateScope"),
218 do_libarary_update: parse_bool(sec, "DoLibraryUpdate"),
219 do_database_update: parse_bool(sec, "DoDatabaseUpdate"),
220 class_gen_cc_auto_en: parse_bool(sec, "ClassGenCCAutoEnabled"),
221 class_gen_cc_auto_room_en: parse_bool(sec, "ClassGenCCAutoRoomEnabled"),
222 class_gen_nc_auto_scope: parse_string(sec, "ClassGenNCAutoScope"),
223 item_revision_guid: parse_string(sec, "DItemRevisionGUID"),
224 generate_class_cluster: parse_bool(sec, "GenerateClassCluster"),
225 document_unique_id: parse_unique_id(sec, "DocumentUniqueId")?,
226 })
227 }
228}
229
230#[non_exhaustive]
231#[derive(Debug, PartialEq)]
232pub struct Variant {
233 unique_id: Uuid,
234 description: String,
235 allow_fabrication: bool,
236 parameter_count: u32,
237 variations: Vec<Variation>,
238}
239
240impl Variant {
241 fn from_prj_ini(ini: &Ini) -> Result<Self, ErrorKind> {
242 todo!()
243 }
244}
245
246#[non_exhaustive]
247#[derive(Debug, PartialEq)]
248pub struct Variation {}
249
250impl Variation {
251 fn from_prj_ini(ini: &Ini) -> Result<Self, ErrorKind> {
252 todo!()
253 }
254}
255
256#[non_exhaustive]
257#[derive(Debug, PartialEq)]
258pub struct Configuration {
259 name: String,
260 }
262
263impl Configuration {
264 fn from_prj_ini(ini: &Ini) -> Result<Self, ErrorKind> {
265 todo!()
266 }
267}