Skip to main content

altium_format/query/
executor.rs

1//! Query executor - matches selectors against schematic elements.
2
3use std::collections::HashSet;
4
5use super::QueryMatch;
6use super::ast::*;
7use super::schql_parser::QueryError;
8use super::view::*;
9
10/// Query executor that matches selectors against a schematic view
11pub struct QueryExecutor<'a> {
12    view: &'a SchematicView,
13}
14
15impl<'a> QueryExecutor<'a> {
16    /// Create a new executor for a schematic view
17    pub fn new(view: &'a SchematicView) -> Self {
18        Self { view }
19    }
20
21    /// Execute a selector and return matches
22    pub fn execute(&self, selector: &Selector) -> Result<Vec<QueryMatch>, QueryError> {
23        let matches = self.execute_selector(selector)?;
24
25        // Apply result modifiers
26        let matches = self.apply_result_modifiers(matches, selector);
27
28        Ok(matches)
29    }
30
31    fn execute_selector(&self, selector: &Selector) -> Result<Vec<QueryMatch>, QueryError> {
32        match selector {
33            Selector::Element(elem_type) => self.select_element(*elem_type),
34            Selector::Id(id) => self.select_id(id),
35            Selector::Universal => self.select_all(),
36            Selector::Attribute(attr) => self.filter_by_attribute(&self.select_all()?, attr),
37            Selector::Pseudo(pseudo) => self.filter_by_pseudo(&self.select_all()?, pseudo),
38            Selector::Compound(parts) => self.select_compound(parts),
39            Selector::Combinator {
40                left,
41                combinator,
42                right,
43            } => self.select_combinator(left, *combinator, right),
44            Selector::Union(selectors) => self.select_union(selectors),
45            Selector::Not(inner) => self.select_not(inner),
46            Selector::Has(inner) => self.select_has(inner),
47        }
48    }
49
50    fn select_element(&self, elem_type: ElementType) -> Result<Vec<QueryMatch>, QueryError> {
51        match elem_type {
52            ElementType::Component => Ok(self
53                .view
54                .components
55                .iter()
56                .map(|c| self.component_to_match(c))
57                .collect()),
58            ElementType::Pin => Ok(self.all_pins()),
59            ElementType::Net => Ok(self
60                .view
61                .nets
62                .iter()
63                .map(|n| self.net_to_match(n))
64                .collect()),
65            ElementType::Port => Ok(self
66                .view
67                .ports
68                .iter()
69                .map(|p| self.port_to_match(p))
70                .collect()),
71            ElementType::Wire => Ok(self
72                .view
73                .wires
74                .iter()
75                .enumerate()
76                .map(|(i, w)| self.wire_to_match(i, w))
77                .collect()),
78            ElementType::Power => Ok(self
79                .view
80                .power_symbols
81                .iter()
82                .filter(|p| !p.is_ground)
83                .map(|p| self.power_to_match(p))
84                .collect()),
85            ElementType::Ground => Ok(self
86                .view
87                .power_symbols
88                .iter()
89                .filter(|p| p.is_ground)
90                .map(|p| self.power_to_match(p))
91                .collect()),
92            ElementType::Label => Ok(self
93                .view
94                .labels
95                .iter()
96                .map(|l| self.label_to_match(l))
97                .collect()),
98            ElementType::Junction => Ok(self
99                .view
100                .junctions
101                .iter()
102                .map(|j| self.junction_to_match(j))
103                .collect()),
104            ElementType::Parameter => Ok(self.all_parameters()),
105            ElementType::Designator => Ok(self
106                .view
107                .components
108                .iter()
109                .map(|c| QueryMatch::Parameter {
110                    component_designator: c.designator.clone(),
111                    name: "Designator".to_string(),
112                    value: c.designator.clone(),
113                })
114                .collect()),
115            ElementType::Sheet => {
116                // Return sheet info - just one match
117                Ok(vec![QueryMatch::Component {
118                    designator: "Sheet".to_string(),
119                    part: self.view.sheet_name.clone().unwrap_or_default(),
120                    description: format!(
121                        "{} components, {} nets",
122                        self.view.components.len(),
123                        self.view.nets.len()
124                    ),
125                    value: None,
126                    footprint: None,
127                    pin_count: 0,
128                }])
129            }
130        }
131    }
132
133    fn select_id(&self, id: &str) -> Result<Vec<QueryMatch>, QueryError> {
134        let id_upper = id.to_uppercase();
135
136        // Try component designator
137        if let Some(comp) = self.view.get_component(id) {
138            return Ok(vec![self.component_to_match(comp)]);
139        }
140        // Case-insensitive search
141        for comp in &self.view.components {
142            if comp.designator.to_uppercase() == id_upper {
143                return Ok(vec![self.component_to_match(comp)]);
144            }
145        }
146
147        // Try net name
148        if let Some(net) = self.view.get_net(id) {
149            return Ok(vec![self.net_to_match(net)]);
150        }
151        // Case-insensitive search
152        for net in &self.view.nets {
153            if net.name.to_uppercase() == id_upper {
154                return Ok(vec![self.net_to_match(net)]);
155            }
156        }
157
158        // Try port name
159        for port in &self.view.ports {
160            if port.name.to_uppercase() == id_upper {
161                return Ok(vec![self.port_to_match(port)]);
162            }
163        }
164
165        // Try power symbol net name
166        for pwr in &self.view.power_symbols {
167            if pwr.net_name.to_uppercase() == id_upper {
168                return Ok(vec![self.power_to_match(pwr)]);
169            }
170        }
171
172        Ok(vec![])
173    }
174
175    fn select_all(&self) -> Result<Vec<QueryMatch>, QueryError> {
176        let mut matches = Vec::new();
177        matches.extend(
178            self.view
179                .components
180                .iter()
181                .map(|c| self.component_to_match(c)),
182        );
183        matches.extend(self.all_pins());
184        matches.extend(self.view.nets.iter().map(|n| self.net_to_match(n)));
185        matches.extend(self.view.ports.iter().map(|p| self.port_to_match(p)));
186        matches.extend(
187            self.view
188                .power_symbols
189                .iter()
190                .map(|p| self.power_to_match(p)),
191        );
192        Ok(matches)
193    }
194
195    fn select_compound(&self, parts: &[Selector]) -> Result<Vec<QueryMatch>, QueryError> {
196        if parts.is_empty() {
197            return Ok(vec![]);
198        }
199
200        // Start with first selector
201        let mut matches = self.execute_selector(&parts[0])?;
202
203        // Apply remaining selectors as filters
204        for part in &parts[1..] {
205            matches = self.filter_matches(matches, part)?;
206        }
207
208        Ok(matches)
209    }
210
211    fn filter_matches(
212        &self,
213        matches: Vec<QueryMatch>,
214        selector: &Selector,
215    ) -> Result<Vec<QueryMatch>, QueryError> {
216        match selector {
217            Selector::Attribute(attr) => self.filter_by_attribute(&matches, attr),
218            Selector::Pseudo(pseudo) => self.filter_by_pseudo(&matches, pseudo),
219            Selector::Element(elem_type) => Ok(matches
220                .into_iter()
221                .filter(|m| self.match_has_type(m, *elem_type))
222                .collect()),
223            Selector::Id(id) => {
224                let id_upper = id.to_uppercase();
225                Ok(matches
226                    .into_iter()
227                    .filter(|m| match m {
228                        QueryMatch::Component { designator, .. } => {
229                            designator.to_uppercase() == id_upper
230                        }
231                        QueryMatch::Net { name, .. } => name.to_uppercase() == id_upper,
232                        QueryMatch::Port { name, .. } => name.to_uppercase() == id_upper,
233                        QueryMatch::Power { net_name, .. } => net_name.to_uppercase() == id_upper,
234                        _ => false,
235                    })
236                    .collect())
237            }
238            Selector::Not(inner) => {
239                let excluded = self.execute_selector(inner)?;
240                let excluded_set: HashSet<String> = excluded
241                    .iter()
242                    .map(|m: &QueryMatch| m.to_short_text())
243                    .collect();
244                Ok(matches
245                    .into_iter()
246                    .filter(|m: &QueryMatch| !excluded_set.contains(&m.to_short_text()))
247                    .collect())
248            }
249            _ => Ok(matches),
250        }
251    }
252
253    fn select_combinator(
254        &self,
255        left: &Selector,
256        combinator: CombinatorType,
257        right: &Selector,
258    ) -> Result<Vec<QueryMatch>, QueryError> {
259        match combinator {
260            CombinatorType::Child | CombinatorType::Descendant => {
261                // Get left side matches (parents)
262                let parents = self.execute_selector(left)?;
263                let mut results = Vec::new();
264
265                for parent in parents {
266                    // For each parent, get children that match right selector
267                    let children = self.get_children(&parent, right)?;
268                    results.extend(children);
269                }
270
271                Ok(results)
272            }
273            CombinatorType::OnNet => {
274                // Get net matches from left
275                let nets = self.execute_selector(left)?;
276                let mut results = Vec::new();
277
278                for net_match in nets {
279                    if let QueryMatch::Net { ref name, .. } = net_match {
280                        // Get all pins on this net
281                        if let Some(net) = self.view.get_net(name) {
282                            for conn in &net.connections {
283                                if let ConnectionPoint::Pin {
284                                    component_designator,
285                                    pin_designator,
286                                    pin_name,
287                                } = conn
288                                {
289                                    // Check if this pin matches the right selector
290                                    let pin_match = QueryMatch::Pin {
291                                        component_designator: component_designator.clone(),
292                                        designator: pin_designator.clone(),
293                                        name: pin_name.clone(),
294                                        electrical_type: "".to_string(),
295                                        connected_net: Some(name.clone()),
296                                        is_hidden: false,
297                                    };
298                                    if self.match_satisfies_selector(&pin_match, right)? {
299                                        results.push(pin_match);
300                                    }
301                                }
302                            }
303                        }
304                    }
305                }
306
307                Ok(results)
308            }
309            CombinatorType::Connected => {
310                // Get left side matches and find everything connected to them
311                let sources = self.execute_selector(left)?;
312                let mut connected_nets: HashSet<String> = HashSet::new();
313
314                // Find all nets connected to source elements
315                for source in &sources {
316                    match source {
317                        QueryMatch::Component { designator, .. } => {
318                            if let Some(comp) = self.view.get_component(designator) {
319                                for pin in &comp.pins {
320                                    if let Some(net) = &pin.connected_net {
321                                        connected_nets.insert(net.clone());
322                                    }
323                                }
324                            }
325                        }
326                        QueryMatch::Pin {
327                            connected_net: Some(net),
328                            ..
329                        } => {
330                            connected_nets.insert(net.clone());
331                        }
332                        QueryMatch::Net { name, .. } => {
333                            connected_nets.insert(name.clone());
334                        }
335                        _ => {}
336                    }
337                }
338
339                // Find all elements connected to those nets
340                let mut results = Vec::new();
341                for net_name in connected_nets {
342                    if let Some(net) = self.view.get_net(&net_name) {
343                        for conn in &net.connections {
344                            if let ConnectionPoint::Pin {
345                                component_designator,
346                                ..
347                            } = conn
348                            {
349                                // Get the component
350                                if let Some(comp) = self.view.get_component(component_designator) {
351                                    let comp_match = self.component_to_match(comp);
352                                    if self.match_satisfies_selector(&comp_match, right)? {
353                                        // Avoid duplicates
354                                        if !results.iter().any(|m| {
355                                            if let QueryMatch::Component { designator, .. } = m {
356                                                designator == component_designator
357                                            } else {
358                                                false
359                                            }
360                                        }) {
361                                            results.push(comp_match);
362                                        }
363                                    }
364                                }
365                            }
366                        }
367                    }
368                }
369
370                Ok(results)
371            }
372            CombinatorType::Sibling => {
373                // Find elements with same parent
374                let left_matches = self.execute_selector(left)?;
375                let mut results = Vec::new();
376
377                for left_match in left_matches {
378                    if let QueryMatch::Pin {
379                        component_designator,
380                        ..
381                    } = &left_match
382                    {
383                        // Get all other pins of the same component
384                        if let Some(comp) = self.view.get_component(component_designator) {
385                            for pin in &comp.pins {
386                                let pin_match = self.pin_to_match(comp, pin);
387                                if self.match_satisfies_selector(&pin_match, right)? {
388                                    results.push(pin_match);
389                                }
390                            }
391                        }
392                    }
393                }
394
395                Ok(results)
396            }
397            CombinatorType::Adjacent => {
398                // For now, treat same as descendant
399                self.select_combinator(left, CombinatorType::Descendant, right)
400            }
401        }
402    }
403
404    fn select_union(&self, selectors: &[Selector]) -> Result<Vec<QueryMatch>, QueryError> {
405        let mut all_matches: Vec<QueryMatch> = Vec::new();
406        let mut seen: HashSet<String> = HashSet::new();
407
408        for sel in selectors {
409            let matches: Vec<QueryMatch> = self.execute_selector(sel)?;
410            for m in matches {
411                let key = m.to_short_text();
412                if !seen.contains(&key) {
413                    seen.insert(key);
414                    all_matches.push(m);
415                }
416            }
417        }
418
419        Ok(all_matches)
420    }
421
422    fn select_not(&self, inner: &Selector) -> Result<Vec<QueryMatch>, QueryError> {
423        let all: Vec<QueryMatch> = self.select_all()?;
424        let excluded: Vec<QueryMatch> = self.execute_selector(inner)?;
425        let excluded_set: HashSet<String> = excluded
426            .iter()
427            .map(|m: &QueryMatch| m.to_short_text())
428            .collect();
429
430        Ok(all
431            .into_iter()
432            .filter(|m: &QueryMatch| !excluded_set.contains(&m.to_short_text()))
433            .collect())
434    }
435
436    fn select_has(&self, inner: &Selector) -> Result<Vec<QueryMatch>, QueryError> {
437        // Find components that have children matching inner
438        let mut results = Vec::new();
439
440        for comp in &self.view.components {
441            // Check if any pin matches inner selector
442            for pin in &comp.pins {
443                let pin_match = self.pin_to_match(comp, pin);
444                if self.match_satisfies_selector(&pin_match, inner)? {
445                    results.push(self.component_to_match(comp));
446                    break;
447                }
448            }
449        }
450
451        Ok(results)
452    }
453
454    fn get_children(
455        &self,
456        parent: &QueryMatch,
457        child_selector: &Selector,
458    ) -> Result<Vec<QueryMatch>, QueryError> {
459        match parent {
460            QueryMatch::Component { designator, .. } => {
461                if let Some(comp) = self.view.get_component(designator) {
462                    let mut children = Vec::new();
463
464                    // Pins are children of components
465                    for pin in &comp.pins {
466                        let pin_match = self.pin_to_match(comp, pin);
467                        if self.match_satisfies_selector(&pin_match, child_selector)? {
468                            children.push(pin_match);
469                        }
470                    }
471
472                    // Parameters are also children
473                    for (name, value) in &comp.parameters {
474                        let param_match = QueryMatch::Parameter {
475                            component_designator: designator.clone(),
476                            name: name.clone(),
477                            value: value.clone(),
478                        };
479                        if self.match_satisfies_selector(&param_match, child_selector)? {
480                            children.push(param_match);
481                        }
482                    }
483
484                    Ok(children)
485                } else {
486                    Ok(vec![])
487                }
488            }
489            _ => Ok(vec![]),
490        }
491    }
492
493    fn match_satisfies_selector(
494        &self,
495        m: &QueryMatch,
496        selector: &Selector,
497    ) -> Result<bool, QueryError> {
498        match selector {
499            Selector::Element(elem_type) => Ok(self.match_has_type(m, *elem_type)),
500            Selector::Id(id) => {
501                let id_upper = id.to_uppercase();
502                Ok(match m {
503                    QueryMatch::Component { designator, .. } => {
504                        designator.to_uppercase() == id_upper
505                    }
506                    QueryMatch::Pin {
507                        component_designator,
508                        designator,
509                        ..
510                    } => {
511                        designator.to_uppercase() == id_upper
512                            || format!("{}.{}", component_designator, designator).to_uppercase()
513                                == id_upper
514                    }
515                    QueryMatch::Net { name, .. } => name.to_uppercase() == id_upper,
516                    QueryMatch::Port { name, .. } => name.to_uppercase() == id_upper,
517                    _ => false,
518                })
519            }
520            Selector::Universal => Ok(true),
521            Selector::Attribute(attr) => Ok(self.match_has_attribute(m, attr)),
522            Selector::Pseudo(pseudo) => Ok(self.match_has_pseudo(m, pseudo)),
523            Selector::Compound(parts) => {
524                for part in parts {
525                    if !self.match_satisfies_selector(m, part)? {
526                        return Ok(false);
527                    }
528                }
529                Ok(true)
530            }
531            Selector::Not(inner) => Ok(!self.match_satisfies_selector(m, inner)?),
532            _ => Ok(true),
533        }
534    }
535
536    fn match_has_type(&self, m: &QueryMatch, elem_type: ElementType) -> bool {
537        matches!(
538            (m, elem_type),
539            (QueryMatch::Component { .. }, ElementType::Component)
540                | (QueryMatch::Pin { .. }, ElementType::Pin)
541                | (QueryMatch::Net { .. }, ElementType::Net)
542                | (QueryMatch::Port { .. }, ElementType::Port)
543                | (QueryMatch::Wire { .. }, ElementType::Wire)
544                | (
545                    QueryMatch::Power {
546                        is_ground: false,
547                        ..
548                    },
549                    ElementType::Power
550                )
551                | (
552                    QueryMatch::Power {
553                        is_ground: true,
554                        ..
555                    },
556                    ElementType::Ground
557                )
558                | (QueryMatch::Label { .. }, ElementType::Label)
559                | (QueryMatch::Junction { .. }, ElementType::Junction)
560                | (QueryMatch::Parameter { .. }, ElementType::Parameter)
561        )
562    }
563
564    fn filter_by_attribute(
565        &self,
566        matches: &[QueryMatch],
567        attr: &AttributeSelector,
568    ) -> Result<Vec<QueryMatch>, QueryError> {
569        Ok(matches
570            .iter()
571            .filter(|m| self.match_has_attribute(m, attr))
572            .cloned()
573            .collect())
574    }
575
576    fn match_has_attribute(&self, m: &QueryMatch, attr: &AttributeSelector) -> bool {
577        let value = self.get_attribute_value(m, &attr.name);
578        attr.matches(value)
579    }
580
581    fn get_attribute_value<'b>(&'b self, m: &'b QueryMatch, attr_name: &str) -> Option<&'b str> {
582        let attr_lower = attr_name.to_lowercase();
583
584        match m {
585            QueryMatch::Component {
586                designator,
587                part,
588                description,
589                value,
590                footprint,
591                ..
592            } => {
593                match attr_lower.as_str() {
594                    "designator" | "des" | "ref" => Some(designator.as_str()),
595                    "part" | "partname" | "libref" => Some(part.as_str()),
596                    "description" | "desc" => Some(description.as_str()),
597                    "value" | "val" => value.as_deref(),
598                    "footprint" | "fp" | "package" => footprint.as_deref(),
599                    "pins" | "pincount" => None, // Numeric, handled separately
600                    _ => None,
601                }
602            }
603            QueryMatch::Pin {
604                component_designator,
605                designator,
606                name,
607                electrical_type,
608                connected_net,
609                is_hidden,
610                ..
611            } => match attr_lower.as_str() {
612                "designator" | "des" | "num" | "number" | "pin" => Some(designator.as_str()),
613                "name" | "pinname" => Some(name.as_str()),
614                "type" | "electrical" | "elec" => Some(electrical_type.as_str()),
615                "net" | "netname" => connected_net.as_deref(),
616                "component" | "comp" => Some(component_designator.as_str()),
617                "hidden" => {
618                    if *is_hidden {
619                        Some("true")
620                    } else {
621                        Some("false")
622                    }
623                }
624                _ => None,
625            },
626            QueryMatch::Net {
627                name,
628                is_power,
629                is_ground,
630                ..
631            } => match attr_lower.as_str() {
632                "name" | "netname" => Some(name.as_str()),
633                "power" => {
634                    if *is_power {
635                        Some("true")
636                    } else {
637                        Some("false")
638                    }
639                }
640                "ground" | "gnd" => {
641                    if *is_ground {
642                        Some("true")
643                    } else {
644                        Some("false")
645                    }
646                }
647                _ => None,
648            },
649            QueryMatch::Port {
650                name,
651                io_type,
652                connected_net,
653                ..
654            } => match attr_lower.as_str() {
655                "name" => Some(name.as_str()),
656                "io" | "iotype" | "type" | "direction" => Some(io_type.as_str()),
657                "net" | "netname" => connected_net.as_deref(),
658                _ => None,
659            },
660            QueryMatch::Power {
661                net_name,
662                style,
663                is_ground,
664                ..
665            } => match attr_lower.as_str() {
666                "name" | "netname" | "net" => Some(net_name.as_str()),
667                "style" => Some(style.as_str()),
668                "ground" | "gnd" => {
669                    if *is_ground {
670                        Some("true")
671                    } else {
672                        Some("false")
673                    }
674                }
675                _ => None,
676            },
677            QueryMatch::Parameter {
678                component_designator,
679                name,
680                value,
681            } => match attr_lower.as_str() {
682                "name" => Some(name.as_str()),
683                "value" | "val" => Some(value.as_str()),
684                "component" | "comp" => Some(component_designator.as_str()),
685                _ => None,
686            },
687            QueryMatch::Label { text, .. } => match attr_lower.as_str() {
688                "text" | "name" | "net" => Some(text.as_str()),
689                _ => None,
690            },
691            _ => None,
692        }
693    }
694
695    fn filter_by_pseudo(
696        &self,
697        matches: &[QueryMatch],
698        pseudo: &PseudoSelector,
699    ) -> Result<Vec<QueryMatch>, QueryError> {
700        // Result modifiers are handled separately
701        if pseudo.is_result_modifier() {
702            return Ok(matches.to_vec());
703        }
704
705        Ok(matches
706            .iter()
707            .filter(|m| self.match_has_pseudo(m, pseudo))
708            .cloned()
709            .collect())
710    }
711
712    fn match_has_pseudo(&self, m: &QueryMatch, pseudo: &PseudoSelector) -> bool {
713        match pseudo {
714            PseudoSelector::Connected => match m {
715                QueryMatch::Pin { connected_net, .. } => connected_net.is_some(),
716                QueryMatch::Net {
717                    connection_count, ..
718                } => *connection_count > 0,
719                QueryMatch::Port { connected_net, .. } => connected_net.is_some(),
720                _ => true,
721            },
722            PseudoSelector::Unconnected => match m {
723                QueryMatch::Pin { connected_net, .. } => connected_net.is_none(),
724                QueryMatch::Net {
725                    connection_count, ..
726                } => *connection_count == 0,
727                _ => false,
728            },
729            PseudoSelector::Power => match m {
730                QueryMatch::Net { is_power, .. } => *is_power,
731                QueryMatch::Pin {
732                    electrical_type, ..
733                } => electrical_type == "Power",
734                QueryMatch::Power { is_ground, .. } => !*is_ground,
735                _ => false,
736            },
737            PseudoSelector::Ground => match m {
738                QueryMatch::Net { is_ground, .. } => *is_ground,
739                QueryMatch::Power { is_ground, .. } => *is_ground,
740                _ => false,
741            },
742            PseudoSelector::Input => match m {
743                QueryMatch::Pin {
744                    electrical_type, ..
745                } => electrical_type == "Input",
746                QueryMatch::Port { io_type, .. } => io_type.to_uppercase().contains("INPUT"),
747                _ => false,
748            },
749            PseudoSelector::Output => match m {
750                QueryMatch::Pin {
751                    electrical_type, ..
752                } => electrical_type == "Output",
753                QueryMatch::Port { io_type, .. } => io_type.to_uppercase().contains("OUTPUT"),
754                _ => false,
755            },
756            PseudoSelector::Bidirectional => match m {
757                QueryMatch::Pin {
758                    electrical_type, ..
759                } => electrical_type == "Bidirectional",
760                QueryMatch::Port { io_type, .. } => io_type.to_uppercase().contains("BIDIR"),
761                _ => false,
762            },
763            PseudoSelector::Passive => match m {
764                QueryMatch::Pin {
765                    electrical_type, ..
766                } => electrical_type == "Passive",
767                _ => false,
768            },
769            PseudoSelector::OpenCollector => match m {
770                QueryMatch::Pin {
771                    electrical_type, ..
772                } => electrical_type == "OpenCollector",
773                _ => false,
774            },
775            PseudoSelector::OpenEmitter => match m {
776                QueryMatch::Pin {
777                    electrical_type, ..
778                } => electrical_type == "OpenEmitter",
779                _ => false,
780            },
781            PseudoSelector::HiZ => match m {
782                QueryMatch::Pin {
783                    electrical_type, ..
784                } => electrical_type == "HiZ",
785                _ => false,
786            },
787            PseudoSelector::Hidden => match m {
788                QueryMatch::Pin { is_hidden, .. } => *is_hidden,
789                _ => false,
790            },
791            PseudoSelector::Visible => match m {
792                QueryMatch::Pin { is_hidden, .. } => !*is_hidden,
793                _ => true,
794            },
795            // Result modifiers handled in apply_result_modifiers
796            _ => true,
797        }
798    }
799
800    fn apply_result_modifiers(
801        &self,
802        mut matches: Vec<QueryMatch>,
803        selector: &Selector,
804    ) -> Vec<QueryMatch> {
805        let modifiers = selector.get_result_modifiers();
806
807        for modifier in modifiers {
808            match modifier {
809                PseudoSelector::Count => {
810                    return vec![QueryMatch::Count(matches.len())];
811                }
812                PseudoSelector::First => {
813                    return matches.into_iter().take(1).collect();
814                }
815                PseudoSelector::Last => {
816                    return matches.into_iter().last().into_iter().collect();
817                }
818                PseudoSelector::Nth(n) => {
819                    if *n > 0 && *n <= matches.len() {
820                        return vec![matches.remove(*n - 1)];
821                    }
822                    return vec![];
823                }
824                PseudoSelector::NthLast(n) => {
825                    if *n > 0 && *n <= matches.len() {
826                        let idx = matches.len() - *n;
827                        return vec![matches.remove(idx)];
828                    }
829                    return vec![];
830                }
831                PseudoSelector::Even => {
832                    return matches
833                        .into_iter()
834                        .enumerate()
835                        .filter(|(i, _)| i % 2 == 1)
836                        .map(|(_, m)| m)
837                        .collect();
838                }
839                PseudoSelector::Odd => {
840                    return matches
841                        .into_iter()
842                        .enumerate()
843                        .filter(|(i, _)| i % 2 == 0)
844                        .map(|(_, m)| m)
845                        .collect();
846                }
847                PseudoSelector::Limit(n) => {
848                    matches = matches.into_iter().take(*n).collect();
849                }
850                PseudoSelector::Offset(n) => {
851                    matches = matches.into_iter().skip(*n).collect();
852                }
853                _ => {}
854            }
855        }
856
857        matches
858    }
859
860    // Helper methods to create QueryMatch from views
861
862    fn component_to_match(&self, comp: &ComponentView) -> QueryMatch {
863        QueryMatch::Component {
864            designator: comp.designator.clone(),
865            part: comp.part_name.clone(),
866            description: comp.description.clone(),
867            value: comp.value.clone(),
868            footprint: comp.footprint.clone(),
869            pin_count: comp.pins.len(),
870        }
871    }
872
873    fn pin_to_match(&self, comp: &ComponentView, pin: &PinView) -> QueryMatch {
874        QueryMatch::Pin {
875            component_designator: comp.designator.clone(),
876            designator: pin.designator.clone(),
877            name: pin.name.clone(),
878            electrical_type: pin.electrical_type.as_str().to_string(),
879            connected_net: pin.connected_net.clone(),
880            is_hidden: pin.is_hidden,
881        }
882    }
883
884    fn all_pins(&self) -> Vec<QueryMatch> {
885        let mut pins = Vec::new();
886        for comp in &self.view.components {
887            for pin in &comp.pins {
888                pins.push(self.pin_to_match(comp, pin));
889            }
890        }
891        pins
892    }
893
894    fn net_to_match(&self, net: &NetView) -> QueryMatch {
895        QueryMatch::Net {
896            name: net.name.clone(),
897            is_power: net.is_power,
898            is_ground: net.is_ground,
899            connection_count: net.connections.len(),
900            connections: net
901                .connections
902                .iter()
903                .map(|c| c.to_short_string())
904                .collect(),
905        }
906    }
907
908    fn port_to_match(&self, port: &PortView) -> QueryMatch {
909        QueryMatch::Port {
910            name: port.name.clone(),
911            io_type: port.io_type.clone(),
912            connected_net: port.connected_net.clone(),
913        }
914    }
915
916    fn wire_to_match(&self, index: usize, wire: &WireView) -> QueryMatch {
917        let start = wire.vertices.first().copied().unwrap_or((0, 0));
918        let end = wire.vertices.last().copied().unwrap_or((0, 0));
919        QueryMatch::Wire {
920            index,
921            vertex_count: wire.vertices.len(),
922            start,
923            end,
924        }
925    }
926
927    fn power_to_match(&self, power: &PowerView) -> QueryMatch {
928        QueryMatch::Power {
929            net_name: power.net_name.clone(),
930            style: power.style.clone(),
931            is_ground: power.is_ground,
932        }
933    }
934
935    fn label_to_match(&self, label: &LabelView) -> QueryMatch {
936        QueryMatch::Label {
937            text: label.text.clone(),
938            location: label.location,
939        }
940    }
941
942    fn junction_to_match(&self, junction: &JunctionView) -> QueryMatch {
943        QueryMatch::Junction {
944            location: junction.location,
945        }
946    }
947
948    fn all_parameters(&self) -> Vec<QueryMatch> {
949        let mut params = Vec::new();
950        for comp in &self.view.components {
951            for (name, value) in &comp.parameters {
952                params.push(QueryMatch::Parameter {
953                    component_designator: comp.designator.clone(),
954                    name: name.clone(),
955                    value: value.clone(),
956                });
957            }
958        }
959        params
960    }
961}
962
963#[cfg(test)]
964mod tests {
965    // These tests would require a mock SchematicView
966    // For now, just verify the executor compiles
967    #[test]
968    fn test_executor_creation() {
969        // Would need a test SchDoc here
970    }
971}