use crate::deserialize_error; use convert_case::{Case, Casing}; use crate::deserialize::util::{get_as_bool, make_build_fn_name, unwrap_single_member_hash}; use crate::spec::struct_spec::*; use yaml_rust2::Yaml; fn deserialize_skip_child(props: &Yaml) -> StructChild { let rule = props["rule"].as_str().unwrap(); StructChild::SkipChild(SkipChild::new(rule)) } fn deserialize_vec_child(child_name: &str, props: &Yaml) -> StructChild { let rule = props["rule"].as_str().unwrap(); let kind = props["kind"].as_str().unwrap_or(rule); if kind == "string" { let build = VecChildBuild::String(VecChildStringBuild::new(&make_build_fn_name(rule))); StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build))) } else { let build = VecChildBuild::Node(VecChildNodeBuild::new(rule, &make_build_fn_name(rule))); StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build))) } } fn deserialize_member_build(child_name: &str, rule: &str, props: &Yaml) -> MemberChildBuild { if props["node"].is_hash() { let node_props = &props["node"]; let kind = node_props["kind"].as_str().unwrap_or(rule); let with = node_props["with"] .as_str() .map(|with| make_build_fn_name(with)) .unwrap_or_else(|| make_build_fn_name(kind)); let or_else = if get_as_bool(&node_props["or_else_default"]) { Some(String::from("default")) } else if let Some(or_else) = node_props["or_else"].as_str() { Some(or_else.to_string()) } else { None }; MemberChildBuild::Node(NodeMemberBuild::new(kind, &with, or_else)) } else if props["boolean"].is_hash() { let boolean_props = &props["boolean"]; if let Some(on) = boolean_props["on"].as_str() { if on == "rule_present" { MemberChildBuild::Boolean(BooleanMemberBuild::new( BooleanMemberBuildOn::RulePresent, )) } else { panic!( "Expected 'on' in 'boolean' in 'build' in {} to be 'rule_present'", child_name ); } } else { panic!("Expected 'on' in 'boolean' in 'build' in {}", child_name); } } else { panic!( "Expected one of 'node', 'boolean', or 'special' in 'build' in {}", child_name ); } } fn deserialize_member_child(child_name: &str, props: &Yaml) -> StructChild { let rule = props["rule"] .as_str() .map(ToString::to_string) .unwrap_or_else(|| child_name.to_case(Case::Pascal)); let optional = get_as_bool(&props["optional"]); if props["build"].is_hash() { let build = deserialize_member_build(child_name, &rule, &props["build"]); StructChild::MemberChild(MemberChild::new( child_name, &rule, optional, Box::new(build), )) } else { StructChild::MemberChild(MemberChild::new( child_name, &rule, optional, Box::new(MemberChildBuild::Node(NodeMemberBuild::new( &rule, &make_build_fn_name(&rule), None, ))), )) } } fn deserialize_special_child(name: &str, props: &Yaml) -> StructChild { match props["kind"].as_str().unwrap() { "file_id" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::FileId)), "range" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::Range)), _ => panic!("Invalid special child kind {} in {}", props["kind"].as_str().unwrap(), name), } } fn deserialize_hash_child(name: &str, props: &Yaml) -> StructChild { if props["skip"].is_hash() { deserialize_skip_child(&props["skip"]) } else if props["vec"].is_hash() { deserialize_vec_child(name, &props["vec"]) } else if props["member"].is_hash() { deserialize_member_child(name, &props["member"]) } else if props["special"].is_hash() { deserialize_special_child(name, &props["special"]) } else { panic!("Expected 'skip' or 'vec' in 'member' in {}", name); } } fn deserialize_string_child(name: &str) -> StructChild { let name_pascal = name.to_case(Case::Pascal); StructChild::MemberChild(MemberChild::new( name, &name_pascal, false, Box::new(MemberChildBuild::Node(NodeMemberBuild::new( &name_pascal, &make_build_fn_name(name), None, ))), )) } fn deserialize_children(children_yaml: &Yaml) -> Vec> { children_yaml .as_vec() .unwrap() .iter() .map(|child_yaml| { if let Some(name) = child_yaml.as_str() { deserialize_string_child(name) } else { let (child_name, child_props) = unwrap_single_member_hash(child_yaml); deserialize_hash_child(&child_name, child_props) } }) .map(Box::new) .collect() } pub fn deserialize_struct_spec(name: &str, struct_yaml: &Yaml) -> StructSpec { let children = if struct_yaml["children"].is_array() { deserialize_children(&struct_yaml["children"]) } else { deserialize_error!("array", "children", name); }; let derive = if struct_yaml["derive"].is_array() { struct_yaml["derive"].as_vec() .unwrap() .iter() .map(|derive| derive.as_str().unwrap().to_string()) .collect() } else { vec![] }; StructSpec::new(name, children, derive) }