use colored::Colorize;
use core::str::Split;
use std::error::Error;
use std::fmt::Display;
use std::ops::Index;
use std::fmt;
use std::process::exit;
const INDENTSTEP: usize = 4;
fn spaces(i: usize) -> String {
let mut s = String::new();
for _n in 0..i { s.push_str(" ") };
s
}
#[derive(Debug, PartialEq)]
pub enum SmallError {
EmptyKey(Token, usize),
Indent(Token, usize),
Empty,
IsKey(Token),
IsValue(String),
KeyParse(String),
MissingToken,
NoColon(Token, usize),
NoQuoteAfterKey(Token, usize),
NoSecondQuote(Token, usize),
NoSpaceAfterKey(Token),
NotUnique(usize),
ParseValue(Token, &'static str),
QuotemarkInKey(Token, usize),
User(String),
}
impl Error for SmallError {
}
impl fmt::Display for SmallError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SmallError::Empty => {
write!(f, "The input string is empty.")
},
SmallError::EmptyKey(token, pos) => {
write!(f, "Line {}:{} Empty key.", token.line, pos)
},
SmallError::Indent(token, _) => {
write!(f, "Line {}:{} Indentation should be aligned by {} to the top key ",
token.line,
token.start_key.unwrap(),
INDENTSTEP
)
},
SmallError::IsKey(s) => {
write!(f, "Expected 'key: value' but found only key \"{}\"", s)
},
SmallError::IsValue(s) => {
write!(f, "\"{}\" is a value, not a key.", s)
},
SmallError::ParseValue(token, to_type) => {
write!(f, "Line {}:{} [{}] The value could not be parsed into a {}.",
token.line,
token.start_val.unwrap(),
token.text.trim().cyan().bold(),
to_type,
)
},
SmallError::KeyParse(s) => {
write!(f,
"\"{}\" cannot be parsed.", s)
},
SmallError::NoColon(token, pos) => {
write!(f, "Line {}: [{}] should have a colon after the key at position {}.", token.line, token.text, pos)
},
SmallError::NotUnique(n) => {
write!(f,
"Resulted in {} keys, but expected 1.", n)
},
SmallError::NoQuoteAfterKey(token, pos) => {
write!(f, "Line {}:{} Value must start with double quotemark", token.line, pos)
},
SmallError::NoSecondQuote(token, pos) => {
write!(f, "Line {}:{} No second quote.", token.line, pos)
},
SmallError::NoSpaceAfterKey(token) => {
write!(f, "Line {}:{} No space after .", token.line, token.end_key.unwrap())
},
SmallError::MissingToken => {
write!(f, "Expected another token.")
},
SmallError::QuotemarkInKey(token, pos) => {
write!(f, "Line {}:{} Quote mark in key.", token.line, pos)
},
SmallError::User(s) => {
write!(f, "{}", s)
},
}
}
}
#[derive(Debug)]
pub struct KeyPath<'a>(&'a str, Split<'a, &'a str>);
impl<'a> KeyPath<'a> {
pub fn from_str(s: &'a str) -> Result<Self, SmallError> {
if s.is_empty() { return Err(SmallError::KeyParse(s.to_owned())) };
if s.split("::").any(|k| k.contains(":")) {
return Err(SmallError::KeyParse(s.to_owned()))
};
Ok(KeyPath(s, s.split("::")))
}
}
impl<'a> fmt::Display for KeyPath<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'a> Iterator for KeyPath<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.1.next()
}
}
#[derive(Clone, Debug)]
pub struct Token {
text: String,
line: usize,
start_key: Option<usize>,
end_key: Option<usize>,
start_val: Option<usize>,
end_val: Option<usize>,
}
impl PartialEq for Token {
fn eq(&self, other: &Token) -> bool {
self.indentation() == other.indentation() &&
self.key() == other.key() &&
self.value() == other.value()
}
}
#[derive(Debug)]
enum PS {
BK,
IK,
RAK,
AK,
IV,
AV,
}
impl Token {
fn new(
text: &str,
line: usize,
start_key: Option<usize>,
end_key: Option<usize>,
start_val: Option<usize>,
end_val: Option<usize>) -> Self {
Token {
text: text.to_string(),
line: line,
start_key: start_key,
end_key: end_key,
start_val: start_val,
end_val: end_val,
}
}
fn indent(&mut self) {
self.text = format!(" {}", self.text);
self.start_key = self.start_key.map(|sk| sk + INDENTSTEP);
self.end_key = self.end_key.map(|ek| ek + INDENTSTEP);
self.start_val = self.start_val.map(|sv| sv + INDENTSTEP);
self.end_val = self.end_val.map(|ev| ev + INDENTSTEP);
}
fn from_str(s: &str, line: usize, root_indent: usize) -> Result<Self, SmallError> {
let mut ps = PS::BK;
let mut escape = false;
let mut start_key: Option<usize> = None;
let mut end_key: Option<usize> = None;
let mut start_val: Option<usize> = None;
let mut end_val: Option<usize> = None;
for (pos, c) in s.char_indices() {
if let PS::BK = ps {
if c == ':' {
start_key = Some(pos);
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
return Err(SmallError::EmptyKey(token, pos));
};
if !c.is_whitespace() {
start_key = Some(pos);
if (pos - root_indent) % INDENTSTEP != 0 {
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
return Err(SmallError::Indent(token, root_indent));
};
ps = PS::IK;
};
continue;
};
if let PS::IK = ps {
if c == '"' {
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
return Err(SmallError::QuotemarkInKey(token, pos));
};
if c.is_whitespace() {
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
return Err(SmallError::NoColon(token, pos));
};
if c == ':' {
if Some(pos) == start_key {
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
return Err(SmallError::EmptyKey(token, pos))
} else {
end_key = Some(pos);
ps = PS::RAK;
continue;
};
};
};
if let PS::RAK = ps {
if !c.is_whitespace() {
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
return Err(SmallError::NoSpaceAfterKey(token))
} else {
ps = PS::AK;
continue;
}
};
if let PS::AK = ps {
if c.is_whitespace() {
continue;
} else {
if c != '"' {
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
return Err(SmallError::NoQuoteAfterKey(token, pos))
} else {
start_val = Some(pos);
ps = PS::IV;
continue;
}
}
};
if let PS::IV = ps {
if c == '\\' {
escape = true;
} else if c == '"' && escape {
continue;
} else if c == '"' {
ps = PS::AV;
end_val = Some(pos);
} else {
escape = false;
continue;
}
};
if let PS::AV = ps { break };
};
let token = Token::new(s, line, start_key, end_key, start_val, end_val);
match ps {
PS::BK => { panic!("Shouldn't fail here") },
PS::IK => { Err(SmallError::NoColon(token, s.char_indices().count())) },
PS::RAK => { Ok(token) },
PS::AK => { Ok(token) },
PS::IV => { return Err(SmallError::NoSecondQuote(token, s.char_indices().count())) },
PS::AV => { Ok(token) },
}
}
fn key(&self) -> String {
self.text[self.start_key.unwrap()..=self.end_key.unwrap() - 1].to_string()
}
fn indentation(&self) -> usize {
self.start_key.expect("Bug: start_key was None but should always be Some.")
}
fn line(&self) -> usize {
self.line
}
fn is_value(&self) -> bool {
if let (Some(_), Some(_), Some(_), Some(_)) =
(self.start_key, self.end_key, self.start_val, self.end_val) {
true
} else {
false
}
}
fn value(&self) -> Result<String, SmallError> {
if self.is_value() {
Ok(self.text[self.start_val.unwrap() + 1..=self.end_val.unwrap() - 1].to_string())
} else {
Err(SmallError::IsKey(self.clone()))
}
}
}
impl Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.text)
}
}
#[derive(Debug)]
pub struct SmallString(Vec<Token>);
fn empty_line(line: &str) -> bool {
!line.chars().any(|c| !c.is_whitespace())
}
impl SmallString {
pub fn from_str(s: &str) -> Result<Self, SmallError> {
let mut v = Vec::new();
let mut before_start_line = true;
let mut root_indent = 0;
let mut line_num = 0;
for ln in s.lines().skip_while(|ln| empty_line(ln)) {
if before_start_line == true {
root_indent = ln.chars().position(|c| !c.is_whitespace()).unwrap();
before_start_line = false;
};
v.push(Token::from_str(ln, line_num, root_indent)?);
line_num += 1;
};
if v.is_empty() { Err(SmallError::Empty) } else { Ok(SmallString(v)) }
}
fn indent(&mut self) {
for token in self.0.iter_mut() {
token.indent();
}
}
pub fn to_ref(&self) -> Small {
Small {
small: self,
reduction: vec!(&self.0[..]),
}
}
fn max_key_end(&self) -> (usize, usize) {
let root_indent = self.0[0].indentation();
let max_end_key = self.0.iter().map(|token| token.end_key.unwrap()).max().unwrap();
(root_indent, max_end_key - root_indent)
}
}
impl Index<usize> for SmallString {
type Output = Token;
fn index(&self, i: usize) -> &Token {
&self.0[i]
}
}
impl fmt::Display for SmallString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (root_indent, max_key_end) = self.max_key_end();
let mut s = String::new();
for token in self.0.iter() {
let val_shift: Option<i32> = match token.start_val {
Some(sv) => Some((max_key_end + 1) as i32 - sv as i32),
None => None,
};
let (start_val, end_val) = match (token.start_val, token.end_val, val_shift) {
(Some(sv), Some(ev), Some(vs)) => {
(Some((sv as i32 + vs as i32) as usize), Some((ev as i32 + vs as i32) as usize))
},
_ => {
(None, None)
},
};
let new_token = Token {
text: token.text.clone(),
line: token.line,
start_key: Some(token.start_key.unwrap() - root_indent),
end_key: Some(token.end_key.unwrap() - root_indent),
start_val: start_val,
end_val: end_val,
};
s.push_str(&format!("{}\n", new_token.to_string()));
};
s.pop();
write!(f, "{}", s)
}
}
#[derive(Clone, Debug)]
pub struct Small<'a> {
pub small: &'a SmallString,
reduction: Vec<&'a [Token]>,
}
impl<'a> Small<'a> {
fn tokens(&self) -> Vec<String> {
let mut v = Vec::new();
for key_set in self.reduction.iter() {
for token in key_set.iter() {
v.push(token.to_string())
}
}
v
}
fn apply_key(self, key: &str) -> Result<Small<'a>, SmallError> {
let mut reduction: Vec<&[Token]> = Vec::new();
for &slice in self.reduction.iter() {
let root_indent = slice[0].indentation();
let mut i = 0usize;
'outer: loop {
if slice[i].key() == key && (slice[i].indentation() - root_indent) / INDENTSTEP == 1 {
let start_index = i;
'inner: loop {
if (i < slice.len() - 1) &&
((slice[i + 1].indentation() - root_indent) / INDENTSTEP <= 1) {
let end_index = i;
reduction.push(&slice[start_index..=end_index]);
break 'inner;
}
if i == slice.len() - 1 {
let end_index = i;
reduction.push(&slice[start_index..=end_index]);
break 'outer;
};
i += 1
}
};
i += 1;
if i == slice.len() { break 'outer };
};
};
Ok(Small {
small: self.small,
reduction: reduction,
})
}
fn apply_path(&self, s: &str) -> Result<Small, SmallError> {
let mut reduction = self.clone();
for (i, key) in KeyPath::from_str(s)?.enumerate() {
if i > 0 {
reduction = reduction.apply_key(&key.to_string())?;
};
}
Ok(reduction)
}
}
impl<'a> Display for Small<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
s.push_str("reductions:\n");
for slice in self.reduction.iter() {
s.push_str("[\n");
for token in slice.iter() {
s.push_str(&token.to_string());
s.push('\n');
}
s.push_str("]\n");
}
s.pop();
write!(f, "{}", s)
}
}
impl<'a> Small<'a> {
fn unique_value(&self) -> Result<Token, SmallError> {
if self.reduction.is_empty() { return Err(SmallError::MissingToken) };
if self.reduction.len() > 1 { return Err(SmallError::NotUnique(self.reduction.len())) };
let token = &self.reduction[0][0];
if token.is_value() { Ok(token.clone()) } else { Err(SmallError::IsKey(token.clone())) }
}
}
pub trait FromSmall {
fn from_small(slice: &Small) -> Result<Self, SmallError>
where Self: std::marker::Sized;
fn path(small: &Small, key_path: &str) -> Result<Self, SmallError>
where
Self: std::marker::Sized
{
Ok(Self::from_small(&small.apply_path(key_path)?)?)
}
fn from_str(s: &str) -> Result<Self, SmallError>
where Self: std::marker::Sized
{
let small = SmallString::from_str(s)?;
let small = Small {
small: &small,
reduction: vec!(&small.0[..]),
};
Ok(Self::from_small(&small)?)
}
fn from_str_debug(s: &str) -> Self
where Self: std::marker::Sized
{
match Self::from_str(s) {
Ok(s) => s,
Err(e) => {
match e {
SmallError::Empty => {
eprintln!("The input data string is empty.");
}
SmallError::EmptyKey(token, pos) => {
let info = &format!(
"{}{} {}",
" ".repeat(pos),
"^".yellow().bold(),
"missing key".yellow().bold(),
);
in_context(s, info, token.line);
},
SmallError::Indent(token, _) => {
let info = &format!(
"{}{}{}{} {} {} {}",
" ".repeat(token.start_key.unwrap() - 4 - (token.start_key.unwrap() % 4)),
"|".yellow().bold(),
"_".repeat(3).yellow().bold(),
"|".yellow().bold(),
"indent".yellow().bold(),
INDENTSTEP.to_string().yellow().bold(),
"spaces only".yellow().bold()
);
in_context(s, info, token.line);
},
SmallError::NoColon(token, pos) => {
let info = &format!(
"{}{} {}",
" ".repeat(token.start_key.unwrap()),
"^".repeat(pos - token.start_key.unwrap()).yellow().bold(),
"key requires colon".yellow().bold()
);
in_context(s, info, token.line);
},
SmallError::NoSecondQuote(token, pos) => {
let info = &format!(
"{}{} {}",
" ".repeat(pos),
"^".yellow().bold(),
"missing second quotemark".yellow().bold(),
);
in_context(s, info, token.line);
}
SmallError::NoSpaceAfterKey(token) => {
let info = &format!(
"{}{}{} {}",
" ".repeat(token.start_key.unwrap()),
" ".repeat(token.end_key.unwrap() + 1 - token.start_key.unwrap()),
"^".yellow().bold(),
"requires at least one space after key".yellow().bold()
);
in_context(s, info, token.line);
},
SmallError::ParseValue(token, to_type) => {
let info = &format!(
"{}{} {}",
" ".repeat(token.start_val.unwrap() + 1),
"^".repeat(token.value().unwrap().char_indices().count()).yellow().bold(),
format!("number could not be parsed into a {}", to_type).yellow().bold(),
);
in_context(s, info, token.line);
},
SmallError::QuotemarkInKey(token, pos) => {
let info = &format!(
"{}{} {}",
" ".repeat(pos),
"^".yellow().bold(),
"no double quotes allowed in key".yellow().bold(),
);
in_context(s, info, token.line);
}
_ => eprintln!("{}", e.to_string()),
};
exit(1);
},
}
}
}
fn in_context(s: &str, info: &str, line: usize) {
for (i, ln) in s.lines().take(20).enumerate() {
eprintln!("{}", ln);
if i == line {
eprintln!("{}", info);
};
};
}
impl FromSmall for String {
fn from_small(small: &Small) -> Result<Self, SmallError> {
small.unique_value()?.value()
}
}
impl FromSmall for u8 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<u8>()
.map_err(|_| SmallError::ParseValue(token, "u8"))
}
}
impl FromSmall for i8 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<i8>()
.map_err(|_| SmallError::ParseValue(token, "i8"))
}
}
impl FromSmall for u16 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<u16>()
.map_err(|_| SmallError::ParseValue(token, "u16"))
}
}
impl FromSmall for i16 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<i16>()
.map_err(|_| SmallError::ParseValue(token, "i16"))
}
}
impl FromSmall for u32 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<u32>()
.map_err(|_| SmallError::ParseValue(token, "u32"))
}
}
impl FromSmall for i32 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<i32>()
.map_err(|_| SmallError::ParseValue(token, "i32"))
}
}
impl FromSmall for u64 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<u64>()
.map_err(|_| SmallError::ParseValue(token, "u64"))
}
}
impl FromSmall for i64 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<i64>()
.map_err(|_| SmallError::ParseValue(token, "i64"))
}
}
impl FromSmall for u128 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<u128>()
.map_err(|_| SmallError::ParseValue(token, "u128"))
}
}
impl FromSmall for i128 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<i128>()
.map_err(|_| SmallError::ParseValue(token, "i128"))
}
}
impl FromSmall for usize {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token.
value()?
.parse::<usize>()
.map_err(|_| SmallError::ParseValue(token, "usize"))
}
}
impl FromSmall for isize {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token.
value()?
.parse::<isize>()
.map_err(|_| SmallError::ParseValue(token, "isize"))
}
}
impl FromSmall for f32 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<f32>()
.map_err(|_| SmallError::ParseValue(token, "f32"))
}
}
impl FromSmall for f64 {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
token
.value()?
.parse::<f64>()
.map_err(|_| SmallError::ParseValue(token, "f64"))
}
}
impl FromSmall for bool {
fn from_small(small: &Small) -> Result<Self, SmallError> {
let token = small.unique_value()?;
if "false" == &token.value()? {
return Ok(false)
};
if "true" == &token.value()? {
return Ok(true)
};
Err(SmallError::ParseValue(token, "bool"))
}
}
impl<T> FromSmall for Option<T>
where
T: FromSmall
{
fn from_small(small: &Small) -> Result<Self, SmallError> {
if small.reduction.is_empty() {
return Ok(None)
} else {
Ok(Some(T::from_small(small)?))
}
}
}
impl<T> FromSmall for Vec<T>
where
T: FromSmall
{
fn from_small(sr: &Small) -> Result<Self, SmallError> {
let mut v: Vec<T> = Vec::new();
for &slice in sr.reduction.iter() {
let dat = Small {
small: sr.small,
reduction: vec!(slice),
};
v.push(T::from_small(&dat)?);
};
Ok(v)
}
}