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
155
156
157
use nom::{
    branch::alt,
    bytes::complete::{is_not, take_till1},
    character::complete::{char, multispace0},
    combinator::complete,
    multi::{many0, many1},
    sequence::delimited,
    IResult,
};

use alloc::collections::BTreeMap;
use alloc::vec;
use alloc::vec::Vec;
use core::result::Result;

use crate::elements::*;
use crate::error::Error;

enum ParsedValue<'a> {
    Subentry(&'a str),
    Value(&'a str),
}

fn open_entry(input: &str) -> IResult<&str, char> {
    let (input, _) = multispace0(input)?;
    let (input, open) = char('(')(input)?;
    Ok((input, open))
}

fn close_entry(input: &str) -> IResult<&str, char> {
    let (input, _) = multispace0(input)?;
    let (input, close) = char(')')(input)?;
    Ok((input, close))
}

fn subentry_contents(input: &str) -> IResult<&str, &str> {
    delimited(char('('), is_not(")"), char(')'))(input)
}

fn quoted_string(input: &str) -> IResult<&str, &str> {
    delimited(char('"'), is_not("\""), char('"'))(input)
}

fn unquoted_string(input: &str) -> IResult<&str, &str> {
    take_till1(|c| c == ' ' || c == '\n')(input)
}

fn string_key(input: &str) -> IResult<&str, &str> {
    let (input, _) = multispace0(input)?;
    let (input, key) = take_till1(|c| c == ' ' || c == '\n' || c == '"')(input)?;
    Ok((input, key))
}

fn parse_string_value(input: &str) -> IResult<&str, (&str, ParsedValue)> {
    let (input, _) = multispace0(input)?;
    let (input, key) = string_key(input)?;
    let (input, _) = char(' ')(input)?;
    let (input, value) = alt((quoted_string, unquoted_string))(input)?;
    Ok((input, (key, ParsedValue::Value(value))))
}

fn parse_sub_entry(input: &str) -> IResult<&str, (&str, ParsedValue)> {
    let (input, _) = multispace0(input)?;
    let (input, key) = string_key(input)?;
    let (input, _) = char(' ')(input)?;
    let (input, contents) = subentry_contents(input)?;
    Ok((input, (key, ParsedValue::Subentry(contents))))
}

fn parse_sub_entry_data<'a>(input: &'a str) -> IResult<&'a str, SubEntry<'a>> {
    let (input, _) = multispace0(input)?;
    let (input, keys) = complete(many1(parse_string_value))(input)?;

    let mut map = BTreeMap::new();
    for (key, value) in keys {
        match value {
            ParsedValue::Value(value) => {
                map.insert(key, value);
            }
            _ => unreachable!(),
        }
    }
    Ok((input, SubEntry { keys: map }))
}

/// Parse multiple ListInfo entries as a document.
pub fn parse_document<'a>(input: &'a str) -> Result<DatDocument<'a>, Error> {
    let (_, fragments) = complete(many1(parse_fragment_internal))(input)?;
    let mut document: BTreeMap<&'a str, Vec<EntryFragment<'a>>> = BTreeMap::new();
    for (key, entry) in fragments {
        if let Some(existing) = document.get_mut(key) {
            existing.push(entry);
        } else {
            document.insert(key, vec![entry]);
        }
    }
    Ok(DatDocument { document })
}

/// Parse a single ListInfo entry, returning it's type and the entry.
pub fn parse_fragment<'a, 'b>(input: &'a str) -> Result<(&'a str, EntryFragment<'a>), Error> {
    let (_, fragment) = parse_fragment_internal(input)?;
    Ok(fragment)
}

fn parse_fragment_internal<'a, 'b>(
    input: &'a str,
) -> IResult<&'a str, (&'a str, EntryFragment<'a>)> {
    let (input, _) = multispace0(input)?;
    let (input, entry_key) = string_key(input)?;
    let (input, _) = open_entry(input)?;

    let mut map = BTreeMap::new();

    let (input, keys) = many0(alt((parse_sub_entry, parse_string_value)))(input)?;
    for (key, value) in keys {
        match value {
            ParsedValue::Subentry(value) => {
                if let Ok((_, subentry)) = parse_sub_entry_data(value) {
                    if let Some(node) = map.remove(key) {
                        match node {
                            EntryNode::Unique(prev) => {
                                map.insert(
                                    key,
                                    EntryNode::Many(vec![prev, EntryData::SubEntry(subentry)]),
                                );
                            }
                            EntryNode::Many(mut prevs) => {
                                prevs.push(EntryData::SubEntry(subentry));
                                map.insert(key, EntryNode::Many(prevs));
                            }
                        }
                    } else {
                        map.insert(key, EntryNode::Unique(EntryData::SubEntry(subentry)));
                    }
                }
            }
            ParsedValue::Value(value) => {
                if let Some(node) = map.remove(key) {
                    match node {
                        EntryNode::Unique(prev) => {
                            map.insert(key, EntryNode::Many(vec![prev, EntryData::Scalar(value)]));
                        }
                        EntryNode::Many(mut prevs) => {
                            prevs.push(EntryData::Scalar(value));
                            map.insert(key, EntryNode::Many(prevs));
                        }
                    }
                } else {
                    map.insert(key, EntryNode::Unique(EntryData::Scalar(value)));
                }
            }
        }
    }
    let (input, _) = close_entry(input)?;
    Ok((input, (entry_key, EntryFragment::new(map))))
}