tex_rs/
latex.rs

1use crate::element::{Element, UDTuple};
2use crate::Convert;
3use async_std::fs::File;
4use async_std::io::WriteExt;
5use std::io::Write;
6use std::path::PathBuf;
7
8type Doc = (Class, Option<u8>, Option<String>);
9
10/// Latex struct that contains everything related to a latex document
11#[derive(Debug, Clone)]
12pub struct Latex {
13    /// Document class contains:
14    /// - Class `(\documentclass{...})`
15    /// - Fontsize `(\documentclass[..pt]{...})`
16    /// - Papersize `(\documentclass[..pt, ...]{...})`
17    pub document_class: Doc,
18    /// Metadata contains the author, title and date
19    pub metadata: Metadata,
20    /// Packages contains all the packages `(\usepackage{...})`
21    pub packages: Vec<Package>,
22    /// Document elements contains a vector of all the elements
23    pub document_elements: Vec<Element>,
24    pub maketitle: bool,
25}
26
27/// Different kind of document classes
28#[derive(Debug, Clone)]
29pub enum Class {
30    /// `\documentclass{article}`
31    Article,
32    /// `\documentclass{book}`
33    Book,
34    /// `\documentclass{report}`
35    Report,
36    /// `\documentclass{beamer}`
37    Beamer,
38}
39/// The metadata in a latex document
40#[derive(Debug, Clone)]
41pub struct Metadata {
42    /// `\author{...}`
43    pub author: String,
44    /// `\title{...}`
45    pub title: String,
46    /// `\date{...}`
47    pub date: String,
48}
49/// Package tuple struct
50#[derive(Debug, Clone)]
51pub struct Package(pub String);
52
53// Implementations //
54impl Package {
55    pub fn new(pkg: &str) -> Self {
56        Package(pkg.to_string())
57    }
58}
59
60impl Class {
61    pub fn as_class_str(&self) -> String {
62        match self {
63            Class::Article => "article".to_string(),
64            Class::Book => "book".to_string(),
65            Class::Report => "report".to_string(),
66            Class::Beamer => "beamer".to_string(),
67        }
68    }
69}
70
71impl Metadata {
72    pub fn new(title: &str, author: &str, date: &str) -> Self {
73        Self {
74            title: title.to_string(),
75            author: author.to_string(),
76            date: date.to_string(),
77        }
78    }
79}
80
81impl Default for Metadata {
82    fn default() -> Self {
83        Self {
84            author: "default author".to_string(),
85            title: "default title".to_string(),
86            date: "what day is it?".to_string(),
87        }
88    }
89}
90
91impl Latex {
92    pub fn new() -> Self {
93        Self {
94            document_class: (Class::Article, Some(11), Some("letterpaper".to_string())),
95            metadata: Metadata::default(),
96            packages: Vec::new(),
97            document_elements: Vec::new(),
98            maketitle: true,
99        }
100    }
101    /// Sets the class for Latex Document
102    pub fn set_class(&mut self, class: Class) {
103        self.document_class.0 = class;
104    }
105    /// Sets the class options for Latex Document
106    pub fn set_class_options(&mut self, font_size: u8, paper_size: &str) {
107        self.document_class.1 = Some(font_size);
108        self.document_class.2 = Some(paper_size.to_string());
109    }
110    /// Sets the metadata for the Latex Document
111    pub fn set_metadata(&mut self, meta: Metadata) {
112        self.metadata = meta;
113    }
114    /// Sets the packages for the Latex Document
115    pub fn set_packages(&mut self, packages: &Vec<Package>) {
116        self.packages = packages.to_owned();
117    }
118    /// Adds a single package to the packages
119    pub fn add_package(&mut self, package: String) {
120        self.packages.push(Package(package));
121    }
122    /// Sets the elements for the Latex Document
123    pub fn set_elements(&mut self, elements: Vec<Element>) {
124        self.document_elements = elements;
125    }
126    /// Returns a vector of UDTuple, used for write so they
127    /// can be written in the correct location
128    pub fn get_ud(&self) -> Vec<UDTuple> {
129        let mut v: Vec<UDTuple> = Vec::new();
130
131        for i in &self.document_elements {
132            match i {
133                Element::UserDefined(u) => v.push(u.evaluate()),
134                _ => v.push(("".to_owned(), "".to_owned(), "".to_owned())),
135            }
136        }
137        v
138    }
139    pub fn no_maketitle(&mut self){
140        self.maketitle = false
141    }
142    /// Normal write that uses `std`
143    pub fn write(&self, path: PathBuf) -> Result<(), std::io::Error> {
144        let ud_vec = &self.get_ud();
145        let path = path.as_path();
146        let mut file = std::fs::File::create(path)?;
147        let mut s: Vec<String> = Vec::new();
148        s.push(self.document_class.to_latex_string());
149        for i in ud_vec {
150            s.push(i.1.to_owned())
151        }
152        s.push(self.metadata.to_latex_string());
153        for i in &self.packages {
154            s.push(i.to_latex_string())
155        }
156        for i in ud_vec {
157            s.push(i.2.to_owned())
158        }
159        s.push(String::from("\\begin{document}"));
160        if self.maketitle{
161            s.push(String::from(
162                "\\maketitle\n\\pagenumbering{arabic}\n\\newpage",
163            ));
164        }
165        // By attach things should be order by priority
166        for i in &self.document_elements {
167            match i {
168                Element::Part(e) => {
169                    s.push(e.to_latex_string());
170                    for j in &e.1 {
171                        s.push(j.to_latex_string());
172                        s.push(j.loop_through())
173                    }
174                }
175                Element::Chapter(e) => {
176                    s.push(e.to_latex_string());
177                    for j in &e.1 {
178                        s.push(j.to_latex_string());
179                        s.push(j.loop_through())
180                    }
181                }
182                Element::Section(e) => {
183                    s.push(e.to_latex_string());
184                    for j in &e.1 {
185                        s.push(j.to_latex_string());
186                        s.push(j.loop_through())
187                    }
188                }
189                Element::Paragraph(e) => {
190                    s.push(e.to_latex_string());
191                    for j in e.1.as_ref().unwrap() {
192                        s.push(j.to_latex_string());
193                        s.push(j.loop_through())
194                    }
195                }
196                Element::Environment(e) => {
197                    s.push(e.to_latex_string());
198                }
199                Element::List(e) => s.push(e.to_latex_string()),
200                Element::UserDefined(e) => {
201                    let ud = e.evaluate();
202                    s.push(ud.0)
203                }
204                Element::Text(e) => s.push(e.to_latex_string()),
205                Element::Input(e) => s.push(e.to_latex_string()),
206            }
207        }
208        s.push(String::from("\\end{document}"));
209        let content = s.join("\n");
210        file.write_all(&content.as_bytes())?;
211        Ok(())
212    }
213    /// Asynchronous and parallel write using `async_std` and `rayon`
214    pub async fn async_write(&self, path: PathBuf) -> Result<(), async_std::io::Error> {
215        let ud_vec = &self.get_ud();
216        let pool = rayon::ThreadPoolBuilder::new()
217            .num_threads(4)
218            .build()
219            .unwrap();
220        let path = path.as_path();
221        let mut file = File::create(path).await?;
222        let mut s: Vec<String> = Vec::new();
223        s.push(self.document_class.to_latex_string());
224        for i in ud_vec {
225            s.push(i.1.to_owned())
226        }
227        s.push(self.metadata.to_latex_string());
228        for i in &self.packages {
229            s.push(i.to_latex_string())
230        }
231        for i in ud_vec {
232            s.push(i.2.to_owned())
233        }
234        s.push(String::from("\\begin{document}"));
235        if self.maketitle{
236            s.push(String::from(
237                "\\maketitle\n\\pagenumbering{arabic}\n\\newpage",
238            ));
239        }
240        // By attach things should be order by priority
241        for i in &self.document_elements {
242            match i {
243                Element::Part(e) => {
244                    s.push(e.to_latex_string());
245                    for j in &e.1 {
246                        s.push(j.to_latex_string());
247                        s.push(pool.install(|| j.clone().loop_through_parallel()))
248                    }
249                }
250                Element::Chapter(e) => {
251                    s.push(e.to_latex_string());
252                    for j in &e.1 {
253                        s.push(j.to_latex_string());
254                        s.push(pool.install(|| j.clone().loop_through_parallel()))
255                    }
256                }
257                Element::Section(e) => {
258                    s.push(e.to_latex_string());
259                    for j in &e.1 {
260                        s.push(j.to_latex_string());
261                        s.push(pool.install(|| j.clone().loop_through_parallel()))
262                    }
263                }
264                Element::Paragraph(e) => {
265                    s.push(e.to_latex_string());
266                    for j in e.1.as_ref().unwrap() {
267                        s.push(j.to_latex_string());
268                        s.push(pool.install(|| j.clone().loop_through_parallel()))
269                    }
270                }
271                Element::Environment(e) => {
272                    s.push(e.to_latex_string());
273                }
274                Element::List(e) => s.push(e.to_latex_string()),
275                Element::UserDefined(e) => {
276                    let ud = e.evaluate();
277                    s.push(ud.0)
278                }
279                Element::Text(e) => s.push(e.to_latex_string()),
280                Element::Input(e) => s.push(e.to_latex_string()),
281            }
282        }
283        s.push(String::from("\\end{document}"));
284        let content = s.join("\n");
285        file.write_all(&content.as_bytes()).await?;
286        Ok(())
287    }
288    /// Split write that writes a `main` and `structure` file
289    /// Uses `async_std` to do write asynchronously
290    pub async fn split_write(
291        &self,
292        main: PathBuf,
293        structure: PathBuf,
294    ) -> Result<(), async_std::io::Error> {
295        let ud_vec = &self.get_ud();
296        let main = main.as_path();
297        let structure = structure.as_path();
298
299        let mut s: Vec<String> = Vec::new();
300        let mut struct_s: Vec<String> = Vec::new();
301
302        s.push(self.document_class.to_latex_string());
303        for i in ud_vec {
304            s.push(i.1.to_owned())
305        }
306        s.push(self.metadata.to_latex_string());
307        for i in &self.packages {
308            struct_s.push(i.to_latex_string())
309        }
310        for i in ud_vec {
311            struct_s.push(i.2.to_owned())
312        }
313        s.push(String::from("\\begin{document}"));
314        if self.maketitle{
315            s.push(String::from(
316                "\\maketitle\n\\pagenumbering{arabic}\n\\newpage",
317            ));
318        }
319        // By attach things should be order by priority
320        for i in &self.document_elements {
321            match i {
322                Element::Part(e) => {
323                    s.push(e.to_latex_string());
324                    for j in &e.1 {
325                        s.push(j.to_latex_string());
326                        s.push(j.loop_through())
327                    }
328                }
329                Element::Chapter(e) => {
330                    s.push(e.to_latex_string());
331                    for j in &e.1 {
332                        s.push(j.to_latex_string());
333                        s.push(j.loop_through())
334                    }
335                }
336                Element::Section(e) => {
337                    s.push(e.to_latex_string());
338                    for j in &e.1 {
339                        s.push(j.to_latex_string());
340                        s.push(j.loop_through())
341                    }
342                }
343                Element::Paragraph(e) => {
344                    s.push(e.to_latex_string());
345                    for j in e.1.as_ref().unwrap() {
346                        s.push(j.to_latex_string());
347                        s.push(j.loop_through())
348                    }
349                }
350                Element::Environment(e) => {
351                    s.push(e.to_latex_string());
352                }
353                Element::List(e) => s.push(e.to_latex_string()),
354                Element::UserDefined(e) => {
355                    let ud = e.evaluate();
356                    s.push(ud.0)
357                }
358                Element::Text(e) => s.push(e.to_latex_string()),
359                Element::Input(e) => s.push(e.to_latex_string()),
360            }
361        }
362        s.push(String::from("\\end{document}"));
363        let content = s.join("\n");
364        let mut main_file = File::create(main).await?;
365        main_file.write_all(&content.as_bytes()).await?;
366
367        let structure_cont = struct_s.join("\n");
368        let mut struct_file = File::create(structure).await?;
369        struct_file.write_all(&structure_cont.as_bytes()).await?;
370        Ok(())
371    }
372    pub fn split_string(&self) -> (String, String) {
373        let ud_vec = &self.get_ud();
374        let mut s: Vec<String> = Vec::new();
375        let mut struct_s: Vec<String> = Vec::new();
376
377        s.push(self.document_class.to_latex_string());
378        for i in ud_vec {
379            s.push(i.1.to_owned())
380        }
381        s.push(self.metadata.to_latex_string());
382        for i in &self.packages {
383            struct_s.push(i.to_latex_string())
384        }
385        for i in ud_vec {
386            struct_s.push(i.2.to_owned())
387        }
388        s.push(String::from("\\begin{document}"));
389        s.push(String::from(
390            "\\maketitle\n\\pagenumbering{arabic}\n\\newpage",
391        ));
392        // By attach things should be order by priority
393        for i in &self.document_elements {
394            match i {
395                Element::Part(e) => {
396                    s.push(e.to_latex_string());
397                    for j in &e.1 {
398                        s.push(j.to_latex_string());
399                        s.push(j.loop_through())
400                    }
401                }
402                Element::Chapter(e) => {
403                    s.push(e.to_latex_string());
404                    for j in &e.1 {
405                        s.push(j.to_latex_string());
406                        s.push(j.loop_through())
407                    }
408                }
409                Element::Section(e) => {
410                    s.push(e.to_latex_string());
411                    for j in &e.1 {
412                        s.push(j.to_latex_string());
413                        s.push(j.loop_through())
414                    }
415                }
416                Element::Paragraph(e) => {
417                    s.push(e.to_latex_string());
418                    for j in e.1.as_ref().unwrap() {
419                        s.push(j.to_latex_string());
420                        s.push(j.loop_through())
421                    }
422                }
423                Element::Environment(e) => {
424                    s.push(e.to_latex_string());
425                }
426                Element::List(e) => s.push(e.to_latex_string()),
427                Element::UserDefined(e) => {
428                    let ud = e.evaluate();
429                    s.push(ud.0)
430                }
431                Element::Text(e) => s.push(e.to_latex_string()),
432                Element::Input(e) => s.push(e.to_latex_string()),
433            }
434        }
435        s.push(String::from("\\end{document}"));
436        let content = s.join("\n");
437        let structure_cont = struct_s.join("\n");
438        (content, structure_cont)
439    }
440}
441
442// Trait Implementations
443impl Convert for Doc {
444    fn to_latex_string(&self) -> String {
445        let fs = match self.1.clone() {
446            Some(f) => f.to_string(),
447            None => "11".to_owned(),
448        };
449        let paper = match self.2.clone() {
450            Some(a) => a,
451            None => "letterpaper".to_owned(),
452        };
453        format!(
454            "\\documentclass[{}pt, {}]{{{}}}",
455            &fs,
456            &paper,
457            &self.0.as_class_str()
458        )
459    }
460}
461
462impl Convert for Metadata {
463    fn to_latex_string(&self) -> String {
464        let v = vec![
465            format!("\\author{{{}}}", &self.author),
466            format!("\\title{{{}}}", &self.title),
467            format!("\\date{{{}}}", &self.date),
468        ];
469        v.join("\n")
470    }
471}
472
473impl Convert for Package {
474    fn to_latex_string(&self) -> String {
475        format!("\\usepackage{{{}}}", &self.0)
476    }
477}