ryna/
docs.rs

1use std::fs::OpenOptions;
2use std::{fs::File, path::Path};
3use std::io::Write;
4
5use crate::interfaces::Interface;
6use crate::operations::Operator;
7use crate::types::TypeTemplate;
8use crate::{annotations::Annotation, config::RynaModule, html_ext::HTMLColorable, types::Type};
9
10pub fn default_markdown_style() -> String {
11    [
12        "<style> ",
13        "span { font-family: monospace; } ",
14        "h2 { background: rgb(30,30,30); padding: 0.15em; border-radius: 0.25em; color: rgb(212,212,212); line-height: 1em; } ",
15        "</style>\n\n"
16    ].join("")
17}
18
19pub fn create_markdown_file(base: &String, name: &str) -> File {
20    let docs_path = Path::new(base).join("docs");
21
22    if !docs_path.is_dir() {
23        std::fs::create_dir(&docs_path).expect("Unable to create docs folder");
24    }
25
26    let file_path = docs_path.join(name);
27
28    if file_path.is_file() {
29        std::fs::remove_file(&file_path).expect("Unable to remove docs file");
30    }
31
32    let mut file = OpenOptions::new()
33        .write(true)
34        .append(true)
35        .create(true)
36        .open(docs_path.join(name))
37        .unwrap();
38
39    write!(file, "{}", default_markdown_style()).expect("Error while writing to docs file");
40
41    file
42}
43
44pub fn write_args_and_ret(file: &mut File, annot: &Annotation) {
45    write!(
46        file,
47        "### Parameters\n\n",
48    ).expect("Error while writing to docs file");
49
50    for arg in &annot.args {
51        if arg.0.parse::<usize>().is_err() {
52            write!(
53                file, 
54                "* `{}`: {}\n", arg.0, arg.1
55            ).expect("Error while writing to docs file");
56        }
57    }
58
59    write!(
60        file,
61        "\n### Description\n{}\n\n",
62        annot.args.get("0").unwrap()
63    ).expect("Error while writing to docs file");
64
65    write!(
66        file,
67        "### Return\n{}\n\n",
68        annot.args.get("1").unwrap()
69    ).expect("Error while writing to docs file");
70}
71
72pub fn write_function_overload_docs(file: &mut File, module: &RynaModule, f: &str, t: usize, args: &Type, ret: &Type, annot: &Annotation) {
73    write!(
74        file, 
75        "## {} {}{}{} -> {}\n\n", 
76        "fn".html_magenta(),
77        f.html_yellow(), if t > 0 { 
78            format!("&lt;{}&gt;", (0..t).into_iter()
79                                        .map(|i| format!("T_{}", i).html_blue())
80                                        .collect::<Vec<_>>()
81                                        .join(", ")
82            ) 
83        } else { "".into() },
84        args.get_name_html(&module.ctx), ret.get_name_html(&module.ctx)
85    ).expect("Error while writing to docs file");
86
87    write_args_and_ret(file, annot);
88}
89
90pub fn write_unary_operation_docs(file: &mut File, module: &RynaModule, op: &str, t: usize, args: &Type, ret: &Type, annot: &Annotation, prefix: bool) {
91    if prefix {
92        write!(
93            file, 
94            "## {} {}{}({}) -> {}\n\n", 
95            "op".html_magenta(),
96            op.html_yellow(), if t > 0 { 
97                format!("&lt;{}&gt;", (0..t).into_iter()
98                                            .map(|i| format!("T_{}", i).html_blue())
99                                            .collect::<Vec<_>>()
100                                            .join(", ")
101                ) 
102            } else { "".into() },
103            args.get_name_html(&module.ctx), ret.get_name_html(&module.ctx)
104        ).expect("Error while writing to docs file");
105
106    } else {
107        write!(
108            file, 
109            "## {} ({}){}{} -> {}\n\n", 
110            "op".html_magenta(),
111            args.get_name_html(&module.ctx),
112            if t > 0 { 
113                format!("&lt;{}&gt;", (0..t).into_iter()
114                                            .map(|i| format!("T_{}", i).html_blue())
115                                            .collect::<Vec<_>>()
116                                            .join(", ")
117                ) 
118            } else { "".into() },
119            op.html_yellow(), ret.get_name_html(&module.ctx)
120        ).expect("Error while writing to docs file");    
121    }
122
123    write_args_and_ret(file, annot);
124}
125
126pub fn write_binary_operation_docs(file: &mut File, module: &RynaModule, op: &str, t: usize, args: &Type, ret: &Type, annot: &Annotation) {
127    if let Type::And(args_t) = args {
128        write!(
129            file, 
130            "## {} ({}) {}{} ({}) -> {}\n\n", 
131            "op".html_magenta(),
132            args_t[0].get_name_html(&module.ctx),
133            op.html_yellow(), if t > 0 { 
134                format!("&lt;{}&gt;", (0..t).into_iter()
135                                            .map(|i| format!("T_{}", i).html_blue())
136                                            .collect::<Vec<_>>()
137                                            .join(", ")
138                ) 
139            } else { "".into() },
140            args_t[1].get_name_html(&module.ctx),
141            ret.get_name_html(&module.ctx)
142        ).expect("Error while writing to docs file");    
143    
144    } else {
145        unreachable!()
146    }
147
148    write_args_and_ret(file, annot);
149}
150
151pub fn write_nary_operation_docs(file: &mut File, module: &RynaModule, op_open: &str, op_close: &str, t: usize, args: &Type, ret: &Type, annot: &Annotation) {
152    if let Type::And(args_t) = args {
153        let args_b = &args_t[1..];
154
155        write!(
156            file, 
157            "## {} ({}){}{}{}{} -> {}\n\n", 
158            "op".html_magenta(),
159            args_t[0].get_name_html(&module.ctx),
160            if t > 0 { 
161                format!("&lt;{}&gt;", (0..t).into_iter()
162                                            .map(|i| format!("T_{}", i).html_blue())
163                                            .collect::<Vec<_>>()
164                                            .join(", ")
165                ) 
166            } else { "".into() },
167            op_open.html_yellow(),
168            args_b.into_iter()
169                  .map(|i| i.get_name_html(&module.ctx))
170                  .collect::<Vec<_>>()
171                  .join(", "),
172            op_close.html_yellow(),
173            ret.get_name_html(&module.ctx)
174        ).expect("Error while writing to docs file");    
175    
176    } else {
177        unreachable!()
178    }
179
180    write_args_and_ret(file, annot);
181}
182
183pub fn write_class_docs(file: &mut File, template: &TypeTemplate, annot: &Annotation) {
184    write!(
185        file,
186        "## {} {}{}",
187        "class".html_magenta(),
188        template.name.html_green(),
189        if template.params.len() > 0 { 
190            format!("&lt;{}&gt;", template.params.iter()
191                                                 .map(|i| format!("T_{}", i).html_blue())
192                                                 .collect::<Vec<_>>()
193                                                 .join(", ")
194            ) 
195        } else { "".into() }
196    ).expect("Error while writing to docs file");
197
198    write!(
199        file,
200        "\n\n### Attributes\n\n",
201    ).expect("Error while writing to docs file");
202
203    for arg in &annot.args {
204        if arg.0.parse::<usize>().is_err() {
205            write!(
206                file, 
207                "* `{}`: {}\n", arg.0, arg.1
208            ).expect("Error while writing to docs file");
209        }
210    }
211}
212
213pub fn write_syntax_docs(file: &mut File, name: &String, annot: &Annotation) {
214    write!(
215        file,
216        "## {} {}",
217        "syntax".html_magenta(),
218        name.html_green(),
219    ).expect("Error while writing to docs file");
220
221    write!(
222        file,
223        "\n\n### Description\n{}\n\n",
224        annot.args.get("0").unwrap()
225    ).expect("Error while writing to docs file");
226}
227
228pub fn write_interface_docs(file: &mut File, module: &RynaModule, interface: &Interface, annot: &Annotation) {
229    write!(
230        file,
231        "# {} {}",
232        "interface".html_magenta(),
233        interface.name.html_green(),
234    ).expect("Error while writing to docs file");
235
236    write!(
237        file,
238        "\n\n## Description\n{}\n\n",
239        annot.args.get("0").unwrap()
240    ).expect("Error while writing to docs file");
241
242    for f in &interface.fns {
243        for a in &f.0 {
244            if a.name == "doc" {
245                let args = Type::And(f.3.iter().map(|(_, i)| i).cloned().collect());
246                let templates = f.2.as_ref().map(Vec::len).unwrap_or_default();
247
248                write_function_overload_docs(file, module, &f.1, templates, &args, &f.4, a);
249                break;
250            }
251        }
252    }
253
254    for u in &interface.uns {
255        for a in &u.0 {
256            if a.name == "doc" {
257                if let Operator::Unary { representation, prefix, .. } = &module.ctx.unary_ops[u.1] {
258                    write_unary_operation_docs(file, module, &representation, u.2.len(), &u.4, &u.5, a, *prefix);
259                    break;    
260                }
261            }
262        }
263    }
264
265    for b in &interface.bin {
266        for a in &b.0 {
267            if a.name == "doc" {
268                if let Operator::Binary { representation, .. } = &module.ctx.binary_ops[b.1] {
269                    let args = Type::And(vec!(b.3.1.clone(), b.4.1.clone()));
270                    write_binary_operation_docs(file, module, &representation, b.2.len(), &args, &b.5, a);
271                    break;    
272                }
273            }
274        }
275    }
276
277    for n in &interface.nary {
278        for a in &n.0 {
279            if a.name == "doc" {
280                if let Operator::Nary { open_rep, close_rep, .. } = &module.ctx.nary_ops[n.1] {
281                    let mut args = vec!(n.3.1.clone());
282                    args.extend(n.4.iter().map(|(_, i)| i).cloned());
283                    
284                    write_nary_operation_docs(file, module, &open_rep, &close_rep, n.2.len(), &Type::And(args), &n.5, a);
285                    break;    
286                }
287            }
288        }
289    }
290}
291
292pub fn generate_all_function_overload_docs(project_path: &String, module: &RynaModule) {
293    let mut functions_file = create_markdown_file(project_path, "functions.md");
294
295    for f in &module.ctx.functions {
296        for ov in &f.overloads {
297            if ov.location.module == module.ctx.module_name {
298                for annot in &ov.annotations {
299                    if annot.name == "doc" {
300                        write_function_overload_docs(&mut functions_file, &module, &f.name, ov.templates, &ov.args, &ov.ret, annot);
301                        break;
302                    }    
303                }
304            }
305        }
306    }
307}
308
309pub fn generate_all_operation_docs(project_path: &String, module: &RynaModule) {
310    let mut operations_file = create_markdown_file(project_path, "operations.md");
311
312    write!(operations_file, "# Unary operations\n\n").expect("Error while writing to docs file");
313
314    for o in &module.ctx.unary_ops {
315        if let Operator::Unary { representation, prefix, operations, .. } = o {
316            for ov in operations {
317                if ov.location.module == module.ctx.module_name {
318                    for annot in &ov.annotations {
319                        if annot.name == "doc" {
320                            write_unary_operation_docs(&mut operations_file, &module, representation, ov.templates, &ov.args, &ov.ret, annot, *prefix);
321                            break;
322                        }    
323                    }
324                }
325            }    
326        }
327    }
328
329    write!(operations_file, "# Binary operations\n\n").expect("Error while writing to docs file");
330
331    for o in &module.ctx.binary_ops {
332        if let Operator::Binary { representation, operations, .. } = o {
333            for ov in operations {
334                if ov.location.module == module.ctx.module_name {
335                    for annot in &ov.annotations {
336                        if annot.name == "doc" {
337                            write_binary_operation_docs(&mut operations_file, &module, representation, ov.templates, &ov.args, &ov.ret, annot);
338                            break;
339                        }    
340                    }
341                }
342            }    
343        }
344    }
345
346    write!(operations_file, "# N-ary operations\n\n").expect("Error while writing to docs file");
347
348    for o in &module.ctx.nary_ops {
349        if let Operator::Nary { open_rep, close_rep, operations, .. } = o {
350            for ov in operations {
351                if ov.location.module == module.ctx.module_name {
352                    for annot in &ov.annotations {
353                        if annot.name == "doc" {
354                            write_nary_operation_docs(&mut operations_file, &module, &open_rep, &close_rep, ov.templates, &ov.args, &ov.ret, annot);
355                            break;
356                        }    
357                    }
358                }
359            }    
360        }
361    }
362}
363
364pub fn generate_all_class_docs(project_path: &String, module: &RynaModule) {
365    let mut classes_file = create_markdown_file(project_path, "classes.md");
366
367    for c in &module.ctx.type_templates {
368        if c.location.module == module.ctx.module_name {
369            for annot in &c.annotations {
370                if annot.name == "doc" {
371                    write_class_docs(&mut classes_file, c, annot);
372                    break;
373                }
374            }
375        }
376    }
377}
378
379pub fn generate_all_syntax_docs(project_path: &String, module: &RynaModule) {
380    let mut syntaxes_file = create_markdown_file(project_path, "syntaxes.md");
381
382    for m in &module.ctx.macros {
383        if m.location.module == module.ctx.module_name {
384            for annot in &m.annotations {
385                if annot.name == "doc" {
386                    write_syntax_docs(&mut syntaxes_file, &m.name, annot);
387                    break;
388                }
389            }
390        }
391    }
392}
393
394pub fn generate_all_interface_docs(project_path: &String, module: &RynaModule) {
395    let mut interfaces_file = create_markdown_file(project_path, "interfaces.md");
396
397    for i in &module.ctx.interfaces {
398        if i.location.module == module.ctx.module_name {
399            for annot in &i.annotations {
400                if annot.name == "doc" {
401                    write_interface_docs(&mut interfaces_file, module, i, annot);
402                    break;
403                }
404            }
405        }
406    }
407}