nbcl 0.5.0

Configuration language designed to be easy and understandable.
Documentation
use super::node;
use crate::ast::source::*;
use crate::error::{Result, Span};
use crate::parser::Rule;
use pest::iterators::Pair;

pub fn build_component_def(pair: Pair<Rule>) -> Result<ComponentDef> {
    let span = Span::from_pair(&pair);
    let mut inner = pair.into_inner();

    let name = inner.next().unwrap().as_str().to_string();

    let mut next = inner.next().unwrap();
    let interface = if next.as_rule() == Rule::component_params {
        let interf = build_interface(next)?;
        next = inner.next().unwrap();
        interf
    } else {
        ComponentInterface::None
    };

    let mut body = Vec::new();
    for item_pair in next.into_inner() {
        body.push(node::build_node_item(item_pair)?);
    }

    Ok(ComponentDef { name, interface, body, span })
}

fn build_interface(pair: Pair<Rule>) -> Result<ComponentInterface> {
    // component_params -> ( any_params | named_params )?
    let inner = match pair.into_inner().next() {
        Some(p) => p,
        None => return Ok(ComponentInterface::None),
    };

    match inner.as_rule() {
        Rule::any_params => {
            // any_params -> "any" ~ ":" ~ snake_ident
            let ident = inner
                .into_inner()
                .find(|p| p.as_rule() == Rule::snake_ident)
                .expect("Grammar guaranteed a snake_ident in any_params")
                .as_str()
                .to_string();
            Ok(ComponentInterface::Loose(ident))
        }
        Rule::named_params => {
            let mut params = Vec::new();
            for item in inner.into_inner() {
                // item is Rule::param_item
                params.push(build_param_item(item)?);
            }
            Ok(ComponentInterface::Strict(params))
        }
        _ => unreachable!(),
    }
}

fn build_param_item(pair: Pair<Rule>) -> Result<Parameter> {
    let mut inner = pair.into_inner();
    let name = inner.next().unwrap().as_str().to_string();
    let mut is_optional = false;

    if let Some(_) = inner.next() {
        is_optional = true;
    }

    Ok(Parameter { name, is_optional })
}