#![feature(try_trait)]
use colored::Colorize;
use std::error::Error;
use std::fmt::Display;
use std::ops::Index;
use core::slice::Iter;
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)]
pub enum SmallError {
BoolParse(Token),
EmptyKey(Token, usize),
Indent(Token, usize),
Empty,
FloatParse(Token),
PathIsEmpty,
IsKey(Token),
IsValue(String),
KeyParse(String),
IntegerParse(Token),
NoColon(Token, usize),
NoQuoteAfterKey(Token, usize),
NoSecondQuote(Token, usize),
NoSpaceAfterKey(Token),
NotUnique(usize),
QuotemarkInKey(Token, usize),
Quotes(usize, String),
Reduction,
TopValueMismatch,
User(String),
}
impl Error for SmallError {
}
impl fmt::Display for SmallError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SmallError::BoolParse(token) => {
write!(f, "Line {}:{} [{}] The number could not be parsed as a boolean.",
token.line,
token.start_val.unwrap(),
token.text.trim().cyan().bold(),
)
},
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::Empty => {
write!(f, "The input string is empty.")
},
SmallError::FloatParse(token) => {
write!(f, "Line {}:{} [{}] The number could not be parsed as a float.",
token.line,
token.start_val.unwrap(),
token.text.trim().cyan().bold(),
)
},
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::IntegerParse(token) => {
write!(f, "Line {}:{} [{}] The number could not be parsed as an integer.",
token.line,
token.start_val.unwrap(),
token.text.trim().cyan().bold(),
)
},
SmallError::KeyParse(s) => {
write!(f,
"\"{}\" cannot be parsed.", s)
},
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::PathIsEmpty => {
write!(f, "There is no data in that path.")
},
SmallError::QuotemarkInKey(token, pos) => {
write!(f, "Line {}:{} Quote mark in key.", token.line, pos)
},
SmallError::Quotes(line, tok_s) => {
write!(f,
"line {}: \"{}\" is not correctly quoted.",
line,
tok_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::Reduction => {
write!(f, "{}", "reduction")
},
SmallError::TopValueMismatch => {
write!(f, "{}", "top_value_mismatch")
},
SmallError::User(s) => {
write!(f, "{}", s)
},
}
}
}
struct KeyPath(Vec<Key>);
impl KeyPath {
pub fn from_str(s: &str) -> Result<Self, SmallError> {
let mut v = Vec::new();
for key in s.split("::") {
if key.contains(":") {
return Err(SmallError::KeyParse(key.to_string()))
};
v.push(Key::from_str(key));
};
Ok(KeyPath(v))
}
fn iter(&self) -> Iter<Key> {
self.0.iter()
}
}
impl fmt::Display for KeyPath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
for i in self.iter() {
s.push_str(&format!("{}::", i));
};
s.pop();
s.pop();
write!(f, "{}", s)
}
}
#[derive(Clone, Debug, PartialEq)]
struct Key(String);
impl Key {
fn from_str(s: &str) -> Self {
Key(s.to_string())
}
fn len(&self) -> usize {
self.0.len()
}
}
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[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>,
}
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 from_str(s: &str, line: usize, root_indent: usize) -> Result<Option<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);
if let PS::BK = ps {
return Ok(None)
};
if let PS::IK = ps {
return Err(SmallError::NoColon(token, s.char_indices().count()))
}
if let PS::RAK = ps {
return Ok(Some(token))
}
if let PS::AK = ps {
return Ok(Some(token))
}
if let PS::IV = ps {
return Err(SmallError::NoSecondQuote(token, s.char_indices().count()))
}
Ok(Some(token))
}
fn key(&self) -> Key {
Key::from_str(&self.text[self.start_key.unwrap()..=self.end_key.unwrap() - 1].to_string())
}
fn indent(&self) -> usize {
self.start_key.unwrap()
}
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 Tokens(Vec<Token>);
impl Tokens {
pub fn from_str(s: &str) -> Result<Self, SmallError> {
let mut v = Vec::new();
let root_indent = match s.lines().find(|&ln| ln.chars().any(|c| c.is_whitespace())) {
Some(line) => {
line.chars().position(|c| !c.is_whitespace()).unwrap()
},
None => {
return Err(SmallError::Empty);
},
};
for (line, tok_str) in s.lines().enumerate() {
match Token::from_str(tok_str, line, root_indent)? {
Some(ts) => {
v.push(ts);
},
None => {},
};
};
Ok(Tokens(v))
}
}
impl Index<usize> for Tokens {
type Output = Token;
fn index(&self, i: usize) -> &Token {
&self.0[i]
}
}
impl fmt::Display for Tokens {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
self.0.iter().for_each(|token| s.push_str(&format!("{}\n", token.to_string())));
s.pop();
write!(f, "{}", s)
}
}
#[derive(Clone, Debug)]
pub struct Data<'a> {
pub tokens: &'a Tokens,
reduction: Vec<&'a [Token]>,
}
impl<'a> Data<'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 reduce_once(self, s: &str) -> Result<Data<'a>, SmallError> {
let key = Key::from_str(s);
let mut reduction: Vec<&[Token]> = Vec::new();
for &slice in self.reduction.iter() {
let root_indent = slice[0].indent();
let mut i = 0usize;
'outer: loop {
if slice[i].key() == key && (slice[i].indent() - root_indent) / INDENTSTEP == 1 {
let start_index = i;
'inner: loop {
if (i < slice.len() - 1) &&
((slice[i + 1].indent() - 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(Data {
tokens: self.tokens,
reduction: reduction,
})
}
fn reduce(&self, s: &str) -> Result<Data, SmallError> {
let key_path = KeyPath::from_str(s)?;
let mut reduction = self.clone();
for (i, key) in key_path.iter().enumerate() {
if i > 0 {
reduction = reduction.reduce_once(&key.to_string())?;
};
}
Ok(reduction)
}
}
impl<'a> Display for Data<'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> Data<'a> {
fn unique_value(&self) -> Result<Token, SmallError> {
if self.reduction.is_empty() {
return Err(SmallError::PathIsEmpty)
};
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 Small {
fn from_data(data: Data) -> Result<Self, SmallError>
where Self: std::marker::Sized;
fn sml(data: &Data, key_path: &str) -> Result<Self, SmallError>
where Self: std::marker::Sized {
Ok(Self::from_data(data.reduce(key_path)?)?)
}
fn from_str(s: &str) -> Result<Self, SmallError>
where Self: std::marker::Sized {
let tokens = Tokens::from_str(s)?;
let data = Data {
tokens: &tokens,
reduction: vec!(&tokens.0[..]),
};
Ok(Self::from_data(data)?)
}
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::BoolParse(token) => {
let info = &format!(
"{}{} {}",
" ".repeat(token.start_val.unwrap() + 1),
"^".repeat(token.value().unwrap().char_indices().count()).yellow().bold(),
"number could not be parsed as bool".yellow().bold(),
);
in_context(s, info, token.line);
},
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::FloatParse(token) => {
let info = &format!(
"{}{} {}",
" ".repeat(token.start_val.unwrap() + 1),
"^".repeat(token.value().unwrap().char_indices().count()).yellow().bold(),
"number could not be parsed as float".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::IntegerParse(token) => {
let info = &format!(
"{}{} {}",
" ".repeat(token.start_val.unwrap() + 1),
"^".repeat(token.value().unwrap().char_indices().count()).yellow().bold(),
"number could not be parsed as integer".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::QuotemarkInKey(token, pos) => {
let info = &format!(
"{}{} {}",
" ".repeat(pos),
"^".yellow().bold(),
"no double quotes allowed in key".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);
},
_ => 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 Small for String {
fn from_data(data: Data) -> Result<Self, SmallError> {
let token = data.unique_value()?;
Ok(token.value()?)
}
}
impl Small for u32 {
fn from_data(data: Data) -> Result<Self, SmallError> {
let token = data.unique_value()?;
match token.value()?.parse::<u32>() {
Ok(n) => Ok(n),
Err(_) => Err(SmallError::IntegerParse(token)),
}
}
}
impl Small for f32 {
fn from_data(data: Data) -> Result<Self, SmallError> {
let token = data.unique_value()?;
match token.value()?.parse::<f32>() {
Ok(n) => Ok(n),
Err(_) => Err(SmallError::FloatParse(token)),
}
}
}
impl Small for bool {
fn from_data(data: Data) -> Result<Self, SmallError> {
let token = data.unique_value()?;
let s = token.value()?;
if s == String::from("false") {
Ok(false)
} else if s == String::from("true") {
Ok(true)
} else {
Err(SmallError::BoolParse(token))
}
}
}
impl<T> Small for Option<T> where T: Small {
fn from_data(data: Data) -> Result<Self, SmallError> {
if data.reduction.is_empty() {
return Ok(None)
} else {
Ok(Some(T::from_data(data)?))
}
}
}
impl<T> Small for Vec<T> where T: Small {
fn from_data(data: Data) -> Result<Self, SmallError> {
let mut v: Vec<T> = Vec::new();
for &slice in data.reduction.iter() {
let dat = Data {
tokens: data.tokens,
reduction: vec!(slice),
};
v.push(T::from_data(dat)?);
};
Ok(v)
}
}