rust_texas/document/
mod.rs

1use std::collections::HashMap;
2
3use crate::prelude::*;
4
5mod doc_class;
6mod metadata;
7mod package;
8
9pub use doc_class::*;
10pub use metadata::*;
11pub use package::*;
12
13/// The king of the land. The `Document` type is where you start.
14/// Has macro support.
15/// Also contains a restriction on the latex commands you use - you can't use one without
16/// declaring it. Atypical of this crate, this particular feature prevents a latex error.
17#[derive(Debug, Clone)]
18pub struct Document {
19    // document_class: DocumentClass,
20    packages: Vec<Package>,
21    pub metadata: Metadata,
22    components: Vec<Component>,
23    commands: HashMap<String, Command>,
24    // labels: HashSet<&'a Label>,
25    img: bool,
26    href: bool,
27    scratch: bool,
28    graphics_path: Vec<String>,
29}
30impl AsLatex for Document {
31    fn to_string(&self) -> String {
32        let dc = self.metadata.class.to_string();
33        let pkgs = self
34            .packages
35            .iter()
36            .map(|x| x.to_string())
37            .collect::<String>();
38        let md = if !self.scratch {
39            self.metadata.to_string()
40        } else {
41            "\n".to_string()
42        };
43        let beamer_init_frames: String = if self.metadata.class.typ == DocumentClassType::Beamer {
44            // Warning: Unused result. Again, cannot n-choose-2 Component Variants.
45            let title_frame = Frame::with_components("", vec![textchunk!(r"\titlepage", "normal")]);
46
47            let mut toc = "".to_string();
48            if self.metadata.tableofcontents {
49                toc = Frame::with_components("", vec![textchunk!(r"\tableofcontents", "normal")])
50                    .to_string();
51            };
52
53            format!("{}\n{}\n", title_frame.to_string(), toc)
54        } else {
55            "\n".to_string()
56        };
57        let body = beamer_init_frames
58            + &self
59                .components
60                .iter()
61                .map(|x| x.to_string())
62                .collect::<String>();
63
64        let cmd = self
65            .commands
66            .iter()
67            .map(|x| format!("{} \n", x.1.declare()))
68            .collect::<String>();
69
70        let gpath = if self.graphics_path.len() > 0 {
71            format!(
72                "\\graphicspath{{{}}} \n",
73                self.graphics_path
74                    .iter()
75                    .map(|x| format!("{{{}}}, ", x))
76                    .collect::<String>()
77            )
78        } else {
79            "".to_string()
80        };
81        format!(
82            "{}\n{}\n{}\n{}\\begin{{document}}\n{}\n{}\n\\end{{document}}",
83            dc, pkgs, cmd, gpath, md, body
84        )
85    }
86}
87impl Document {
88    pub fn new(class: DocumentClass) -> Self {
89        let mut out = Self {
90            // document_class: class,
91            packages: vec![],
92            metadata: Metadata::new(class, "title", &["author"]),
93            components: vec![],
94            commands: HashMap::new(),
95            // labels: HashSet::new(),
96            img: true,
97            href: true,
98            scratch: false,
99            graphics_path: vec![".".to_string()],
100        };
101        out.new_package(package!("graphicx"));
102        out.new_package(package!("hyperref"));
103        out
104    }
105
106    pub fn get_command(&self, cmd: &str) -> TexResult<Command> {
107        match self.commands.get(cmd) {
108            Some(s) => Ok(s.clone()),
109            None => Err(TexError::Undefined.into()),
110        }
111    }
112
113    pub fn scratch(&mut self) {
114        self.scratch = true;
115    }
116
117    pub fn new_command(&mut self, c: Command) {
118        self.commands.insert(c.name.clone(), c);
119    }
120
121    pub fn new_component(&mut self, new: Component) {
122        self.components.push(new);
123    }
124
125    pub fn set_md(&mut self, title: &str, author: &[&str]) {
126        self.metadata.title = title.to_string();
127        self.metadata.author = author.iter().map(|x| x.to_string()).collect();
128    }
129
130    pub fn new_package(&mut self, new: Package) {
131        self.packages.push(new);
132    }
133
134    pub fn enable_graphicx(&mut self, path: &str) {
135        self.img = true;
136        self.new_package(package!("graphicx"));
137        self.graphics_path = vec![path.to_string()];
138    }
139
140    pub fn disable_graphicx(&mut self) {
141        self.img = false;
142        self.packages.retain(|x| x.name != "graphicx");
143        self.graphics_path = vec![];
144    }
145
146    pub fn enable_hyperref(&mut self) {
147        self.href = true;
148        self.new_package(package!("hyperref"));
149    }
150
151    pub fn disable_hyperref(&mut self) {
152        self.href = false;
153        self.packages.retain(|x| x.name != "hyperref");
154    }
155
156    pub fn push_gpath(&mut self, path: &str) {
157        self.graphics_path.push(path.to_string());
158    }
159}
160impl Opt for Document {
161    fn add_option(&mut self, opt: &str) {
162        self.metadata.class.add_option(opt);
163    }
164}
165impl Populate for Document {
166    fn attach(&mut self, other: Component) -> TexResult<&mut Self> {
167        self.new_component(other);
168        Ok(self)
169    }
170
171    fn attach_iter<I: Iterator<Item = Component>>(&mut self, other: I) -> TexResult<&mut Self> {
172        for i in other {
173            self.attach(i)?;
174        }
175        Ok(self)
176    }
177
178    fn attach_vec(&mut self, other: Vec<Component>) -> TexResult<&mut Self> {
179        self.attach_iter(other.into_iter())
180    }
181}