ice_rs/slice/
module.rs

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            // This file has been generated.
221            #[allow(dead_code)]
222            #[allow(unused_imports)]
223        });
224
225        // build up use statements
226        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}