1use super::common::{FilterOp, FilterValue, compare_filter};
4
5#[derive(Debug, Clone, PartialEq)]
7pub enum Selector {
8 Element(ElementType),
10
11 Id(String),
13
14 Universal,
16
17 Attribute(AttributeSelector),
19
20 Pseudo(PseudoSelector),
22
23 Compound(Vec<Selector>),
25
26 Combinator {
28 left: Box<Selector>,
29 combinator: CombinatorType,
30 right: Box<Selector>,
31 },
32
33 Union(Vec<Selector>),
35
36 Not(Box<Selector>),
38
39 Has(Box<Selector>),
41}
42
43impl Selector {
44 pub fn has_result_modifier(&self) -> bool {
46 match self {
47 Selector::Pseudo(p) => p.is_result_modifier(),
48 Selector::Compound(parts) => parts.iter().any(|s| s.has_result_modifier()),
49 Selector::Combinator { right, .. } => right.has_result_modifier(),
50 Selector::Union(selectors) => selectors.iter().any(|s| s.has_result_modifier()),
51 _ => false,
52 }
53 }
54
55 pub fn get_result_modifiers(&self) -> Vec<&PseudoSelector> {
57 let mut modifiers = Vec::new();
58 self.collect_result_modifiers(&mut modifiers);
59 modifiers
60 }
61
62 fn collect_result_modifiers<'a>(&'a self, modifiers: &mut Vec<&'a PseudoSelector>) {
63 match self {
64 Selector::Pseudo(p) if p.is_result_modifier() => {
65 modifiers.push(p);
66 }
67 Selector::Compound(parts) => {
68 for part in parts {
69 part.collect_result_modifiers(modifiers);
70 }
71 }
72 Selector::Combinator { right, .. } => {
73 right.collect_result_modifiers(modifiers);
74 }
75 Selector::Union(selectors) => {
76 for sel in selectors {
77 sel.collect_result_modifiers(modifiers);
78 }
79 }
80 _ => {}
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
87pub enum ElementType {
88 Component,
90 Pin,
92 Net,
94 Port,
96 Wire,
98 Power,
100 Ground,
102 Label,
104 Junction,
106 Parameter,
108 Designator,
110 Sheet,
112}
113
114impl ElementType {
115 pub fn try_parse(s: &str) -> Option<Self> {
117 match s.to_lowercase().as_str() {
118 "component" | "comp" | "c" => Some(ElementType::Component),
119 "pin" | "p" => Some(ElementType::Pin),
120 "net" | "n" => Some(ElementType::Net),
121 "port" => Some(ElementType::Port),
122 "wire" | "w" => Some(ElementType::Wire),
123 "power" | "pwr" => Some(ElementType::Power),
124 "ground" | "gnd" => Some(ElementType::Ground),
125 "label" | "netlabel" => Some(ElementType::Label),
126 "junction" | "junc" => Some(ElementType::Junction),
127 "parameter" | "param" => Some(ElementType::Parameter),
128 "designator" | "des" => Some(ElementType::Designator),
129 "sheet" | "document" | "doc" => Some(ElementType::Sheet),
130 _ => None,
131 }
132 }
133
134 pub fn as_str(&self) -> &'static str {
136 match self {
137 ElementType::Component => "component",
138 ElementType::Pin => "pin",
139 ElementType::Net => "net",
140 ElementType::Port => "port",
141 ElementType::Wire => "wire",
142 ElementType::Power => "power",
143 ElementType::Ground => "ground",
144 ElementType::Label => "label",
145 ElementType::Junction => "junction",
146 ElementType::Parameter => "parameter",
147 ElementType::Designator => "designator",
148 ElementType::Sheet => "sheet",
149 }
150 }
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum AttributeOp {
156 Exists,
158 Equals,
160 NotEquals,
162 WordMatch,
164 StartsWith,
166 EndsWith,
168 Contains,
170 GreaterThan,
172 LessThan,
174 GreaterOrEqual,
176 LessOrEqual,
178}
179
180#[derive(Debug, Clone, PartialEq)]
182pub struct AttributeSelector {
183 pub name: String,
185 pub op: AttributeOp,
187 pub value: Option<String>,
189 pub case_insensitive: bool,
191}
192
193impl AttributeSelector {
194 pub fn new(name: impl Into<String>, op: AttributeOp, value: Option<String>) -> Self {
196 Self {
197 name: name.into().to_lowercase(),
198 op,
199 value,
200 case_insensitive: false,
201 }
202 }
203
204 pub fn matches(&self, attr_value: Option<&str>) -> bool {
206 let filter_op = self.op.to_filter_op();
208 let filter_value = self
209 .value
210 .as_ref()
211 .map(|v| FilterValue::String(v.clone()))
212 .unwrap_or_else(|| FilterValue::String(String::new()));
213
214 compare_filter(attr_value, filter_op, &filter_value, self.case_insensitive)
215 }
216}
217
218impl AttributeOp {
219 pub fn to_filter_op(&self) -> FilterOp {
221 match self {
222 AttributeOp::Exists => FilterOp::Exists,
223 AttributeOp::Equals => FilterOp::Equals,
224 AttributeOp::NotEquals => FilterOp::NotEquals,
225 AttributeOp::WordMatch => FilterOp::WordMatch,
226 AttributeOp::StartsWith => FilterOp::StartsWith,
227 AttributeOp::EndsWith => FilterOp::EndsWith,
228 AttributeOp::Contains => FilterOp::Contains,
229 AttributeOp::GreaterThan => FilterOp::GreaterThan,
230 AttributeOp::LessThan => FilterOp::LessThan,
231 AttributeOp::GreaterOrEqual => FilterOp::GreaterOrEqual,
232 AttributeOp::LessOrEqual => FilterOp::LessOrEqual,
233 }
234 }
235
236 pub fn from_filter_op(op: FilterOp) -> Self {
238 match op {
239 FilterOp::Exists => AttributeOp::Exists,
240 FilterOp::Equals => AttributeOp::Equals,
241 FilterOp::NotEquals => AttributeOp::NotEquals,
242 FilterOp::WordMatch => AttributeOp::WordMatch,
243 FilterOp::StartsWith => AttributeOp::StartsWith,
244 FilterOp::EndsWith => AttributeOp::EndsWith,
245 FilterOp::Contains => AttributeOp::Contains,
246 FilterOp::GreaterThan => AttributeOp::GreaterThan,
247 FilterOp::LessThan => AttributeOp::LessThan,
248 FilterOp::GreaterOrEqual => AttributeOp::GreaterOrEqual,
249 FilterOp::LessOrEqual => AttributeOp::LessOrEqual,
250 }
251 }
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum StandardAttribute {
257 Designator,
259 Part,
260 Value,
261 Footprint,
262 Description,
263
264 Name,
266 Number,
267 Type,
268 Hidden,
269
270 NetName,
272
273 IoType,
275
276 Style,
278
279 X,
281 Y,
282}
283
284impl StandardAttribute {
285 pub fn try_parse(s: &str) -> Option<Self> {
287 match s.to_lowercase().as_str() {
288 "designator" | "des" | "ref" | "refdes" => Some(Self::Designator),
289 "part" | "partname" | "libref" | "libreference" => Some(Self::Part),
290 "value" | "val" => Some(Self::Value),
291 "footprint" | "fp" | "package" | "pcbfootprint" => Some(Self::Footprint),
292 "description" | "desc" => Some(Self::Description),
293 "name" | "pinname" => Some(Self::Name),
294 "number" | "num" | "pin" | "pinnum" => Some(Self::Number),
295 "type" | "electrical" | "elec" => Some(Self::Type),
296 "hidden" => Some(Self::Hidden),
297 "netname" | "net" => Some(Self::NetName),
298 "io" | "iotype" | "direction" | "dir" => Some(Self::IoType),
299 "style" => Some(Self::Style),
300 "x" | "locx" | "locationx" => Some(Self::X),
301 "y" | "locy" | "locationy" => Some(Self::Y),
302 _ => None,
303 }
304 }
305
306 pub fn canonical_name(&self) -> &'static str {
308 match self {
309 Self::Designator => "designator",
310 Self::Part => "part",
311 Self::Value => "value",
312 Self::Footprint => "footprint",
313 Self::Description => "description",
314 Self::Name => "name",
315 Self::Number => "number",
316 Self::Type => "type",
317 Self::Hidden => "hidden",
318 Self::NetName => "net",
319 Self::IoType => "io",
320 Self::Style => "style",
321 Self::X => "x",
322 Self::Y => "y",
323 }
324 }
325}
326
327#[derive(Debug, Clone, PartialEq)]
329pub enum PseudoSelector {
330 Connected,
332 Unconnected,
333
334 Power,
336 Ground,
337 Input,
338 Output,
339 Bidirectional,
340 Passive,
341 OpenCollector,
342 OpenEmitter,
343 HiZ,
344
345 Hidden,
347 Visible,
348
349 First,
351 Last,
352 Nth(usize),
353 NthLast(usize),
354 Even,
355 Odd,
356
357 Count,
359 Limit(usize),
360 Offset(usize),
361}
362
363impl PseudoSelector {
364 pub fn try_parse(s: &str, arg: Option<&str>) -> Option<Self> {
366 match s.to_lowercase().as_str() {
367 "connected" | "conn" => Some(Self::Connected),
368 "unconnected" | "unconn" | "floating" | "nc" => Some(Self::Unconnected),
369 "power" | "pwr" => Some(Self::Power),
370 "ground" | "gnd" => Some(Self::Ground),
371 "input" | "in" => Some(Self::Input),
372 "output" | "out" => Some(Self::Output),
373 "bidirectional" | "bidir" | "inout" => Some(Self::Bidirectional),
374 "passive" | "pass" => Some(Self::Passive),
375 "opencollector" | "oc" => Some(Self::OpenCollector),
376 "openemitter" | "oe" => Some(Self::OpenEmitter),
377 "hiz" | "tristate" => Some(Self::HiZ),
378 "hidden" => Some(Self::Hidden),
379 "visible" => Some(Self::Visible),
380 "first" => Some(Self::First),
381 "last" => Some(Self::Last),
382 "even" => Some(Self::Even),
383 "odd" => Some(Self::Odd),
384 "count" => Some(Self::Count),
385 "nth" => arg.and_then(|a| a.parse().ok()).map(Self::Nth),
386 "nth-last" | "nthlast" => arg.and_then(|a| a.parse().ok()).map(Self::NthLast),
387 "limit" => arg.and_then(|a| a.parse().ok()).map(Self::Limit),
388 "offset" | "skip" => arg.and_then(|a| a.parse().ok()).map(Self::Offset),
389 _ => None,
390 }
391 }
392
393 pub fn is_result_modifier(&self) -> bool {
395 matches!(
396 self,
397 PseudoSelector::Count
398 | PseudoSelector::Limit(_)
399 | PseudoSelector::Offset(_)
400 | PseudoSelector::First
401 | PseudoSelector::Last
402 | PseudoSelector::Nth(_)
403 | PseudoSelector::NthLast(_)
404 | PseudoSelector::Even
405 | PseudoSelector::Odd
406 )
407 }
408
409 pub fn is_filter(&self) -> bool {
411 !self.is_result_modifier()
412 }
413}
414
415#[derive(Debug, Clone, Copy, PartialEq, Eq)]
417pub enum CombinatorType {
418 Descendant,
420 Child,
422 Connected,
424 Sibling,
426 Adjacent,
428 OnNet,
430}
431
432impl CombinatorType {
433 pub fn syntax(&self) -> &'static str {
435 match self {
436 CombinatorType::Descendant => " ",
437 CombinatorType::Child => " > ",
438 CombinatorType::Connected => " >> ",
439 CombinatorType::Sibling => " ~ ",
440 CombinatorType::Adjacent => " + ",
441 CombinatorType::OnNet => " :: ",
442 }
443 }
444}