use super::{error::*, node::*};
use std::collections::BTreeMap;
pub fn link(nodes: BTreeMap<String, Node>) -> FlattenResult<BTreeMap<String, LinkedNode>> {
let lookup = nodes.clone();
nodes
.into_iter()
.map(|(key, node)| Ok((key, link_node(node, &lookup)?)))
.collect()
}
fn link_node(node: Node, ctx: &BTreeMap<String, Node>) -> FlattenResult<LinkedNode> {
match node {
Node::Literal(lit) => Ok(LinkedNode::Literal(lit)),
Node::Primative(t) => Ok(LinkedNode::Primative(t)),
Node::Foreign(t) => link_foreign(t, ctx),
Node::Group(g) => link_group(g, ctx),
Node::Map(g) => link_struct(g, ctx),
Node::Array(a) => link_array(a, ctx),
Node::KeyVal(_) => Err(FlattenError::Infallible),
}
}
fn link_array(arr: Array, ctx: &BTreeMap<String, Node>) -> FlattenResult<LinkedNode> {
link_node(*arr.ty, ctx).and_then(|node| match node {
LinkedNode::Array(..) => Err(FlattenError::InvalidArray),
LinkedNode::Fields(_) => Err(FlattenError::InvalidArray),
LinkedNode::Struct(_) => Err(FlattenError::InvalidArray),
n => Ok(LinkedNode::Array(LinkedArray {
ty: Box::new(n),
len: arr.len,
})),
})
}
fn link_foreign(key: String, ctx: &BTreeMap<String, Node>) -> FlattenResult<LinkedNode> {
ctx.get(&key)
.map(Node::clone)
.ok_or_else(|| FlattenError::ForeignKey(key.clone()))
.and_then(|node| match link_node(node, ctx)? {
LinkedNode::Struct(_s) => Ok(LinkedNode::ForeignStruct(key)),
node => Ok(node),
})
}
fn link_group(map: Group, ctx: &BTreeMap<String, Node>) -> FlattenResult<LinkedNode> {
link_field_key_values(map, ctx).map(|members| LinkedNode::Fields(Fields { members }))
}
fn link_struct(map: Group, ctx: &BTreeMap<String, Node>) -> FlattenResult<LinkedNode> {
link_field_key_values(map, ctx).map(|members| LinkedNode::Struct(Fields { members }))
}
fn link_field_key_values(
map: Group,
ctx: &BTreeMap<String, Node>,
) -> FlattenResult<Vec<LinkedKeyVal>> {
Ok(link_fields(map, ctx)?
.into_iter()
.map(LinkedKeyVal::from)
.collect())
}
fn link_fields(
map: Group,
ctx: &BTreeMap<String, Node>,
) -> FlattenResult<Vec<(String, LinkedNode)>> {
Ok(map
.members
.into_iter()
.map(|node| link_field_member(node, ctx))
.collect::<FlattenResult<Vec<Vec<(String, LinkedNode)>>>>()?
.into_iter()
.flatten()
.collect())
}
fn link_field_member(
node: Node,
ctx: &BTreeMap<String, Node>,
) -> FlattenResult<Vec<(String, LinkedNode)>> {
match node {
Node::KeyVal(KeyVal(k, v)) => link_node(*v, ctx).map(|n| vec![(k, n)]),
Node::Foreign(key) => match ctx.get(&key).map(Node::clone) {
Some(Node::Group(g)) => link_fields(g, ctx),
_ => Err(FlattenError::InvalidType),
},
_ => Err(FlattenError::InvalidGroupMissingKey),
}
}