pub mod color;
pub mod error_handling;
mod parse;
mod read;
pub mod datastructure {
use std::{cmp::{self, min}, collections::HashMap, path::Path, usize};
use crate::{
color::Color,
error_handling::{FileReadingError, ParsingError},
parse::{Entry, parse_file},
};
pub struct ParsedData {
file: String,
map: HashMap<String, (usize, String)>,
}
impl ParsedData {
pub fn from_file(path: &Path) -> Result<Self, FileReadingError> {
let entries: Vec<Entry> = parse_file(&path.display().to_string())?;
Ok(ParsedData::from_entries(
&path.display().to_string(),
entries,
))
}
pub fn as_raw(&self, key: &str) -> Result<(usize, String), FileReadingError> {
let modified_key = key
.chars()
.filter(|c| c.is_alphanumeric())
.collect::<String>()
.to_lowercase();
let entry = self.map.get(&modified_key);
if entry.is_none() {
return Err(FileReadingError::new(
self.file.to_string(),
format!("key '{key}' could not be found inside of the file"),
));
}
Ok(entry.unwrap().clone())
}
pub fn as_text(&self, key: &str) -> Result<String, FileReadingError> {
let (line, raw) = self.as_raw(key)?;
let mut quote_count: u16 = 0;
let mut first_quote:usize = 0xFFFFFFFF;
let mut last_quote:usize = 0x0;
for (i,c) in raw.chars().enumerate() {
if c != '"' {
continue;
}
quote_count+=1;
first_quote = min(first_quote, i);
last_quote = cmp::max(last_quote, i);
}
let trimmed: String = raw
.chars()
.enumerate()
.filter(|(i,_)|{*i > first_quote && *i < last_quote})
.map(|(_,c)| {c})
.collect();
if quote_count < 2 {
return Err(FileReadingError::from(ParsingError::new(
self.file.to_string(),
format!("There isn't enough double quotes in the value of key '{key}'"),
line,
)));
}
Ok(trimmed)
}
pub fn as_number(&self, key: &str) -> Result<f64, FileReadingError> {
let (line, raw) = self.as_raw(key)?;
Ok(f64_from_string(self.file.to_string(), line, &raw)?)
}
pub fn as_boolean(&self, key: &str) -> Result<bool, FileReadingError> {
let (line, raw) = self.as_raw(key)?;
let lower_raw = raw.to_lowercase();
let yes: bool =
lower_raw == "yes" || lower_raw == "y" || lower_raw == "true" || lower_raw == "1";
let no: bool =
lower_raw == "no" || lower_raw == "n" || lower_raw == "false" || lower_raw == "0";
if !no && !yes {
return Err(FileReadingError::from(ParsingError::new(
self.file.to_string(),
format!("Invalid symbol '{raw}' was found"),
line,
)));
}
Ok(yes)
}
pub fn as_color(&self, key: &str) -> Result<Color, FileReadingError> {
let (line, raw) = self.as_raw(key)?;
let rgb = Color::from_rgb_string(&raw);
let hexadecimal = Color::from_hexadecimal(&raw);
let text = Color::from_color_string(&raw);
if let Ok(color) = rgb {
return Ok(color);
} else if let Ok(color) = hexadecimal {
return Ok(color);
} else if let Ok(color) = text {
return Ok(color);
}
Err(FileReadingError::from(ParsingError::new(
self.file.to_string(),
format!("{raw} could not be interpreted as a color"),
line,
)))
}
fn from_entries(path: &str, entries: Vec<Entry>) -> Self {
let mut map: HashMap<String, (usize, String)> = HashMap::new();
for entry in entries {
map.insert(
String::from(entry.name()),
(entry.line_number(), String::from(entry.content())),
);
}
Self {
file: path.to_string(),
map,
}
}
}
fn f64_from_string(file: String, line: usize, str: &str) -> Result<f64, ParsingError> {
let mut number: f64 = 0.0;
let mut decimal: f64 = 1.0;
let mut decimals: bool = false;
let mut negative:bool = false;
for character in str.chars() {
if !character.is_numeric() && character != '.' && character != '-'{
return Err(ParsingError::new(
file,
String::from("Non numeric character in f64"),
line,
));
}
if character == '-' {
negative =true;
continue;
}
if !decimals {
if character == '.' {
decimals = true;
continue;
}
} else {
decimal /= 10.0
}
let num = character as u32 - '0' as u32;
if decimal == 1.0 {
number *= 10.0;
}
number += num as f64 * decimal;
}
if negative{
return Ok(-number);
}
Ok(number)
}
}