fn parse_sass_items()

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(())
}