1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use crate::*;
use lazy_static::lazy_static;
use serde_derive::Deserialize;

pub const DATA_STR: &str = include_str!(concat!(
	env!("CARGO_MANIFEST_DIR"),
	"/declarations/BookDeclarations.toml"
));

lazy_static! {
	pub static ref DATA: BookDeclarations = toml::from_str(DATA_STR).unwrap();
}

#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct BookDeclarations {
	#[serde(rename = "struct")]
	pub structs: Vec<Struct>,
}

impl BookDeclarations {
	pub fn get_struct(&self, name: &str) -> &Struct {
		if let Some(s) = self.structs.iter().find(|s| s.name == name) {
			s
		} else {
			panic!("Cannot find bookkeeping struct {}", name);
		}
	}
}

#[derive(Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Accessors {
	pub get: bool,
	pub set: bool,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Id {
	#[serde(rename = "struct")]
	pub struct_name: String,
	pub prop: String,
}

impl Id {
	pub fn find_property<'a>(&self, structs: &'a [Struct]) -> &'a Property {
		// Find struct
		for s in structs {
			if s.name == self.struct_name {
				// Find property
				for p in &s.properties {
					if p.name == self.prop {
						return p;
					}
				}
			}
		}
		panic!("Cannot find struct {} of id", self.struct_name);
	}
}

#[derive(Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Struct {
	pub name: String,
	pub id: Vec<Id>,
	pub doc: String,
	pub accessor: Accessors,
	pub properties: Vec<Property>,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Property {
	/// The name of this property (in PascalCase) which can be called from rust
	/// when generated.
	pub name: String,
	/// The rust declaration type.
	#[serde(rename = "type")]
	pub type_s: String,
	pub doc: Option<String>,
	pub get: Option<bool>,
	pub set: Option<bool>,
	#[serde(default = "get_false")]
	pub opt: bool,
	#[serde(rename = "mod")]
	pub modifier: Option<String>,
	pub key: Option<String>,
}

impl Property {
	pub fn get_get(&self, struc: &Struct) -> bool {
		self.get.unwrap_or_else(|| struc.accessor.get)
	}
	pub fn get_set(&self, struc: &Struct) -> bool {
		self.set.unwrap_or_else(|| struc.accessor.set)
	}
	pub fn get_rust_type(&self) -> String {
		let mut res = convert_type(&self.type_s, false);

		if self
			.modifier
			.as_ref()
			.map(|s| s == "array")
			.unwrap_or(false)
		{
			res = format!("Vec<{}>", res);
		} else if self.modifier.as_ref().map(|s| s == "map").unwrap_or(false) {
			let key = self.key.as_ref().expect("Specified map without key");
			res = format!("HashMap<{}, {}>", key, res);
		}
		if self.opt {
			res = format!("Option<{}>", res);
		}
		res
	}
}

pub enum PropId<'a> {
	Prop(&'a Property),
	Id(&'a Id),
}

impl<'a> PropId<'a> {
	pub fn get_attr_name(&self, struc: &Struct) -> String {
		match *self {
			PropId::Prop(p) => to_snake_case(&p.name),
			PropId::Id(id) => {
				if struc.name == id.struct_name {
					to_snake_case(&id.prop)
				} else {
					format!(
						"{}_{}",
						to_snake_case(&id.struct_name),
						to_snake_case(&id.prop)
					)
				}
			}
		}
	}

	pub fn get_doc(&self) -> Option<&str> {
		match *self {
			PropId::Prop(p) => p.doc.as_ref().map(|s| s.as_str()),
			PropId::Id(_) => None,
		}
	}

	pub fn get_rust_type(&self, structs: &[Struct]) -> String {
		match *self {
			PropId::Prop(p) => p.get_rust_type(),
			PropId::Id(id) => id.find_property(structs).get_rust_type(),
		}
	}
}

impl<'a> From<&'a Property> for PropId<'a> {
	fn from(p: &'a Property) -> Self { PropId::Prop(p) }
}

impl<'a> From<&'a Id> for PropId<'a> {
	fn from(p: &'a Id) -> Self { PropId::Id(p) }
}