199 lines
6.4 KiB
Rust
199 lines
6.4 KiB
Rust
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_field(field_yaml: &Yaml) -> StructField {
|
|
let (name, props) = unwrap_single_member_hash(field_yaml);
|
|
let kind = props["kind"].as_str().unwrap();
|
|
let wrap = if let Some(wrap) = props["wrap"].as_str() {
|
|
match wrap {
|
|
"rc_ref_cell" => Some(StructFieldWrap::RcRefCell),
|
|
_ => panic!(),
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
let vec = get_as_bool(&props["vec"]);
|
|
StructField::new(&name, kind, wrap, vec)
|
|
}
|
|
|
|
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<Box<StructChild>> {
|
|
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 fields = if struct_yaml["fields"].is_array() {
|
|
struct_yaml["fields"]
|
|
.as_vec()
|
|
.unwrap()
|
|
.iter()
|
|
.map(|field| deserialize_field(field))
|
|
.map(Box::new)
|
|
.collect()
|
|
} else {
|
|
vec![]
|
|
};
|
|
|
|
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, fields, derive)
|
|
}
|