1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*!
ruSTLa is a Rust implementation of the rSTLa or resTructuredText → LaTeX transpiler.
ruSTLa is a master's thesis project of Santtu Söderholm,
and is intended to function as the counterpart to the LarST, or LaTeX → reStructuredText
parser, written by Tomi Janhunen.

Copyright © 2020 Santtu Söderholm
*/
#![allow(dead_code, unused_variables)]
mod rustla_options;
pub mod parser;
use parser::Parser;
use parser::state_machine::State;
pub mod doctree;
use doctree::DocTree;
pub mod common;
mod utf8_to_latex;

use std::io::BufRead;

/// Program starting point.
/// Reads the input string, feeds it to the parser and prints the generated doctree.
pub fn run() -> Result<(), MainError> {
    copyright();

    let args: Vec<String> = std::env::args().collect();
    let args_len = args.len();

    let rustla_options = crate::rustla_options::ruSTLaOptions::new(&args);

    let mut src_lines = Vec::new();

    // Populate src_lines and generate a path buffer to the source
    // (empty if read from stdin)
    let path: std::path::PathBuf = if let Some(arg) = args.last() {
        if args_len == 1 {
            // Handle no arguments first
            let stdin = std::io::stdin();
            for line in stdin.lock().lines() {
                match line {
                    Ok(line) => src_lines.push(line),
                    Err(e) => {
                        return Err(MainError::InputError(format!(
                            "Error when reading stdin: {}",
                            e
                        )))
                    }
                }
            }

            std::path::PathBuf::new()
        } else if let Ok(pathbuf) = std::fs::canonicalize(arg) {
            let line_iter = match crate::common::read_path_lines(&pathbuf) {
                Ok(lines) => lines,
                Err(e) => {
                    return Err(MainError::PathError(format!(
                        "Could not split file into lines: {}",
                        e
                    )))
                }
            };
            for line in line_iter {
                match line {
                    Ok(line) => src_lines.push(line),
                    Err(e) => {
                        return Err(MainError::InputError(String::from(
                            "Could not construct a line vector from input...",
                        )))
                    }
                }
            }

            pathbuf
        } else {
            let stdin = std::io::stdin();
            for line in stdin.lock().lines() {
                match line {
                    Ok(line) => src_lines.push(line),
                    Err(e) => {
                        return Err(MainError::InputError(format!(
                            "Error when reading stdin: {}",
                            e
                        )))
                    }
                }
            }
            std::path::PathBuf::new()
        }
    } else {
        unreachable!("No arguments, not even the program itself? Computer says no...")
    };

    // Enter parser here...

    let mut doctree = DocTree::new(path);
    let mut parser = Parser::new(src_lines, doctree, 0, 0, State::Body, 0);

    use common::ParsingResult;

    doctree = match parser.parse() {
        ParsingResult::EOF { doctree, .. } | ParsingResult::EmptyStateStack { doctree, .. } => {
            doctree
        }
        ParsingResult::Failure { message, doctree } => {
            eprintln!("Parsing error: {}", message);
            doctree
        }
    };

    doctree = doctree.perform_restructuredtext_transforms();
    doctree.write_to_larst(&rustla_options);

    Ok(())
}

/// Prints out copyright information of ruSTLa
fn copyright() {
    /// Version retrieved from cargo.toml
    const VERSION: &'static str = env!("CARGO_PKG_VERSION");
    /// Author name retrieved from cargo.toml
    const AUTHOR_NAME: &'static str = env!("AUTHOR_NAME");
    /// Author email retrieved from cargo.toml
    // const AUTHOR_EMAIL: &'static str = env!("AUTHOR_EMAIL");
    /// Year retrieved from cargo.toml
    const AUTHOR_YEAR: &'static str = env!("AUTHOR_YEAR");
    eprintln!("\nThis is ruSTLa, version {}", VERSION);
    eprintln!("Copyright © {} {}\n", AUTHOR_YEAR, AUTHOR_NAME);
}

/// A function that prints the usage instructions for ruSTLa
fn usage() {
    println!("Instructions");
    println!("============");
    println!("In order to transpile a document,");
    println!("point ruSTLa to an rST file with");
    println!("\n  $ rustla path/to/file.rst\n");
    println!("Capabilities to transpile an entire");
    println!("toctree will be added later.");
}

#[derive(Debug)]
/// The ways in which the `main` function can fail.
pub enum MainError {
    /// The file handed in for parsing wasn't valid.
    PathError(String),
    /// Something was amiss with the input, so it couldn't be read.
    InputError(String),
    /// The parser returned in an error state.
    ParseError(String),
    /// The doctree generated by the parser couldn't be transformed to object code.
    PrintError(String),
    /// Something was off with the command line arguments.
    ArgumentError(String),
}