in cli/src/generate/properties.rs [667:845]
fn parse_sass_items(
items: Vec<rsass::Item>,
selector_prefixes: &Vec<Vec<SelectorStep>>,
result: &mut Vec<Rule>,
) -> Result<()> {
let mut properties = PropertySet::new();
for item in items {
match item {
rsass::Item::None | rsass::Item::Comment(_) => {}
rsass::Item::Property(name, value) => {
let value = parse_sass_value(&value)?;
match properties.entry(name.to_string()) {
btree_map::Entry::Vacant(v) => {
v.insert(value);
}
btree_map::Entry::Occupied(mut o) => {
let existing_value = o.get_mut();
if let PropertyValue::Array(items) = existing_value {
items.push(value);
continue;
} else {
let v = existing_value.clone();
*existing_value = PropertyValue::Array(vec![v, value]);
}
}
}
}
rsass::Item::Rule(selectors, items) => {
let mut full_selectors = Vec::new();
for prefix in selector_prefixes {
for selector in &selectors.s {
let mut prefix = prefix.clone();
let mut operator_was_immediate: Option<bool> = Some(false);
for part in &selector.0 {
match part {
SelectorPart::BackRef => {
operator_was_immediate = None;
}
SelectorPart::Simple(value) => {
if let Some(value) = value.single_raw() {
for (i, value) in value.split('.').enumerate() {
if value.is_empty() {
continue;
}
let value = value.to_string();
check_node_kind(&value)?;
if i > 0 {
if let Some(immediate) = operator_was_immediate {
prefix.push(SelectorStep {
kind: None,
field: Some(value),
is_named: None,
child_index: None,
text_pattern: None,
is_immediate: immediate,
})
} else {
prefix.last_mut().unwrap().field = Some(value);
}
} else {
if let Some(immediate) = operator_was_immediate {
prefix.push(SelectorStep {
kind: Some(value.to_string()),
field: None,
child_index: None,
text_pattern: None,
is_named: Some(true),
is_immediate: immediate,
});
} else {
return Error::err(format!("Node type {} must be separated by whitespace or the `>` operator", value));
}
}
operator_was_immediate = None;
}
} else {
return Err(interpolation_error());
}
operator_was_immediate = None;
}
SelectorPart::Attribute { name, val, .. } => {
match name.single_raw() {
None => return Err(interpolation_error()),
Some("text") => {
if operator_was_immediate.is_some() {
return Error::err("The `text` attribute must be used in combination with a node type or field".to_string());
}
if let Some(last_step) = prefix.last_mut() {
last_step.text_pattern =
Some(get_string_value(val.to_string())?)
}
}
Some("token") => {
if let Some(immediate) = operator_was_immediate {
prefix.push(SelectorStep {
kind: Some(get_string_value(val.to_string())?),
field: None,
is_named: Some(false),
child_index: None,
text_pattern: None,
is_immediate: immediate,
});
operator_was_immediate = None;
} else {
return Error::err("The `token` attribute canot be used in combination with a node type".to_string());
}
}
_ => {
return Error::err(format!(
"Unsupported attribute {}",
part
));
}
}
}
SelectorPart::PseudoElement { .. } => {
return Error::err(
"Pseudo elements are not supported".to_string(),
);
}
SelectorPart::Pseudo { name, arg } => match name.single_raw() {
None => return Err(interpolation_error()),
Some("nth-child") => {
if let Some(arg) = arg {
let mut arg_str = String::new();
write!(&mut arg_str, "{}", arg).unwrap();
if let Some(last_step) = prefix.last_mut() {
if let Ok(i) = usize::from_str_radix(&arg_str, 10) {
last_step.child_index = Some(i);
} else {
return Error::err(format!(
"Invalid child index {}",
arg
));
}
}
}
}
_ => {
return Error::err(format!(
"Unsupported pseudo-class {}",
part
));
}
},
SelectorPart::Descendant => {
operator_was_immediate = Some(false);
}
SelectorPart::RelOp(operator) => {
let operator = *operator as char;
if operator == '>' {
operator_was_immediate = Some(true);
} else {
return Error::err(format!(
"Unsupported operator {}",
operator
));
}
}
}
}
full_selectors.push(prefix);
}
}
parse_sass_items(items, &full_selectors, result)?;
}
_ => return Error::err(format!("Unsupported syntax type {:?}", item)),
}
}
if !properties.is_empty() {
result.push(Rule {
selectors: selector_prefixes.iter().cloned().map(Selector).collect(),
properties,
});
}
Ok(())
}