config_to_rs 0.2.2

Convert config files to Rust code
Documentation
use convert_case::{Case, Casing};
use hashlink::LinkedHashMap;
use saphyr::Yaml;

use crate::types::{Ast, Type};

impl Ast {
    pub fn from_yaml(base_name: String, yaml: Yaml) -> Ast {
        parse("", &base_name, yaml, false)
    }
}

fn parse(path: &str, key: &str, yaml: Yaml, in_array: bool) -> Ast {
    let path = if path.is_empty() {
        key.to_string()
    } else {
        format!("{} {}", path, key).to_case(Case::Pascal)
    };
    match yaml {
        Yaml::Hash(hash) => parse_hashtable(&path, key, hash, in_array),
        Yaml::Array(array) => parse_array(&path, key, array),
        x => parse_terminal(key, x),
    }
}

fn parse_hashtable(path: &str, key: &str, hash: LinkedHashMap<Yaml, Yaml>, in_array: bool) -> Ast {
    let mut fields = vec![];
    let mut types = vec![];
    hash.into_iter().for_each(|(entry_key, value)| {
        let entry_key = entry_key.as_str().unwrap();
        let value = parse(path, entry_key, value, in_array);
        let type_name = (path.to_string() + "_" + entry_key).to_case(Case::Pascal);
        let type_def = value.get_type();
        fields.push(value.clone());
        if in_array {
            types.push((type_name.to_string(), type_def));
        }
    });
    Ast::HashTable {
        key: key.to_string(),
        type_name: path.to_string(),
        children: fields,
        type_def: Type::HashTable {
            name: path.to_string(),
            full_type: types,
        },
    }
}

fn parse_array(path: &str, key: &str, values: Vec<Yaml>) -> Ast {
    let mut fields = vec![];
    let mut inner_type: Option<Type> = None;
    values.iter().for_each(|value| {
        let value = parse(path, "", value.clone(), true);
        if let Some(t) = inner_type.clone() {
            if t != value.get_type() {
                panic!("All entries in an array must have the same type");
            }
        } else {
            inner_type = Some(value.get_type());
        }
        fields.push(value);
    });

    if fields.is_empty() {
        return Ast::Array {
            key: key.to_string(),
            type_def: Type::Array(Box::new(Type::Empty), 0),
            children: vec![],
        };
    }
    Ast::Array {
        key: key.to_string(),
        type_def: Type::Array(Box::new(fields[0].get_type()), fields.len()),
        children: fields,
    }
}

fn parse_terminal(key: &str, value: Yaml) -> Ast {
    match value {
        Yaml::String(s) => Ast::String {
            key: key.to_string(),
            value: s,
        },
        Yaml::Real(r) => Ast::Float {
            key: key.to_string(),
            value: r.parse().unwrap(),
        },
        Yaml::Integer(i) => Ast::Int {
            key: key.to_string(),
            value: i,
        },
        Yaml::Boolean(b) => Ast::Bool {
            key: key.to_string(),
            value: b,
        },
        _ => panic!("Not supported yet"),
    }
}