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!("<{}>", (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!("<{}>", (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!("<{}>", (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!("<{}>", (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!("<{}>", (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!("<{}>", 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}