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
extern crate pest;
#[macro_use]
extern crate pest_derive;

use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};

use thiserror::Error;

pub use parser::*;

use crate::code_reader::CodeReader;

pub mod parser;
pub mod code_reader;

#[derive(Error, Debug)]
pub enum WritingError {
    #[error("io error: `{0}` ")]
    IOError(String),
    #[error("read file error: `{0}` ")]
    ReadFileError(String),
    #[error("unknown data store error")]
    Unknown,
}

pub struct Writing {}

impl Writing {
    pub fn process_file<P: AsRef<Path>>(path: P) -> Result<String, WritingError> {
        let path = path.as_ref().to_path_buf();

        if let Err(err) = Writing::pre_process_file(&path) {
            return Err(err);
        };

        let result = Writing::write_it(path).join("\n");

        Ok(result)
    }

    fn write_it(path: PathBuf) -> Vec<String> {
        let file = File::open(path).expect("cannot open file");
        let reader = BufReader::new(file);
        let mut is_lang = false;

        let mut results = vec![];
        for res in reader.lines() {
            let line = res.expect("cannot parse line");
            if line.starts_with("```") {
                is_lang = !is_lang
            }

            // todo: add remove backspace & tab, before check??
            if is_lang && line.starts_with("// doc-") {
                let writing = parser::parse(line.replace("//", "").as_str());

                if writing.code_docs.len() > 0 {
                    results.append(&mut CodeReader::read_doc_code(&writing.code_docs[0]));
                } else if writing.code_sections.len() > 0 {
                    results.append(&mut CodeReader::read_doc_section(&writing.code_sections[0]));
                } else if writing.code_funcs.len() > 0 {
                    results.append(&mut CodeReader::read_code_func(&writing.code_funcs[0]));
                } else {
                    results.push(String::from(line));
                };
            } else {
                results.push(String::from(line));
            }
        }

        results
    }

// doc-start: section1
    fn pre_process_file(path: &PathBuf) -> Result<(), WritingError> {
        if path.is_dir() {
            return Err(WritingError::IOError(format!("path: {:?} is a dir", path)));
        }

        if let Err(e) = fs::read(path) {
            return Err(WritingError::IOError(format!("read file error: {:?}", e)));
        }

        Ok(())
    }
// doc-end: section1
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use crate::Writing;

    #[test]
    fn should_convert_doc_code() {
        let path = PathBuf::from("samples").join("doc-code.md");
        let content = Writing::process_file(path);

        assert_eq!("```writing
extern crate pest;
#[macro_use]
extern crate pest_derive;

use std::fs;
```", content.expect(""));
    }

    #[test]
    fn should_convert_doc_section() {
        let path = PathBuf::from("samples").join("doc-section.md");
        let content = Writing::process_file(path);

        assert_eq!("```writing
    fn pre_process_file(path: &PathBuf) -> Result<(), WritingError> {
        if path.is_dir() {
            return Err(WritingError::IOError(format!(\"path: {:?} is a dir\", path)));
        }

        if let Err(e) = fs::read(path) {
            return Err(WritingError::IOError(format!(\"read file error: {:?}\", e)));
        }

        Ok(())
    }
```", content.expect(""));
    }
}