writing 0.2.1

Writing, is a simple tool for code-document sync.
Documentation
use pest::iterators::Pair;
use pest::Parser;

use crate::parser::ast::{CodeBlock, CodeDep, CodeFunc, CodeSection, CodeSource, Writing};

#[derive(Parser)]
#[grammar = "parser/writing.pest"]
struct WritingParser;

pub fn parse(text: &str) -> Writing {
    let pairs = WritingParser::parse(Rule::start, text).unwrap_or_else(|e| panic!("{}", e));

    let mut writing = Writing::new();
    for pair in pairs {
        for decl in pair.into_inner() {
            match decl.as_rule() {
                Rule::doc_code_decl => {
                    writing.code_docs.push(parse_doc_decl(decl));
                }
                Rule::code_dep_decl => {
                    writing.code_deps.push(parse_deps_decl(decl));
                }
                Rule::code_section_decl => {
                    writing.code_sections.push(parse_code_sections(decl));
                }
                Rule::code_func_decl => {
                    writing.code_funcs.push(parse_code_func(decl));
                }
                _ => {
                    println!("Rule:    {:?}", decl.as_rule());
                    println!("Span:    {:?}", decl.as_span());
                    println!("Text:    {}", decl.as_str());
                },
            }
        }
    }

    writing
}

fn parse_deps_decl(decl: Pair<Rule>) -> CodeDep {
    let mut code_dep = CodeDep::new();
    for pair in decl.into_inner() {
        match pair.as_rule() {
            Rule::artifact_id => {
                code_dep.artifact_id = String::from(pair.as_str());
            }
            Rule::group_id => {
                code_dep.group_id = String::from(pair.as_str());
            }
            Rule::version => {
                code_dep.version = String::from(pair.as_str());
            }
            _ => {

            }
        }
    }
    code_dep
}

fn parse_code_sections(decl: Pair<Rule>) -> CodeSection {
    let mut section = CodeSection::new();
    let mut block = CodeBlock::new();
    for pair in decl.into_inner() {
        match pair.as_rule() {
            Rule::string_literal => {
                block.file = parse_string(pair.as_str());
            }
            Rule::section_name => {
                block.name = parse_string(pair.as_str());
            }
            _ => {

            }
        }
    }
    section.blocks.push(block);
    section
}


fn parse_code_func(decl: Pair<Rule>) -> CodeFunc {
    let mut func = CodeFunc::new();
    for pair in decl.into_inner() {
        match pair.as_rule() {
            Rule::string_literal => {
                func.file = parse_string(pair.as_str());
            }
            Rule::func_name => {
                func.funcs.push(parse_string(pair.as_str()))
            }
            _ => {}
        }
    }
    func
}

fn parse_doc_decl(decl: Pair<Rule>) -> CodeSource {
    let mut code_doc = CodeSource::new();
    for pair in decl.into_inner() {
        match pair.as_rule() {
            Rule::string_literal => {
                code_doc.file = parse_string(pair.as_str());
            }
            Rule::start_line => {
                code_doc.start_line = pair.as_str()
                    .parse()
                    .expect("convert int error");
            }
            Rule::end_line => {
                code_doc.end_line = pair.as_str()
                    .parse()
                    .expect("convert int error");
            }
            _ => {

            }
        }
    }

    code_doc
}

pub fn parse_string(input: &str) -> String {
    match input.chars().next().unwrap() {
        '"' => input.replace('"', ""),
        '\'' => input.replace('\'', ""),
        _ => unreachable!("output: {:?}", input),
    }
}


#[cfg(test)]
mod tests {
    use crate::parser::parse;

    #[test]
    fn should_parse_doc_code() {
        let writing = parse("doc-code: file(\"src/lib.rs\").line()[2, 5]");
        assert_eq!(writing.code_docs.len(), 1);

        let doc = writing.code_docs[0].clone();

        assert_eq!("src/lib.rs", doc.file);
        assert_eq!(2, doc.start_line);
        assert_eq!(5, doc.end_line);
    }

    #[test]
    fn should_parse_section() {
        let writing = parse("doc-section: file(\"src/lib.rs\").section(\"section1\")");
        assert_eq!(writing.code_sections.len(), 1);
    }

    #[test]
    fn should_parse_doc_dep() {
        let writing = parse("code-dep: colored;version=1.8.0");
        assert_eq!(writing.code_deps.len(), 1);
        let dep = &writing.code_deps[0];

        assert_eq!("1.8.0", dep.version);
        assert_eq!("colored", dep.artifact_id);
    }

    #[test]
    fn should_parse_function() {
        let writing = parse("doc-func: file(\"src/lib.rs\").func()[\"it_works\"]");
        assert_eq!(writing.code_funcs.len(), 1);
        assert_eq!(writing.code_funcs[0].file, "src/lib.rs");

        let writing = parse("doc-func: file(\"src/lib.rs\").func()[\"it_works\", \"should_parse_function\"]");
        assert_eq!(writing.code_funcs[0].funcs.len(), 2);
    }
}