tsproto_structs/
book.rs

1use heck::*;
2use once_cell::sync::Lazy;
3use serde::Deserialize;
4
5use crate::*;
6
7pub const DATA_STR: &str =
8	include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/declarations/Book.toml"));
9
10pub static DATA: Lazy<BookDeclarations> = Lazy::new(|| toml::from_str(DATA_STR).unwrap());
11
12#[derive(Deserialize, Debug)]
13#[serde(deny_unknown_fields)]
14pub struct BookDeclarations {
15	#[serde(rename = "struct")]
16	pub structs: Vec<Struct>,
17}
18
19impl BookDeclarations {
20	pub fn get_struct(&self, name: &str) -> &Struct {
21		if let Some(s) = self.structs.iter().find(|s| s.name == name) {
22			s
23		} else {
24			panic!("Cannot find bookkeeping struct {}", name);
25		}
26	}
27}
28
29#[derive(Deserialize, Clone, Debug)]
30#[serde(deny_unknown_fields)]
31pub struct Id {
32	#[serde(rename = "struct")]
33	pub struct_name: String,
34	pub prop: String,
35}
36
37impl Id {
38	pub fn find_property<'a>(&self, structs: &'a [Struct]) -> &'a Property {
39		// Find struct
40		for s in structs {
41			if s.name == self.struct_name {
42				// Find property
43				for p in &s.properties {
44					if p.name == self.prop {
45						return p;
46					}
47				}
48			}
49		}
50		panic!("Cannot find struct {} of id", self.struct_name);
51	}
52}
53
54#[derive(Deserialize, Clone, Debug)]
55#[serde(deny_unknown_fields)]
56pub struct Struct {
57	pub name: String,
58	#[serde(default = "get_false")]
59	pub opt: bool,
60	pub id: Vec<Id>,
61	pub doc: String,
62	pub properties: Vec<Property>,
63}
64
65#[derive(Deserialize, Clone, Debug)]
66#[serde(deny_unknown_fields)]
67pub struct Property {
68	/// The name of this property (in PascalCase) which can be called from rust
69	/// when generated.
70	pub name: String,
71	/// The rust declaration type.
72	#[serde(rename = "type")]
73	pub type_s: String,
74	pub doc: Option<String>,
75	#[serde(default = "get_false")]
76	pub opt: bool,
77	#[serde(rename = "mod")]
78	pub modifier: Option<String>,
79	pub key: Option<String>,
80}
81
82impl Struct {
83	pub fn get_type(&self) -> Result<RustType> { RustType::with_opt(&self.name, self.opt) }
84
85	pub fn get_ids(&self, structs: &[Struct]) -> String {
86		let mut res = String::new();
87		for id in &self.id {
88			let p = id.find_property(structs);
89			if !res.is_empty() {
90				res.push_str(", ");
91			}
92			res.push_str(&p.get_type().unwrap().to_string());
93		}
94		embrace(&res)
95	}
96
97	pub fn get_properties(&self, structs: &[Struct]) -> Vec<&Property> {
98		self.properties.iter().filter(|p| !structs.iter().any(|s| s.name == p.type_s)).collect()
99	}
100
101	/// Get all properties, including foreign ids (own ids are listed in properties).
102	pub fn get_all_properties(&self) -> impl Iterator<Item = PropId> {
103		self.id.iter()
104			// Only foreign ids, others are also stored in the properties
105			.filter_map(move |i| if i.struct_name != self.name { Some(PropId::from(i)) }
106				else { None })
107			.chain(self.properties.iter().map(|p| p.into()))
108	}
109}
110
111impl Property {
112	pub fn get_inner_type(&self) -> Result<RustType> { RustType::with_opt(&self.type_s, self.opt) }
113
114	pub fn get_type(&self) -> Result<RustType> {
115		let key = if self.is_map() {
116			Some(self.key.as_deref().ok_or_else(|| {
117				eprintln!("Specified map without key");
118				fmt::Error
119			})?)
120		} else {
121			None
122		};
123		RustType::with(&self.type_s, self.opt, key, self.is_set(), self.is_array())
124	}
125
126	/// Gets the type as a name, used for storing it in an enum.
127	pub fn get_inner_type_as_name(&self) -> Result<String> { Ok(self.get_inner_type()?.to_name()) }
128
129	pub fn get_ids(&self, structs: &[Struct], struc: &Struct) -> String {
130		let mut ids = struc.get_ids(structs);
131		if !ids.is_empty() {
132			ids.remove(0);
133			ids.pop();
134		}
135		if let Some(m) = &self.modifier {
136			if !ids.is_empty() {
137				ids.push_str(", ");
138			}
139			if m == "map" {
140				// The key is part of the id
141				ids.push_str(self.key.as_ref().unwrap());
142			} else if m == "array" || m == "set" {
143				// Take the element itself as part of the id.
144				// It has to be copied but most of the times it is an id itself.
145				ids.push_str(&self.get_inner_type().unwrap().to_string());
146			} else {
147				panic!("Unknown modifier {}", m);
148			}
149		}
150		embrace(&ids)
151	}
152
153	/// Get the name without trailing `s`.
154	pub fn get_name(&self) -> &str {
155		if self.modifier.is_some() && self.name.ends_with('s') {
156			&self.name[..self.name.len() - 1]
157		} else {
158			&self.name
159		}
160	}
161
162	pub fn is_array(&self) -> bool { self.modifier.as_ref().map(|s| s == "array").unwrap_or(false) }
163	pub fn is_set(&self) -> bool { self.modifier.as_ref().map(|s| s == "set").unwrap_or(false) }
164	pub fn is_map(&self) -> bool { self.modifier.as_ref().map(|s| s == "map").unwrap_or(false) }
165}
166
167pub enum PropId<'a> {
168	Prop(&'a Property),
169	Id(&'a Id),
170}
171
172impl<'a> PropId<'a> {
173	pub fn get_attr_name(&self, struc: &Struct) -> String {
174		match *self {
175			PropId::Prop(p) => p.name.to_snake_case(),
176			PropId::Id(id) => {
177				if struc.name == id.struct_name {
178					id.prop.to_snake_case()
179				} else {
180					format!("{}_{}", id.struct_name.to_snake_case(), id.prop.to_snake_case(),)
181				}
182			}
183		}
184	}
185
186	pub fn get_doc(&self) -> Option<&str> {
187		match *self {
188			PropId::Prop(p) => p.doc.as_deref(),
189			PropId::Id(_) => None,
190		}
191	}
192
193	pub fn get_type(&self, structs: &[Struct]) -> Result<RustType> {
194		match *self {
195			PropId::Prop(p) => p.get_type(),
196			PropId::Id(id) => id.find_property(structs).get_type(),
197		}
198	}
199}
200
201impl<'a> From<&'a Property> for PropId<'a> {
202	fn from(p: &'a Property) -> Self { PropId::Prop(p) }
203}
204
205impl<'a> From<&'a Id> for PropId<'a> {
206	fn from(p: &'a Id) -> Self { PropId::Id(p) }
207}