1use atoxide_domain::QuantityInterval;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub enum ComponentType {
13 Resistor,
15 Capacitor,
17 Inductor,
19 Diode,
21 Led,
23 Transistor,
25 Mosfet,
27 IntegratedCircuit,
29 Connector,
31 Crystal,
33 Fuse,
35 Other,
37}
38
39impl ComponentType {
40 pub fn endpoint_name(&self) -> &'static str {
42 match self {
43 ComponentType::Resistor => "resistors",
44 ComponentType::Capacitor => "capacitors",
45 ComponentType::Inductor => "inductors",
46 ComponentType::Diode => "diodes",
47 ComponentType::Led => "leds",
48 ComponentType::Transistor => "transistors",
49 ComponentType::Mosfet => "mosfets",
50 ComponentType::IntegratedCircuit => "ics",
51 ComponentType::Connector => "connectors",
52 ComponentType::Crystal => "crystals",
53 ComponentType::Fuse => "fuses",
54 ComponentType::Other => "other",
55 }
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61pub enum ParameterConstraint {
62 Equals(f64),
64 GreaterOrEqual(f64),
66 LessOrEqual(f64),
68 Range { min: f64, max: f64 },
70 Interval(QuantityInterval),
72}
73
74impl ParameterConstraint {
75 pub fn equals(value: f64) -> Self {
77 Self::Equals(value)
78 }
79
80 pub fn at_least(value: f64) -> Self {
82 Self::GreaterOrEqual(value)
83 }
84
85 pub fn at_most(value: f64) -> Self {
87 Self::LessOrEqual(value)
88 }
89
90 pub fn between(min: f64, max: f64) -> Self {
92 Self::Range { min, max }
93 }
94
95 pub fn interval(interval: QuantityInterval) -> Self {
97 Self::Interval(interval)
98 }
99
100 pub fn is_satisfied_by(&self, value: f64) -> bool {
102 match self {
103 Self::Equals(v) => (value - v).abs() < v.abs() * 1e-9 + 1e-15,
104 Self::GreaterOrEqual(min) => value >= *min,
105 Self::LessOrEqual(max) => value <= *max,
106 Self::Range { min, max } => value >= *min && value <= *max,
107 Self::Interval(interval) => {
108 value >= interval.min().value() && value <= interval.max().value()
109 }
110 }
111 }
112
113 pub fn min_value(&self) -> Option<f64> {
115 match self {
116 Self::Equals(v) => Some(*v),
117 Self::GreaterOrEqual(min) => Some(*min),
118 Self::LessOrEqual(_) => None,
119 Self::Range { min, .. } => Some(*min),
120 Self::Interval(interval) => Some(interval.min().value()),
121 }
122 }
123
124 pub fn max_value(&self) -> Option<f64> {
126 match self {
127 Self::Equals(v) => Some(*v),
128 Self::GreaterOrEqual(_) => None,
129 Self::LessOrEqual(max) => Some(*max),
130 Self::Range { max, .. } => Some(*max),
131 Self::Interval(interval) => Some(interval.max().value()),
132 }
133 }
134}
135
136#[derive(Debug, Clone, Default, Serialize, Deserialize)]
138pub struct PartQuery {
139 pub component_type: Option<ComponentType>,
141 pub package: Option<String>,
143 pub parameters: HashMap<String, ParameterConstraint>,
145 pub min_stock: Option<u32>,
147 pub basic_only: bool,
149 pub limit: Option<usize>,
151}
152
153impl PartQuery {
154 pub fn new() -> Self {
156 Self::default()
157 }
158
159 pub fn for_type(component_type: ComponentType) -> Self {
161 Self {
162 component_type: Some(component_type),
163 ..Default::default()
164 }
165 }
166
167 pub fn resistor() -> Self {
169 Self::for_type(ComponentType::Resistor)
170 }
171
172 pub fn capacitor() -> Self {
174 Self::for_type(ComponentType::Capacitor)
175 }
176
177 pub fn inductor() -> Self {
179 Self::for_type(ComponentType::Inductor)
180 }
181
182 pub fn with_package(mut self, package: impl Into<String>) -> Self {
184 self.package = Some(package.into());
185 self
186 }
187
188 pub fn with_param(mut self, name: impl Into<String>, constraint: ParameterConstraint) -> Self {
190 self.parameters.insert(name.into(), constraint);
191 self
192 }
193
194 pub fn with_resistance(self, constraint: ParameterConstraint) -> Self {
196 self.with_param("resistance", constraint)
197 }
198
199 pub fn with_capacitance(self, constraint: ParameterConstraint) -> Self {
201 self.with_param("capacitance", constraint)
202 }
203
204 pub fn with_inductance(self, constraint: ParameterConstraint) -> Self {
206 self.with_param("inductance", constraint)
207 }
208
209 pub fn with_voltage(self, constraint: ParameterConstraint) -> Self {
211 self.with_param("voltage", constraint)
212 }
213
214 pub fn with_power(self, constraint: ParameterConstraint) -> Self {
216 self.with_param("power", constraint)
217 }
218
219 pub fn with_tolerance(self, constraint: ParameterConstraint) -> Self {
221 self.with_param("tolerance", constraint)
222 }
223
224 pub fn with_min_stock(mut self, stock: u32) -> Self {
226 self.min_stock = Some(stock);
227 self
228 }
229
230 pub fn basic_only(mut self) -> Self {
232 self.basic_only = true;
233 self
234 }
235
236 pub fn with_limit(mut self, limit: usize) -> Self {
238 self.limit = Some(limit);
239 self
240 }
241}
242
243#[derive(Debug, Clone, Default)]
245pub struct ResistorQuery {
246 query: PartQuery,
247}
248
249impl ResistorQuery {
250 pub fn new() -> Self {
252 Self {
253 query: PartQuery::resistor(),
254 }
255 }
256
257 pub fn resistance(mut self, ohms: f64) -> Self {
259 self.query = self
260 .query
261 .with_resistance(ParameterConstraint::equals(ohms));
262 self
263 }
264
265 pub fn resistance_range(mut self, min_ohms: f64, max_ohms: f64) -> Self {
267 self.query = self
268 .query
269 .with_resistance(ParameterConstraint::between(min_ohms, max_ohms));
270 self
271 }
272
273 pub fn package(mut self, package: impl Into<String>) -> Self {
275 self.query = self.query.with_package(package);
276 self
277 }
278
279 pub fn power(mut self, watts: f64) -> Self {
281 self.query = self.query.with_power(ParameterConstraint::at_least(watts));
282 self
283 }
284
285 pub fn tolerance(mut self, percent: f64) -> Self {
287 self.query = self
288 .query
289 .with_tolerance(ParameterConstraint::at_most(percent / 100.0));
290 self
291 }
292
293 pub fn build(self) -> PartQuery {
295 self.query
296 }
297}
298
299#[derive(Debug, Clone, Default)]
301pub struct CapacitorQuery {
302 query: PartQuery,
303}
304
305impl CapacitorQuery {
306 pub fn new() -> Self {
308 Self {
309 query: PartQuery::capacitor(),
310 }
311 }
312
313 pub fn capacitance(mut self, farads: f64) -> Self {
315 self.query = self
316 .query
317 .with_capacitance(ParameterConstraint::equals(farads));
318 self
319 }
320
321 pub fn capacitance_range(mut self, min_farads: f64, max_farads: f64) -> Self {
323 self.query = self
324 .query
325 .with_capacitance(ParameterConstraint::between(min_farads, max_farads));
326 self
327 }
328
329 pub fn package(mut self, package: impl Into<String>) -> Self {
331 self.query = self.query.with_package(package);
332 self
333 }
334
335 pub fn voltage(mut self, volts: f64) -> Self {
337 self.query = self
338 .query
339 .with_voltage(ParameterConstraint::at_least(volts));
340 self
341 }
342
343 pub fn build(self) -> PartQuery {
345 self.query
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 #[test]
354 fn test_parameter_constraint() {
355 let eq = ParameterConstraint::equals(10000.0);
356 assert!(eq.is_satisfied_by(10000.0));
357 assert!(!eq.is_satisfied_by(10001.0));
358
359 let range = ParameterConstraint::between(9500.0, 10500.0);
360 assert!(range.is_satisfied_by(10000.0));
361 assert!(range.is_satisfied_by(9500.0));
362 assert!(range.is_satisfied_by(10500.0));
363 assert!(!range.is_satisfied_by(9499.0));
364 assert!(!range.is_satisfied_by(10501.0));
365
366 let at_least = ParameterConstraint::at_least(5.0);
367 assert!(at_least.is_satisfied_by(5.0));
368 assert!(at_least.is_satisfied_by(100.0));
369 assert!(!at_least.is_satisfied_by(4.9));
370 }
371
372 #[test]
373 fn test_part_query() {
374 let query = PartQuery::resistor()
375 .with_package("0402")
376 .with_resistance(ParameterConstraint::between(9500.0, 10500.0))
377 .with_power(ParameterConstraint::at_least(0.0625))
378 .with_min_stock(100)
379 .with_limit(10);
380
381 assert_eq!(query.component_type, Some(ComponentType::Resistor));
382 assert_eq!(query.package, Some("0402".to_string()));
383 assert!(query.parameters.contains_key("resistance"));
384 assert!(query.parameters.contains_key("power"));
385 assert_eq!(query.min_stock, Some(100));
386 assert_eq!(query.limit, Some(10));
387 }
388
389 #[test]
390 fn test_resistor_query_builder() {
391 let query = ResistorQuery::new()
392 .resistance_range(9500.0, 10500.0)
393 .package("0402")
394 .power(0.0625)
395 .tolerance(1.0)
396 .build();
397
398 assert_eq!(query.component_type, Some(ComponentType::Resistor));
399 assert_eq!(query.package, Some("0402".to_string()));
400 assert!(query.parameters.contains_key("resistance"));
401 assert!(query.parameters.contains_key("power"));
402 assert!(query.parameters.contains_key("tolerance"));
403 }
404
405 #[test]
406 fn test_capacitor_query_builder() {
407 let query = CapacitorQuery::new()
408 .capacitance(100e-9) .package("0603")
410 .voltage(16.0)
411 .build();
412
413 assert_eq!(query.component_type, Some(ComponentType::Capacitor));
414 assert_eq!(query.package, Some("0603".to_string()));
415 assert!(query.parameters.contains_key("capacitance"));
416 assert!(query.parameters.contains_key("voltage"));
417 }
418
419 #[test]
420 fn test_component_type() {
421 assert_eq!(ComponentType::Resistor.endpoint_name(), "resistors");
422 assert_eq!(ComponentType::Capacitor.endpoint_name(), "capacitors");
423 assert_eq!(ComponentType::Mosfet.endpoint_name(), "mosfets");
424 }
425}