use super::common::{FilterOp, FilterValue, compare_filter};
#[derive(Debug, Clone, PartialEq)]
pub enum Selector {
Element(ElementType),
Id(String),
Universal,
Attribute(AttributeSelector),
Pseudo(PseudoSelector),
Compound(Vec<Selector>),
Combinator {
left: Box<Selector>,
combinator: CombinatorType,
right: Box<Selector>,
},
Union(Vec<Selector>),
Not(Box<Selector>),
Has(Box<Selector>),
}
impl Selector {
pub fn has_result_modifier(&self) -> bool {
match self {
Selector::Pseudo(p) => p.is_result_modifier(),
Selector::Compound(parts) => parts.iter().any(|s| s.has_result_modifier()),
Selector::Combinator { right, .. } => right.has_result_modifier(),
Selector::Union(selectors) => selectors.iter().any(|s| s.has_result_modifier()),
_ => false,
}
}
pub fn get_result_modifiers(&self) -> Vec<&PseudoSelector> {
let mut modifiers = Vec::new();
self.collect_result_modifiers(&mut modifiers);
modifiers
}
fn collect_result_modifiers<'a>(&'a self, modifiers: &mut Vec<&'a PseudoSelector>) {
match self {
Selector::Pseudo(p) if p.is_result_modifier() => {
modifiers.push(p);
}
Selector::Compound(parts) => {
for part in parts {
part.collect_result_modifiers(modifiers);
}
}
Selector::Combinator { right, .. } => {
right.collect_result_modifiers(modifiers);
}
Selector::Union(selectors) => {
for sel in selectors {
sel.collect_result_modifiers(modifiers);
}
}
_ => {}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ElementType {
Component,
Pin,
Net,
Port,
Wire,
Power,
Ground,
Label,
Junction,
Parameter,
Designator,
Sheet,
}
impl ElementType {
pub fn try_parse(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"component" | "comp" | "c" => Some(ElementType::Component),
"pin" | "p" => Some(ElementType::Pin),
"net" | "n" => Some(ElementType::Net),
"port" => Some(ElementType::Port),
"wire" | "w" => Some(ElementType::Wire),
"power" | "pwr" => Some(ElementType::Power),
"ground" | "gnd" => Some(ElementType::Ground),
"label" | "netlabel" => Some(ElementType::Label),
"junction" | "junc" => Some(ElementType::Junction),
"parameter" | "param" => Some(ElementType::Parameter),
"designator" | "des" => Some(ElementType::Designator),
"sheet" | "document" | "doc" => Some(ElementType::Sheet),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
ElementType::Component => "component",
ElementType::Pin => "pin",
ElementType::Net => "net",
ElementType::Port => "port",
ElementType::Wire => "wire",
ElementType::Power => "power",
ElementType::Ground => "ground",
ElementType::Label => "label",
ElementType::Junction => "junction",
ElementType::Parameter => "parameter",
ElementType::Designator => "designator",
ElementType::Sheet => "sheet",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeOp {
Exists,
Equals,
NotEquals,
WordMatch,
StartsWith,
EndsWith,
Contains,
GreaterThan,
LessThan,
GreaterOrEqual,
LessOrEqual,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AttributeSelector {
pub name: String,
pub op: AttributeOp,
pub value: Option<String>,
pub case_insensitive: bool,
}
impl AttributeSelector {
pub fn new(name: impl Into<String>, op: AttributeOp, value: Option<String>) -> Self {
Self {
name: name.into().to_lowercase(),
op,
value,
case_insensitive: false,
}
}
pub fn matches(&self, attr_value: Option<&str>) -> bool {
let filter_op = self.op.to_filter_op();
let filter_value = self
.value
.as_ref()
.map(|v| FilterValue::String(v.clone()))
.unwrap_or_else(|| FilterValue::String(String::new()));
compare_filter(attr_value, filter_op, &filter_value, self.case_insensitive)
}
}
impl AttributeOp {
pub fn to_filter_op(&self) -> FilterOp {
match self {
AttributeOp::Exists => FilterOp::Exists,
AttributeOp::Equals => FilterOp::Equals,
AttributeOp::NotEquals => FilterOp::NotEquals,
AttributeOp::WordMatch => FilterOp::WordMatch,
AttributeOp::StartsWith => FilterOp::StartsWith,
AttributeOp::EndsWith => FilterOp::EndsWith,
AttributeOp::Contains => FilterOp::Contains,
AttributeOp::GreaterThan => FilterOp::GreaterThan,
AttributeOp::LessThan => FilterOp::LessThan,
AttributeOp::GreaterOrEqual => FilterOp::GreaterOrEqual,
AttributeOp::LessOrEqual => FilterOp::LessOrEqual,
}
}
pub fn from_filter_op(op: FilterOp) -> Self {
match op {
FilterOp::Exists => AttributeOp::Exists,
FilterOp::Equals => AttributeOp::Equals,
FilterOp::NotEquals => AttributeOp::NotEquals,
FilterOp::WordMatch => AttributeOp::WordMatch,
FilterOp::StartsWith => AttributeOp::StartsWith,
FilterOp::EndsWith => AttributeOp::EndsWith,
FilterOp::Contains => AttributeOp::Contains,
FilterOp::GreaterThan => AttributeOp::GreaterThan,
FilterOp::LessThan => AttributeOp::LessThan,
FilterOp::GreaterOrEqual => AttributeOp::GreaterOrEqual,
FilterOp::LessOrEqual => AttributeOp::LessOrEqual,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StandardAttribute {
Designator,
Part,
Value,
Footprint,
Description,
Name,
Number,
Type,
Hidden,
NetName,
IoType,
Style,
X,
Y,
}
impl StandardAttribute {
pub fn try_parse(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"designator" | "des" | "ref" | "refdes" => Some(Self::Designator),
"part" | "partname" | "libref" | "libreference" => Some(Self::Part),
"value" | "val" => Some(Self::Value),
"footprint" | "fp" | "package" | "pcbfootprint" => Some(Self::Footprint),
"description" | "desc" => Some(Self::Description),
"name" | "pinname" => Some(Self::Name),
"number" | "num" | "pin" | "pinnum" => Some(Self::Number),
"type" | "electrical" | "elec" => Some(Self::Type),
"hidden" => Some(Self::Hidden),
"netname" | "net" => Some(Self::NetName),
"io" | "iotype" | "direction" | "dir" => Some(Self::IoType),
"style" => Some(Self::Style),
"x" | "locx" | "locationx" => Some(Self::X),
"y" | "locy" | "locationy" => Some(Self::Y),
_ => None,
}
}
pub fn canonical_name(&self) -> &'static str {
match self {
Self::Designator => "designator",
Self::Part => "part",
Self::Value => "value",
Self::Footprint => "footprint",
Self::Description => "description",
Self::Name => "name",
Self::Number => "number",
Self::Type => "type",
Self::Hidden => "hidden",
Self::NetName => "net",
Self::IoType => "io",
Self::Style => "style",
Self::X => "x",
Self::Y => "y",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PseudoSelector {
Connected,
Unconnected,
Power,
Ground,
Input,
Output,
Bidirectional,
Passive,
OpenCollector,
OpenEmitter,
HiZ,
Hidden,
Visible,
First,
Last,
Nth(usize),
NthLast(usize),
Even,
Odd,
Count,
Limit(usize),
Offset(usize),
}
impl PseudoSelector {
pub fn try_parse(s: &str, arg: Option<&str>) -> Option<Self> {
match s.to_lowercase().as_str() {
"connected" | "conn" => Some(Self::Connected),
"unconnected" | "unconn" | "floating" | "nc" => Some(Self::Unconnected),
"power" | "pwr" => Some(Self::Power),
"ground" | "gnd" => Some(Self::Ground),
"input" | "in" => Some(Self::Input),
"output" | "out" => Some(Self::Output),
"bidirectional" | "bidir" | "inout" => Some(Self::Bidirectional),
"passive" | "pass" => Some(Self::Passive),
"opencollector" | "oc" => Some(Self::OpenCollector),
"openemitter" | "oe" => Some(Self::OpenEmitter),
"hiz" | "tristate" => Some(Self::HiZ),
"hidden" => Some(Self::Hidden),
"visible" => Some(Self::Visible),
"first" => Some(Self::First),
"last" => Some(Self::Last),
"even" => Some(Self::Even),
"odd" => Some(Self::Odd),
"count" => Some(Self::Count),
"nth" => arg.and_then(|a| a.parse().ok()).map(Self::Nth),
"nth-last" | "nthlast" => arg.and_then(|a| a.parse().ok()).map(Self::NthLast),
"limit" => arg.and_then(|a| a.parse().ok()).map(Self::Limit),
"offset" | "skip" => arg.and_then(|a| a.parse().ok()).map(Self::Offset),
_ => None,
}
}
pub fn is_result_modifier(&self) -> bool {
matches!(
self,
PseudoSelector::Count
| PseudoSelector::Limit(_)
| PseudoSelector::Offset(_)
| PseudoSelector::First
| PseudoSelector::Last
| PseudoSelector::Nth(_)
| PseudoSelector::NthLast(_)
| PseudoSelector::Even
| PseudoSelector::Odd
)
}
pub fn is_filter(&self) -> bool {
!self.is_result_modifier()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CombinatorType {
Descendant,
Child,
Connected,
Sibling,
Adjacent,
OnNet,
}
impl CombinatorType {
pub fn syntax(&self) -> &'static str {
match self {
CombinatorType::Descendant => " ",
CombinatorType::Child => " > ",
CombinatorType::Connected => " >> ",
CombinatorType::Sibling => " ~ ",
CombinatorType::Adjacent => " + ",
CombinatorType::OnNet => " :: ",
}
}
}