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
/// Grabs imported data.
mod handle_imports;
/// Literate programming support - extracts relevant code from Markdown file.
pub mod markdown;

use handle_imports::retrieve_imports;
pub use markdown::{extract_code, CodeExtraction};
use path_abs::{PathAbs, PathInfo};
use std::env;
use std::fs::read_to_string;
use std::io::{Error, ErrorKind};
use std::path::Path;

/// All supported input filename extensions.
pub const SUPPORTED_EXTENSIONS: &[&str] = &["md"];

/// Filename for ZAMM override file.
pub const ZAMM_OVERRIDE_NAME: &str = "zamm_override.md";

/// Parse output, including the original markdown text.
pub struct ParseOutput {
    /// The original filename.
    pub filename: String,
    /// The original markdown text.
    pub markdown: String,
    /// Code extractions from the original markdown.
    pub extractions: CodeExtraction,
}

/// Find the right input file.
pub fn find_file(specified_file: Option<&str>) -> Result<PathAbs, Error> {
    match specified_file {
        Some(filename) => {
            let path = PathAbs::new(Path::new(&filename))?;
            let path_str = path.as_path().to_str().unwrap();
            if path.exists() {
                println!("Using specified input file at {}", path_str);
                Ok(path)
            } else {
                Err(Error::new(
                    ErrorKind::NotFound,
                    format!("Specified input file was not found at {}", path_str),
                ))
            }
        }
        None => {
            for extension in SUPPORTED_EXTENSIONS {
                let path = PathAbs::new(Path::new(format!("yin.{}", extension).as_str()))?;
                if path.exists() {
                    println!(
                        "Using default input file at {}",
                        path.as_path().to_str().unwrap()
                    );
                    return Ok(path);
                }
            }
            let current_dir = env::current_dir()?;
            let current_dir_path = current_dir.to_str().unwrap();
            Err(Error::new(
                ErrorKind::NotFound,
                format!(
                    "No input file was specified, and no default inputs were found in the current \
                    directory of {}",
                    current_dir_path
                ),
            ))
        }
    }
}

fn retrieve_override() -> Result<Option<String>, Error> {
    let override_path = PathAbs::new(Path::new(ZAMM_OVERRIDE_NAME))?;
    if override_path.exists() {
        let override_content = read_to_string(&override_path)?;
        Ok(Some(override_content))
    } else {
        Ok(None)
    }
}

/// Parse the given input file.
pub fn parse_input(found_input: PathAbs) -> Result<ParseOutput, Error> {
    println!(
        "cargo:rerun-if-changed={}",
        found_input.as_os_str().to_str().unwrap()
    );
    let contents = read_to_string(&found_input)?;
    let extension = found_input
        .extension()
        .map(|e| e.to_str().unwrap())
        .unwrap_or("");
    match extension {
        "md" => {
            let mut initial_extraction = extract_code(&contents);
            let override_content: String = retrieve_override()?.unwrap_or_default();
            let override_extraction = extract_code(&override_content);

            initial_extraction.rust += &override_extraction.rust;
            if !override_extraction.imports.is_empty() {
                initial_extraction.imports = override_extraction.imports;
            }
            if !override_extraction.toml.is_empty() {
                initial_extraction.toml = override_extraction.toml;
            }

            Ok(ParseOutput {
                filename: found_input
                    .file_name()
                    .unwrap()
                    .to_str()
                    .unwrap()
                    .to_owned(),
                markdown: contents,
                extractions: retrieve_imports(&initial_extraction)?,
            })
        }
        _ => Err(Error::new(
            ErrorKind::NotFound,
            format!(
                "The extension \"{}\" is not recognized. Please see the help message for \
                    recognized extension types.",
                extension
            ),
        )),
    }
}