1use crate::{errors::ParsingError, slice::enumeration::Enum};
2use crate::slice::structure::Struct;
3use crate::slice::interface::Interface;
4use crate::slice::exception::Exception;
5use crate::slice::class::Class;
6use std::{path::Path, process::Stdio};
7use std::fs::File;
8use std::collections::BTreeMap;
9use std::rc::Rc;
10use std::cell::RefCell;
11use inflector::cases::{pascalcase, snakecase};
12use quote::{__private::TokenStream, format_ident, quote};
13use std::io::Write;
14use super::types::IceType;
15use std::process::Command;
16
17
18struct UseStatements {
19 uses: BTreeMap<String, TokenStream>,
20}
21
22impl UseStatements {
23 fn new() -> UseStatements {
24 UseStatements {
25 uses: BTreeMap::new()
26 }
27 }
28
29 fn use_crate(&mut self, token: TokenStream) {
30 self.uses.insert(token.to_string(), token);
31 }
32
33 fn generate(&self) -> Result<TokenStream, Box<dyn std::error::Error>>{
34 let tokens = self.uses.iter().map(|(_, token)| {
35 quote! {
36 #token;
37 }
38 }).collect::<Vec<_>>();
39 Ok(quote! {
40 #(#tokens)*
41 })
42 }
43}
44
45pub struct Module {
46 pub name: String,
47 pub full_name: String,
48 pub sub_modules: Vec<Module>,
49 enumerations: Vec<Enum>,
50 exceptions: Vec<Exception>,
51 structs: Vec<Struct>,
52 interfaces: Vec<Interface>,
53 typedefs: Vec<(String, IceType)>,
54 classes: Vec<Class>,
55 pub type_map: Rc<RefCell<BTreeMap<String, String>>>
56}
57
58impl Module {
59 pub fn new(type_map: Rc<RefCell<BTreeMap<String, String>>>) -> Module {
60 Module {
61 name: String::new(),
62 full_name: String::new(),
63 sub_modules: vec![],
64 enumerations: vec![],
65 structs: vec![],
66 interfaces: vec![],
67 exceptions: vec![],
68 typedefs: vec![],
69 classes: vec![],
70 type_map: type_map
71 }
72 }
73
74 pub fn has_dict(&self) -> bool {
75 for (_, var) in &self.typedefs {
76 match var {
77 IceType::DictType(_, _) => return true,
78 _ => {}
79 }
80 }
81 false
82 }
83
84 pub fn snake_name(&self) -> String {
85 snakecase::to_snake_case(&self.name)
86 }
87
88 pub fn add_enum(&mut self, enumeration: Enum) {
89 self.enumerations.push(enumeration);
90 }
91
92 pub fn add_struct(&mut self, structure: Struct) {
93 self.structs.push(structure);
94 }
95
96 pub fn add_interface(&mut self, interface: Interface) {
97 self.interfaces.push(interface);
98 }
99
100 pub fn add_exception(&mut self, exception: Exception) {
101 self.exceptions.push(exception);
102 }
103
104 pub fn add_typedef(&mut self, id: &str, vartype: IceType) {
105 self.typedefs.push((String::from(id), vartype.clone()));
106 }
107
108 pub fn add_class(&mut self, class: Class) {
109 self.classes.push(class);
110 }
111
112 fn uses(&self, super_mod: &str) -> UseStatements {
113 let mut use_statements = UseStatements::new();
114
115 use_statements.use_crate(quote! { use async_trait::async_trait });
116 if self.has_dict() {
117 use_statements.use_crate(quote! { use std::collections::HashMap });
118 }
119
120 if self.enumerations.len() > 0 || self.structs.len() > 0 || self.interfaces.len() > 0 {
121 use_statements.use_crate(quote! { use ice_rs::errors::* });
122 }
123
124 if self.enumerations.len() > 0 {
125 use_statements.use_crate(quote! { use num_enum::TryFromPrimitive });
126 use_statements.use_crate(quote! { use std::convert::TryFrom });
127 use_statements.use_crate(quote! { use ice_rs::encoding::* });
128 }
129
130 if self.structs.len() > 0 {
131 use_statements.use_crate(quote! { use ice_rs::encoding::* });
132
133 for item in &self.structs {
134 for member in &item.members {
135 match &member.r#type {
136 IceType::CustomType(name) => {
137 let use_statement = self.type_map.as_ref().borrow().get(name).unwrap().clone();
138 if !use_statement.eq(&self.snake_name()) {
139 let super_token = format_ident!("{}", super_mod);
140 let use_token = format_ident!("{}", use_statement);
141 use_statements.use_crate(quote! { use crate::#super_token::#use_token::* });
142 }
143 }
144 _ => {}
145 }
146 }
147 }
148 }
149
150 if self.classes.len() > 0 {
151 use_statements.use_crate(quote! { use ice_rs::encoding::* });
152
153 for item in &self.classes {
154 for member in &item.members {
155 match &member.r#type {
156 IceType::CustomType(name) => {
157 let use_statement = self.type_map.as_ref().borrow().get(name).unwrap().clone();
158 if !use_statement.eq(&self.snake_name()) {
159 let super_token = format_ident!("{}", super_mod);
160 let use_token = format_ident!("{}", use_statement);
161 use_statements.use_crate(quote! { use crate::#super_token::#use_token::* });
162 }
163 }
164 _ => {}
165 }
166 }
167 }
168 }
169
170 if self.interfaces.len() > 0 {
171 use_statements.use_crate(quote! { use ice_rs::encoding::* });
172 use_statements.use_crate(quote! { use ice_rs::proxy::Proxy });
173 use_statements.use_crate(quote! { use ice_rs::iceobject::* });
174 use_statements.use_crate(quote! { use ice_rs::protocol::* });
175
176 for item in &self.interfaces {
177 for func in &item.functions {
178 use_statements.use_crate(quote! { use std::collections::HashMap });
179
180 for arg in &func.arguments {
181 match &arg.r#type {
182 IceType::CustomType(name) => {
183 let use_statement = self.type_map.as_ref().borrow().get(name).unwrap().clone();
184 if !use_statement.eq(&self.snake_name()) {
185 let super_token = format_ident!("{}", super_mod);
186 let use_token = format_ident!("{}", use_statement);
187 use_statements.use_crate(quote! { use crate::#super_token::#use_token::* });
188 }
189 }
190 _ => {}
191 };
192 }
193
194 match &func.throws.r#type {
195 Some(throws) => {
196 match throws {
197 IceType::CustomType(name) => {
198 let use_statement = self.type_map.as_ref().borrow().get(name).unwrap().clone();
199 if !use_statement.eq(&self.snake_name()) {
200 let super_token = format_ident!("{}", super_mod);
201 let use_token = format_ident!("{}", use_statement);
202 use_statements.use_crate(quote! { use crate::#super_token::#use_token::* });
203 }
204 }
205 _ => {}
206 };
207 }
208 _ => {}
209 };
210 }
211 }
212 }
213
214 use_statements
215 }
216
217 pub fn generate(&self, dest: &Path, mod_path: &str) -> Result<(), Box<dyn std::error::Error>> {
218 let mut tokens = vec![];
219 tokens.push(quote! {
220 #[allow(dead_code)]
222 #[allow(unused_imports)]
223 });
224
225 let mut use_path = mod_path;
227 if use_path.len() == 0 {
228 use_path = dest.iter().last().unwrap().to_str().unwrap();
229 }
230 tokens.push(self.uses(&use_path).generate()?);
231
232 for sub_module in &self.sub_modules {
233 let mod_name = sub_module.snake_name();
234 let ident = format_ident!("{}", mod_name);
235 tokens.push(quote! {
236 pub mod #ident;
237 });
238 sub_module.generate(&dest.join(Path::new(&mod_name)), &use_path)?;
239 }
240
241 for (id, vartype) in &self.typedefs {
242 let id_str = format_ident!("{}", pascalcase::to_pascal_case(&id));
243 let var_token = vartype.token();
244 tokens.push(quote! {
245 type #id_str = #var_token;
246 });
247 }
248
249 for enumeration in &self.enumerations {
250 tokens.push(enumeration.generate()?);
251 }
252
253 for structure in &self.structs {
254 tokens.push(structure.generate()?);
255 }
256
257 for class in &self.classes {
258 tokens.push(class.generate(&self.full_name)?);
259 }
260
261 for exception in &self.exceptions {
262 tokens.push(exception.generate()?);
263 }
264
265 for interface in &self.interfaces {
266 tokens.push(interface.generate(&self.full_name)?);
267 }
268
269 let mod_token = quote! { #(#tokens)* };
270
271 std::fs::create_dir_all(dest)?;
272 let mod_file = &dest.join(Path::new("mod.rs"));
273 let mut child = Command::new("rustfmt")
274 .stdin(Stdio::piped())
275 .stdout(Stdio::piped())
276 .arg("--edition")
277 .arg("2018")
278 .spawn()?;
279 {
280 let stdin = child.stdin.as_mut().ok_or(ParsingError::new("Could not get stdin of rustfmt process"))?;
281 stdin.write_all(mod_token.to_string().as_bytes())?;
282 }
283 let output = child.wait_with_output()?;
284 let mut file = File::create(mod_file)?;
285 match file.write_all(&output.stdout) {
286 Ok(_) => Ok(()),
287 Err(_) => Err(Box::new(ParsingError::new("Could not write file")))
288 }
289 }
290}