aboutsummaryrefslogtreecommitdiffstats
path: root/src/parser.rs
blob: 69ead450b78ccda77f931f3f1f48e2e000161fa6 (plain)
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
pub mod token;
#[cfg(test)]
pub mod tests;

mod pest_rst {
    use pest_derive::Parser;
    
    #[derive(Parser)]
    #[grammar = "rst.pest"]
    pub struct RstParser;
}
use self::pest_rst::Rule;

use std::io::Write;

use url::Url;
use failure::Error;
use failure_derive::Fail;
use pest::Parser;

use crate::document_tree::{
    HasChildren,
    elements::{
        Document,
        Title,
        Paragraph,
        Target,
        Attention, Hint, Note, Caution, Danger, Error as ErrorEl, Important, Tip, Warning
    },
    element_categories::{
        StructuralSubElement,
        SubStructure,
        BodyElement,
    },
    attribute_types::ID,
    extra_attributes,
};


#[derive(Debug, Fail)]
enum ConversionError {
    #[fail(display = "unknown rule: {:?}", rule)]
    UnknownRuleError {
        rule: Rule,
    },
}


fn convert_ssubel(pair: pest::iterators::Pair<Rule>) -> Result<StructuralSubElement, Error> {
    // TODO: This is just a proof of concep. Keep closely to DTD in final version!
    match pair.as_rule() {
        Rule::title => Ok(convert_title(pair).into()),
        Rule::paragraph => Ok(to_ssub(Paragraph::with_children(vec![pair.as_str().into()]))),
        Rule::target => Ok(to_ssub(convert_target(pair)?)),
        Rule::admonition_gen => Ok(to_ssub(convert_admonition_gen(pair)?)),
        rule => Err(ConversionError::UnknownRuleError { rule }.into()),
    }
}


fn to_ssub<E>(elem: E) -> StructuralSubElement where E: Into<BodyElement> {
    let belm: BodyElement = elem.into();
    let sstruc: SubStructure = belm.into();
    sstruc.into()
}


fn convert_title(pair: pest::iterators::Pair<pest_rst::Rule>) -> Title {
    let mut title: Option<&str> = None;
    let mut _adornment_char: Option<char> = None;
    for p in pair.into_inner() {
        match p.as_rule() {
            Rule::line => title = Some(p.as_str()),
            Rule::adornments => _adornment_char = Some(p.as_str().chars().next().expect("Empty adornment?")),
            rule => panic!("Unexpected rule in title: {:?}", rule),
        };
    }
    // TODO adornment char
    Title::with_children(vec![
        title.expect("No text in title").into()
    ])
}

fn convert_target(pair: pest::iterators::Pair<pest_rst::Rule>) -> Result<Target, Error> {
    let mut attrs = extra_attributes::Target {
        anonymous: false,
        ..Default::default()
    };
    for p in pair.into_inner() {
        match p.as_rule() {
            // TODO: or is it refnames?
            Rule::target_name_uq | Rule::target_name_qu => attrs.refid = Some(ID(p.as_str().to_owned())),
            Rule::link_target => attrs.refuri = Some(Url::parse(p.as_str())?),
            rule => panic!("Unexpected rule in target: {:?}", rule),
        }
    }
    Ok(Target::new(Default::default(), attrs))
}

fn convert_admonition_gen(pair: pest::iterators::Pair<pest_rst::Rule>) -> Result<BodyElement, Error> {
    let mut iter = pair.into_inner();
    let typ = iter.next().unwrap().as_str();
    // TODO: in reality it contains body elements.
    let children: Vec<BodyElement> = iter.map(|p| Paragraph::with_children(vec![p.as_str().into()]).into()).collect();
    Ok(match typ {
        "attention" => Attention::with_children(children).into(),
        "hint"      =>      Hint::with_children(children).into(),
        "note"      =>      Note::with_children(children).into(),
        "caution"   =>   Caution::with_children(children).into(),
        "danger"    =>    Danger::with_children(children).into(),
        "error"     =>   ErrorEl::with_children(children).into(),
        "important" => Important::with_children(children).into(),
        "tip"       =>       Tip::with_children(children).into(),
        "warning"   =>   Warning::with_children(children).into(),
        typ         => panic!("Unknown admontion type {}!", typ),
    })
}


/// tokens to Document tree. resolves sections, but not references
pub fn parse(source: &str) -> Result<Document, Error> {
    let pairs = pest_rst::RstParser::parse(pest_rst::Rule::document, source)?;
    let structural_elems = pairs.map(convert_ssubel).collect::<Result<_, _>>()?;
    Ok(Document::with_children(structural_elems))
}


/// only until we can serialize DocumentTrees
pub fn serialize_json<W>(source: &str, stream: W) -> Result<(), Error> where W: Write {
    let parsed = parse(source)?;
    serde_json::to_writer(stream, &parsed)?;
    Ok(())
}