rustla/
lib.rs

1/*!
2ruSTLa is a Rust implementation of the rSTLa or resTructuredText → LaTeX transpiler.
3ruSTLa is a master's thesis project of Santtu Söderholm,
4and is intended to function as the counterpart to the LarST, or LaTeX → reStructuredText
5parser, written by Tomi Janhunen.
6
7Copyright © 2020 Santtu Söderholm
8*/
9#![allow(dead_code, unused_variables)]
10mod rustla_options;
11pub mod parser;
12use parser::Parser;
13use parser::state_machine::State;
14pub mod doctree;
15use doctree::DocTree;
16pub mod common;
17mod utf8_to_latex;
18
19use std::io::BufRead;
20
21/// Program starting point.
22/// Reads the input string, feeds it to the parser and prints the generated doctree.
23pub fn run() -> Result<(), MainError> {
24    copyright();
25
26    let args: Vec<String> = std::env::args().collect();
27    let args_len = args.len();
28
29    let rustla_options = crate::rustla_options::ruSTLaOptions::new(&args);
30
31    let mut src_lines = Vec::new();
32
33    // Populate src_lines and generate a path buffer to the source
34    // (empty if read from stdin)
35    let path: std::path::PathBuf = if let Some(arg) = args.last() {
36        if args_len == 1 {
37            // Handle no arguments first
38            let stdin = std::io::stdin();
39            for line in stdin.lock().lines() {
40                match line {
41                    Ok(line) => src_lines.push(line),
42                    Err(e) => {
43                        return Err(MainError::InputError(format!(
44                            "Error when reading stdin: {}",
45                            e
46                        )))
47                    }
48                }
49            }
50
51            std::path::PathBuf::new()
52        } else if let Ok(pathbuf) = std::fs::canonicalize(arg) {
53            let line_iter = match crate::common::read_path_lines(&pathbuf) {
54                Ok(lines) => lines,
55                Err(e) => {
56                    return Err(MainError::PathError(format!(
57                        "Could not split file into lines: {}",
58                        e
59                    )))
60                }
61            };
62            for line in line_iter {
63                match line {
64                    Ok(line) => src_lines.push(line),
65                    Err(e) => {
66                        return Err(MainError::InputError(String::from(
67                            "Could not construct a line vector from input...",
68                        )))
69                    }
70                }
71            }
72
73            pathbuf
74        } else {
75            let stdin = std::io::stdin();
76            for line in stdin.lock().lines() {
77                match line {
78                    Ok(line) => src_lines.push(line),
79                    Err(e) => {
80                        return Err(MainError::InputError(format!(
81                            "Error when reading stdin: {}",
82                            e
83                        )))
84                    }
85                }
86            }
87            std::path::PathBuf::new()
88        }
89    } else {
90        unreachable!("No arguments, not even the program itself? Computer says no...")
91    };
92
93    // Enter parser here...
94
95    let mut doctree = DocTree::new(path);
96    let mut parser = Parser::new(src_lines, doctree, 0, 0, State::Body, 0);
97
98    use common::ParsingResult;
99
100    doctree = match parser.parse() {
101        ParsingResult::EOF { doctree, .. } | ParsingResult::EmptyStateStack { doctree, .. } => {
102            doctree
103        }
104        ParsingResult::Failure { message, doctree } => {
105            eprintln!("Parsing error: {}", message);
106            doctree
107        }
108    };
109
110    doctree = doctree.perform_restructuredtext_transforms();
111    doctree.write_to_larst(&rustla_options);
112
113    Ok(())
114}
115
116/// Prints out copyright information of ruSTLa
117fn copyright() {
118    /// Version retrieved from cargo.toml
119    const VERSION: &'static str = env!("CARGO_PKG_VERSION");
120    /// Author name retrieved from cargo.toml
121    const AUTHOR_NAME: &'static str = env!("AUTHOR_NAME");
122    /// Author email retrieved from cargo.toml
123    // const AUTHOR_EMAIL: &'static str = env!("AUTHOR_EMAIL");
124    /// Year retrieved from cargo.toml
125    const AUTHOR_YEAR: &'static str = env!("AUTHOR_YEAR");
126    eprintln!("\nThis is ruSTLa, version {}", VERSION);
127    eprintln!("Copyright © {} {}\n", AUTHOR_YEAR, AUTHOR_NAME);
128}
129
130/// A function that prints the usage instructions for ruSTLa
131fn usage() {
132    println!("Instructions");
133    println!("============");
134    println!("In order to transpile a document,");
135    println!("point ruSTLa to an rST file with");
136    println!("\n  $ rustla path/to/file.rst\n");
137    println!("Capabilities to transpile an entire");
138    println!("toctree will be added later.");
139}
140
141#[derive(Debug)]
142/// The ways in which the `main` function can fail.
143pub enum MainError {
144    /// The file handed in for parsing wasn't valid.
145    PathError(String),
146    /// Something was amiss with the input, so it couldn't be read.
147    InputError(String),
148    /// The parser returned in an error state.
149    ParseError(String),
150    /// The doctree generated by the parser couldn't be transformed to object code.
151    PrintError(String),
152    /// Something was off with the command line arguments.
153    ArgumentError(String),
154}