1use std::str::FromStr;
2
3use heck::*;
4use once_cell::sync::Lazy;
5use serde::Deserialize;
6
7use crate::book::{BookDeclarations, Property, Struct};
8use crate::messages::{Field, Message, MessageDeclarations};
9use crate::*;
10
11pub const DATA_STR: &str =
12 include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/declarations/BookToMessages.toml"));
13
14pub static DATA: Lazy<BookToMessagesDeclarations<'static>> = Lazy::new(|| {
15 let rules: TomlStruct = toml::from_str(DATA_STR).unwrap();
16 let book = &book::DATA;
17 let messages = &messages::DATA;
18
19 let decls: Vec<_> = rules
20 .rule
21 .into_iter()
22 .map(|r| {
23 let msg = messages.get_message(&r.to);
24 let msg_fields =
25 msg.attributes.iter().map(|a| messages.get_field(a)).collect::<Vec<_>>();
26 let book_struct = book
27 .structs
28 .iter()
29 .find(|s| s.name == r.from)
30 .unwrap_or_else(|| panic!("Cannot find struct {}", r.from));
31
32 let find_prop =
33 |name: &str, book_struct: &'static Struct| -> Option<&'static Property> {
34 book_struct.properties.iter().find(|p| p.name == *name)
35 };
36
37 let to_rule_kind = |p: RuleProperty| {
39 p.assert_valid();
40
41 if p.function.is_some() {
42 if p.type_s.is_some() {
43 RuleKind::ArgumentFunction {
44 type_s: p.type_s.unwrap(),
45 from: p.from.unwrap(),
46 name: p.function.unwrap(),
47 to: p
48 .tolist
49 .unwrap()
50 .into_iter()
51 .map(|p| find_field(&p, &msg_fields))
52 .collect(),
53 }
54 } else {
55 RuleKind::Function {
56 from: p.from.as_ref().map(|p| {
57 find_prop(p, book_struct).unwrap_or_else(|| {
58 panic!("No such (nested) property {} found in struct", p)
59 })
60 }),
61 name: p.function.unwrap(),
62 to: p
63 .tolist
64 .unwrap()
65 .into_iter()
66 .map(|p| find_field(&p, &msg_fields))
67 .collect(),
68 }
69 }
70 } else if let Some(prop) = find_prop(p.from.as_ref().unwrap(), book_struct) {
71 RuleKind::Map { from: prop, to: find_field(&p.to.unwrap(), &msg_fields) }
72 } else {
73 RuleKind::ArgumentMap {
74 from: p.from.unwrap(),
75 to: find_field(&p.to.unwrap(), &msg_fields),
76 }
77 }
78 };
79
80 let mut ev = Event {
81 op: r.operation.parse().expect("Failed to parse operation"),
82 ids: r.ids.into_iter().map(to_rule_kind).collect(),
83 msg,
84 book_struct,
85 rules: r.properties.into_iter().map(to_rule_kind).collect(),
86 };
87
88 for field in msg_fields.iter().filter(|f| msg.attributes.iter().any(|a| *a == f.map)) {
91 if !ev.ids.iter().any(|i| match i {
92 RuleKind::Map { to, .. } => to == field,
93 RuleKind::ArgumentMap { to, .. } => to == field,
94 RuleKind::Function { to, .. } | RuleKind::ArgumentFunction { to, .. } => {
95 to.contains(field)
96 }
97 }) {
98 if let Some(prop) = book
100 .get_struct(&ev.book_struct.name)
101 .properties
102 .iter()
103 .find(|p| !p.opt && p.name == field.pretty)
104 {
105 ev.ids.push(RuleKind::Map { from: prop, to: field })
106 }
107 }
109 }
110
111 for field in msg_fields.iter().filter(|f| !msg.attributes.iter().any(|a| *a == f.map)) {
113 if !ev.ids.iter().chain(ev.rules.iter()).any(|i| match i {
114 RuleKind::Map { to, .. } => to == field,
115 RuleKind::ArgumentMap { to, .. } => to == field,
116 RuleKind::Function { to, .. } | RuleKind::ArgumentFunction { to, .. } => {
117 to.contains(field)
118 }
119 }) {
120 if let Some(prop) = book
124 .get_struct(&ev.book_struct.name)
125 .properties
126 .iter()
127 .find(|p| p.name == field.pretty)
128 {
129 if !ev.ids.iter().chain(ev.rules.iter()).any(|i| i.from_name() == prop.name)
130 {
131 ev.rules.push(RuleKind::Map { from: prop, to: field })
132 }
133 }
134 }
135 }
136
137 ev
138 })
139 .collect();
140
141 BookToMessagesDeclarations { book, messages, decls }
142});
143
144#[derive(Debug)]
145pub struct BookToMessagesDeclarations<'a> {
146 pub book: &'a BookDeclarations,
147 pub messages: &'a MessageDeclarations,
148 pub decls: Vec<Event<'a>>,
149}
150
151#[derive(Debug)]
152pub struct Event<'a> {
153 pub op: RuleOp,
154 pub msg: &'a Message,
155 pub book_struct: &'a Struct,
156 pub ids: Vec<RuleKind<'a>>,
157 pub rules: Vec<RuleKind<'a>>,
158}
159
160#[derive(Debug)]
161pub enum RuleKind<'a> {
162 Map { from: &'a Property, to: &'a Field },
163 ArgumentMap { from: String, to: &'a Field },
164 Function { from: Option<&'a Property>, name: String, to: Vec<&'a Field> },
165 ArgumentFunction { from: String, type_s: String, name: String, to: Vec<&'a Field> },
166}
167
168#[derive(Debug, PartialEq, Eq, Clone, Copy)]
169pub enum RuleOp {
170 Add,
171 Remove,
172 Update,
173}
174
175#[derive(Deserialize, Debug)]
176#[serde(deny_unknown_fields)]
177struct TomlStruct {
178 rule: Vec<Rule>,
179}
180
181#[derive(Clone, Deserialize, Debug)]
182#[serde(deny_unknown_fields)]
183pub struct Rule {
184 from: String,
185 to: String,
186 operation: String,
187 #[serde(default = "Vec::new")]
188 ids: Vec<RuleProperty>,
189 #[serde(default = "Vec::new")]
190 properties: Vec<RuleProperty>,
191}
192
193#[derive(Clone, Deserialize, Debug)]
194#[serde(deny_unknown_fields)]
195pub struct RuleProperty {
196 from: Option<String>,
197 to: Option<String>,
198
199 #[serde(rename = "type")]
200 type_s: Option<String>,
201 function: Option<String>,
202 tolist: Option<Vec<String>>,
203}
204
205impl RuleProperty {
206 fn assert_valid(&self) {
207 if let Some(to) = &self.to {
208 assert!(self.from.is_some(), "to-property '{}' is invalid. It needs a 'from'", to);
209 assert!(
210 self.function.is_none(),
211 "to-property '{}' is invalid. It must not have a 'function'",
212 to
213 );
214 assert!(
215 self.tolist.is_none(),
216 "to-property '{}' is invalid. It must not have a 'tolist'",
217 to
218 );
219 assert!(
220 self.type_s.is_none(),
221 "to-property '{}' is invalid. It must not have a 'type'",
222 to
223 );
224 } else if let Some(fun) = &self.function {
225 assert!(
226 self.tolist.is_some(),
227 "function-property '{}' is invalid. It needs 'tolist'",
228 fun
229 );
230 assert!(
231 self.type_s.is_none() || self.from.is_some(),
232 "function-property '{}' is invalid. If the type ({:?}) is set, from must be set \
233 too",
234 fun,
235 self.type_s
236 );
237 } else {
238 panic!(
239 "Property is invalid. It needs either a 'to' or 'tolist'+'function'.Info: \
240 tolist={:?} type={:?} from={:?}",
241 self.tolist, self.type_s, self.from
242 );
243 }
244 }
245}
246
247impl FromStr for RuleOp {
248 type Err = fmt::Error;
249 fn from_str(s: &str) -> Result<Self> {
250 if s == "add" {
251 Ok(RuleOp::Add)
252 } else if s == "remove" {
253 Ok(RuleOp::Remove)
254 } else if s == "update" {
255 Ok(RuleOp::Update)
256 } else {
257 eprintln!("Cannot parse operation, needs to be add, remove or update");
258 Err(fmt::Error)
259 }
260 }
261}
262
263fn find_field<'a>(name: &str, msg_fields: &[&'a Field]) -> &'a Field {
265 *msg_fields
266 .iter()
267 .find(|f| f.pretty == name)
268 .unwrap_or_else(|| panic!("Cannot find field '{}'", name))
269}
270
271impl<'a> RuleKind<'a> {
272 pub fn from_name(&'a self) -> &'a str {
273 match self {
274 RuleKind::Map { from, .. } => &from.name,
275 RuleKind::ArgumentMap { from, .. } => &from,
276 RuleKind::Function { from, name, .. } => {
277 &from.unwrap_or_else(|| panic!("From not set for function {}", name)).name
278 }
279 RuleKind::ArgumentFunction { from, .. } => &from,
280 }
281 }
282
283 pub fn from_name_singular(&'a self) -> &'a str {
284 let name = self.from_name();
285 if let Some(s) = name.strip_suffix('s') { s } else { name }
286 }
287
288 pub fn from(&self) -> &'a Property {
289 match self {
290 RuleKind::Map { from, .. } => from,
291 RuleKind::Function { from, name, .. } => {
292 from.unwrap_or_else(|| panic!("From not set for function {}", name))
293 }
294 RuleKind::ArgumentMap { .. } | RuleKind::ArgumentFunction { .. } => {
295 panic!("From is not a property for argument functions")
296 }
297 }
298 }
299
300 pub fn is_function(&self) -> bool {
301 matches!(self, RuleKind::Function { .. } | RuleKind::ArgumentFunction { .. })
302 }
303
304 pub fn get_type(&self) -> RustType {
305 match self {
306 RuleKind::Map { .. } | RuleKind::Function { .. } => self.from().get_type().unwrap(),
307 RuleKind::ArgumentMap { to, .. } => to.get_type("").unwrap(),
308 RuleKind::ArgumentFunction { type_s, .. } => type_s.parse().unwrap(),
309 }
310 }
311
312 pub fn get_type_no_option(&self) -> RustType {
313 match self {
314 RuleKind::Map { .. } => {
315 let mut rust_type = self.from().clone();
316 rust_type.opt = false;
317 rust_type.get_type().unwrap()
318 }
319 _ => self.get_type(),
320 }
321 }
322
323 pub fn get_argument(&self) -> String {
324 format!("{}: {}", self.from_name().to_snake_case(), self.get_type().to_ref(true))
325 }
326
327 pub fn get_argument_no_option(&self) -> String {
328 format!("{}: {}", self.from_name().to_snake_case(), self.get_type_no_option().to_ref(true))
329 }
330}
331
332impl<'a> Event<'a> {
333 pub fn get_small_name(&self) -> String {
335 self.msg.name.replace(&self.book_struct.name, "").replace("Request", "")
337 }
338
339 pub fn get_change_name(&self) -> String {
341 let small_change_name = self.get_small_name();
342 if small_change_name == "Move" { self.msg.name.clone() } else { small_change_name }
343 }
344}