nadi_core 0.8.0

Core library for Nadi systems, for use by plugins
Documentation
use crate::network::StrPath;
use crate::parser::{
    components::*,
    errors::{ParseError, ParseErrorType},
    network::*,
    tokenizer::{RawToken, Token},
};
use crate::prelude::AttrMap;
use nom::{
    branch::alt,
    combinator::map,
    multi::separated_list0,
    sequence::{delimited, pair, tuple},
    Finish,
};
use std::collections::HashMap;

#[derive(Default)]
pub struct GvNetwork {
    pub nodes: HashMap<String, AttrMap>,
    pub edges: HashMap<StrPath, AttrMap>,
}

pub enum GvLine {
    Node((String, AttrMap)),
    Edge((StrPath, AttrMap)),
}

pub fn gv_file<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, GvNetwork> {
    let (rest, lines) = trailing_newlines(delimited(
        tuple((variable, maybe_space(variable), maybe_space(brace_start))),
        maybe_newline(gv_network),
        maybe_newline(brace_end),
    ))(inp)?;
    let mut net = GvNetwork::default();
    for line in lines {
        match line {
            GvLine::Node((k, v)) => net.nodes.insert(k, v),
            GvLine::Edge((k, v)) => net.edges.insert(k, v),
        };
    }
    Ok((rest, net))
}

pub fn gv_network<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, Vec<GvLine>> {
    newline_separated(alt((
        map(maybe_space(gv_node), GvLine::Node),
        map(maybe_space(gv_edge), GvLine::Edge),
    )))(inp)
}

pub fn gv_node<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, (String, AttrMap)> {
    pair(
        node_name,
        delimited(
            maybe_space(bracket_start),
            map(
                separated_list0(maybe_space(comma), maybe_newline(key_val)),
                |kv| kv.into_iter().map(|(k, v)| (k.into(), v)).collect(),
            ),
            maybe_space(pair(bracket_end, semicolon)),
        ),
    )(inp)
}

pub fn gv_edge<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, (StrPath, AttrMap)> {
    pair(
        str_path,
        delimited(
            maybe_space(bracket_start),
            map(
                separated_list0(maybe_space(comma), maybe_newline(key_val)),
                |kv| kv.into_iter().map(|(k, v)| (k.into(), v)).collect(),
            ),
            maybe_space(pair(bracket_end, semicolon)),
        ),
    )(inp)
}

pub fn parse(tokens: Vec<RawToken>) -> Result<GvNetwork, ParseError> {
    let tokens = Token::validate(tokens)?;
    match gv_file(&tokens).finish() {
        Ok((rest, paths)) => {
            if rest.is_empty() {
                Ok(paths)
            } else {
                match maybe_newline(gv_network)(rest).finish() {
                    Ok((rest, _)) => {
                        Err(ParseError::new(&tokens, rest, ParseErrorType::SyntaxError))
                    }
                    Err(err) => Err(ParseError::new(&tokens, err.internal.input, err.ty)),
                }
            }
        }
        Err(e) => Err(ParseError::new(&tokens, e.internal.input, e.ty)),
    }
}