use crate::path::UniquePath;
use crate::error::KeyTreeErr;
use crate::{
EachIndent,
Key,
KeyLen,
Value,
Token,
Tokens,
KeyMap,
KeyTreeCore,
};
const INDENT_STEP: usize = 4;
#[derive(Clone, Debug, PartialEq)]
enum PS {
FC, BK, COK,
IK, RAK, AK,
IV,
CM, }
pub struct KeyTreeBuilder;
#[derive(Debug)]
pub struct BuildVars<'a> {
keymap: KeyMap,
keylen: KeyLen,
tokens: Tokens,
each_indent: EachIndent,
path: UniquePath,
first_key: bool,
ch_root_indent: Option<usize>,
root_path: Option<UniquePath>,
pos: usize,
ch_indent: Option<usize>,
start_line: Option<usize>,
start_key: Option<usize>,
end_key: Option<usize>,
start_val: Option<usize>,
end_val: Option<usize>,
pub s: &'a str,
}
impl<'a> BuildVars<'a> {
fn new(s: &'a str) -> Self {
Self {
keymap: KeyMap::new(),
keylen: KeyLen::new(),
tokens: Tokens::new(),
each_indent: EachIndent::new(),
path: UniquePath::new(),
first_key: true,
ch_root_indent: None,
root_path: None,
pos: 0,
ch_indent: None,
start_line: None,
start_key: None,
end_key: None,
start_val: None,
end_val: None,
s: s,
}
}
fn new_line(&mut self, pos: usize) {
self.start_line = Some(pos);
self.start_key = None;
self.end_key = None;
self.start_val = None;
self.end_val = None;
}
}
impl<'a> KeyTreeBuilder {
pub fn parse(s: &'a str) -> KeyTreeCore<'a> {
if s == "" { KeyTreeErr::empty_string(); unreachable!() };
let mut vars = BuildVars::new(s);
let mut parse_state: PS = PS::FC;
let mut iter = s.char_indices();
while let Some((pos, ch)) = iter.next() {
vars.pos = pos;
match (&parse_state, ch, ch.is_whitespace()) {
(PS::FC, '\n', true) => {
parse_state = PS::FC;
},
(PS::FC, _, true) => {
Self::set_start_line(&mut vars, pos);
parse_state = PS::BK;
},
(PS::FC, '/', false) => {
Self::set_start_line(&mut vars, pos);
Self::set_start_key(&mut vars, pos);
parse_state = PS::COK;
},
(PS::FC, ':', false) => {
KeyTreeErr::colon_before_key(pos);
unreachable!();
},
(PS::FC, _, false) => {
Self::set_start_line(&mut vars, pos);
Self::set_start_key(&mut vars, pos);
vars.start_key = Some(pos);
parse_state = PS::IK;
},
(PS::BK, '\n', true) => {
parse_state = PS::FC;
},
(PS::BK, _, true) => { },
(PS::BK, '/', false) => {
Self::set_start_key(&mut vars, pos);
parse_state = PS::COK;
},
(PS::BK, ':', false) => {
KeyTreeErr::colon_before_key(pos);
unreachable!();
},
(PS::BK, _, false) => {
Self::set_start_key(&mut vars, pos);
parse_state = PS::IK;
},
(PS::COK, '\n', true) => {
KeyTreeErr::line_incomplete(pos);
unreachable!();
},
(PS::COK, _, true) => {
KeyTreeErr::no_colon(pos);
unreachable!();
},
(PS::COK, '/', false) => {
parse_state = PS::CM;
},
(PS::COK, _, false) => {
parse_state = PS::IK;
},
(PS::CM, '\n', true) => {
parse_state = PS::FC;
},
(PS::CM, _, _) => { },
(PS::IK, '\n', true) => {
KeyTreeErr::line_incomplete(pos);
unreachable!();
},
(PS::IK, _, true) => {
KeyTreeErr::no_colon(pos);
unreachable!();
},
(PS::IK, ':', false) => {
Self::set_end_key(&mut vars, pos - 1);
parse_state = PS::RAK;
},
(PS::IK, _, false) => { }
(PS::RAK, _, false) => {
KeyTreeErr::no_space_after_key(pos);
unreachable!();
},
(PS::RAK, '\n', true) => {
Self::new_token(Self::key_token(&vars), &mut vars);
parse_state = PS::FC;
},
(PS::RAK, _, true) => {
parse_state = PS::AK;
},
(PS::AK, _, false) => {
if vars.first_key {
KeyTreeErr::first_token_is_val(vars.start_key.unwrap(), &vars);
unreachable!();
};
Self::set_start_val(&mut vars, pos);
parse_state = PS::IV;
},
(PS::AK, '\n', true) => {
Self::new_token(Self::key_token(&vars), &mut vars);
parse_state = PS::FC;
},
(PS::AK, _, true) => { },
(PS::IV, '\n', true) => {
Self::set_end_val(&mut vars, pos - 1);
Self::new_token(Self::value_token(&vars), &mut vars);
parse_state = PS::FC;
},
(PS::IV, _, true) => { },
(PS::IV, _, false) => {
vars.end_val = Some(pos);
},
}; };
match parse_state {
PS::CM => { },
PS::RAK | PS::AK => {
vars.end_key = Some(vars.pos);
Self::new_token(Self::key_token(&vars), &mut vars);
},
PS::IV => {
if vars.first_key {
KeyTreeErr::first_token_is_val(vars.start_key.unwrap(), &vars);
unreachable!();
};
vars.end_val = Some(vars.pos);
Self::new_token(Self::value_token(&vars), &mut vars);
},
_ => {
KeyTreeErr::line_incomplete(s.len() - 1);
},
};
Self::insert_end_indices(&mut vars, 0);
KeyTreeCore {
s: s,
keymap: vars.keymap,
keylen: vars.keylen,
tokens: vars.tokens,
root: vars.root_path.unwrap(),
}
}
fn new_token(token: Token, mut vars: &mut BuildVars) {
let key = &vars.s[vars.start_key.unwrap()..=vars.end_key.unwrap()];
if vars.first_key {
vars.ch_root_indent = Some(vars.start_key.unwrap());
vars.first_key = false;
vars.path = UniquePath::from(key).unwrap();
vars.root_path = Some(vars.path.clone());
vars.tokens.push(token);
vars.keymap.insert(&vars.path, vars.tokens.len() - 1);
vars.keylen.insert(&vars.path);
vars.each_indent.push(&vars.path);
} else {
let old_indent = vars.path.len() - 1;
let new_indent = Self::indent(&vars);
vars.path = vars.path
.clone()
.truncate(new_indent)
.append_unique(
&mut UniquePath::from(key).unwrap()
);
let index = vars.each_indent.new_index(
&vars.path,
new_indent
);
vars.path.set_last_index(index);
if new_indent <= old_indent {
Self::insert_end_indices(&mut vars, new_indent);
};
vars.keylen.insert(&vars.path);
vars.tokens.push(token);
vars.keymap.insert(&vars.path, vars.tokens.len() - 1);
vars.each_indent.insert(&vars.path, new_indent);
};
}
fn indent(vars: &BuildVars) -> usize {
let ch_indent = (vars.start_key.unwrap() - vars.start_line.unwrap()) - vars.ch_root_indent.unwrap() + 1;
if ch_indent % INDENT_STEP != 0 {
KeyTreeErr::indent(ch_indent, vars);
unreachable!();
} else {
ch_indent / INDENT_STEP
}
}
fn value_token(vars: &BuildVars) -> Token {
Token::Value(
Value::new(
vars.start_key.unwrap(),
vars.end_key.unwrap(),
vars.start_val.unwrap(),
vars.end_val.unwrap(),
)
)
}
fn key_token(vars: &BuildVars) -> Token {
Token::Key(
Key::new(
vars.start_key.unwrap(),
vars.end_key.unwrap(),
)
)
}
fn set_start_line(vars: &mut BuildVars, pos: usize) {
vars.start_line = Some(pos);
}
fn set_start_key(vars: &mut BuildVars, pos: usize) {
vars.start_key = Some(pos);
}
fn set_end_key(vars: &mut BuildVars, pos: usize) {
vars.end_key = Some(pos);
}
fn set_start_val(vars: &mut BuildVars, pos: usize) {
vars.start_val = Some(pos);
}
fn set_end_val(vars: &mut BuildVars, pos: usize) {
vars.end_val = Some(pos);
}
fn insert_end_indices(vars: &mut BuildVars, indent: usize) {
for i in indent..vars.each_indent.len() {
vars.keymap
.set_end(&vars.each_indent[i], vars.tokens.len() - 1);
};
vars.each_indent.0.truncate(indent);
}
}