use std::{collections::HashMap, fmt::Display, fs::File};
use std::{io::prelude::*, ops::Deref};
use std::{io::BufReader, ops::DerefMut};
use crate::ForceUnwrap;
use serde_json::json;
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum FQLType {
String(String),
Float(f64),
Int(i64),
Null,
}
impl Into<serde_json::Value> for FQLType {
fn into(self) -> serde_json::Value {
match self {
Self::String(s) => json!(s),
Self::Int(s) => json!(s),
Self::Float(s) => json!(s),
Self::Null => serde_json::Value::Null,
}
}
}
impl std::fmt::Display for FQLType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::String(s) => write!(f, "\"{}\"", s),
Self::Int(s) => write!(f, "{}", s),
Self::Float(s) => write!(f, "{}", s),
Self::Null => write!(f, "null"),
}
}
}
macro_rules! _impl_from_for_fqltype {
(int: $($t:ty),*) => {
$(
impl From<$t> for FQLType {
fn from(value: $t) -> Self {
Self::Int(value as i64)
}
}
)*
};
(float: $($t:ty),*) => {
$(
impl From<$t> for FQLType {
fn from(value: $t) -> Self {
Self::Float(value as f64)
}
}
)*
};
(display: $($t:ty),*) => {
$(
impl From<$t> for FQLType {
fn from(value: $t) -> Self {
if format!("{}", value) == "null" {
return Self::Null;
}
if let Ok(v) = value.parse::<i64>() {
return Self::Int(v);
}
if let Ok(v) = value.parse::<f64>() {
return Self::Float(v);
}
if value.starts_with("\"") && value.ends_with("\"") {
return Self::String(value[1..value.len() - 1].to_string());
}
Self::String(value.to_string())
}
}
)*
};
}
_impl_from_for_fqltype!(float: f32, f64);
_impl_from_for_fqltype!(display: String, &str, &String);
_impl_from_for_fqltype!(int: i8, u8, i16, u16, i32, u32, i64, u64, isize, usize);
#[derive(Clone, Debug)]
pub struct AppData(HashMap<String, FQLType>);
impl AppData {
pub fn update(&mut self, key: String, val: FQLType) {
self.0.insert(key, val);
}
pub fn inner(self) -> HashMap<String, FQLType> {
self.0
}
pub fn serialize(&self) -> HashMap<String, serde_json::Value> {
let mut map = HashMap::new();
for (k, v) in &self.0 {
map.insert(k.to_string(), v.clone().into());
}
map
}
}
impl From<HashMap<String, FQLType>> for AppData {
fn from(data: HashMap<String, FQLType>) -> Self {
AppData(data)
}
}
impl AsRef<HashMap<String, FQLType>> for AppData {
fn as_ref(&self) -> &HashMap<String, FQLType> {
&self.0
}
}
impl Deref for AppData {
type Target = HashMap<String, FQLType>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for AppData {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Clone, Debug)]
pub struct App {
pub path: Option<String>,
pub data: Vec<AppData>,
pub keys: Vec<String>,
delimiter: String,
}
impl From<Vec<String>> for App {
fn from(lines: Vec<String>) -> Self {
let mut index = 0;
let mut data = Vec::new();
let delimiter = lines[0].trim().to_string();
let keys: Vec<String> = lines[1].split(&delimiter).map(|s| s.to_string()).collect();
for line in &lines[2..] {
let mut map = HashMap::new();
line.split(&delimiter)
.enumerate()
.into_iter()
.for_each(|(i, s)| {
map.insert(keys[i].clone(), FQLType::from(s));
map.insert("$index".to_string(), FQLType::from(index));
});
data.push(AppData::from(map));
index += 1;
}
Self {
path: None,
data,
keys,
delimiter,
}
}
}
impl From<&str> for App {
fn from(s: &str) -> Self {
App::from(
s.split("\n")
.map(|s| s.trim().to_string())
.filter(|s| s.len() > 0)
.collect::<Vec<String>>(),
)
}
}
impl From<Vec<AppData>> for App {
fn from(data: Vec<AppData>) -> Self {
if data.len() == 0 {
return Self {
path: None,
data: Vec::new(),
keys: Vec::new(),
delimiter: "".to_string(),
};
}
let keys: Vec<String> = data[0].keys().map(|s| s.to_string()).collect();
Self {
path: None,
data,
keys,
delimiter: "".to_string(),
}
}
}
impl From<Vec<&AppData>> for App {
fn from(data: Vec<&AppData>) -> Self {
if data.len() == 0 {
return Self {
path: None,
data: Vec::new(),
keys: Vec::new(),
delimiter: "".to_string(),
};
}
let keys: Vec<String> = data[0].keys().map(|s| s.to_string()).collect();
let data = data.into_iter().map(|s| s.clone()).collect();
Self {
path: None,
data,
keys,
delimiter: "".to_string(),
}
}
}
impl Into<Vec<Vec<FQLType>>> for App {
fn into(self) -> Vec<Vec<FQLType>> {
self.data
.into_iter()
.map(|item| {
item.values()
.map(|t| t.to_owned())
.collect::<Vec<FQLType>>()
})
.collect()
}
}
impl App {
pub fn from_file<T: Display>(path: T) -> std::io::Result<Self> {
File::open(format!("{}", path)).and_then(|f| {
Ok(Self {
path: Some(format!("{}", path)),
..Self::from(
BufReader::new(f)
.lines()
.map(|line| line.force_unwrap().trim().to_string())
.collect::<Vec<String>>(),
)
})
})
}
pub fn serialize(&self) -> Vec<HashMap<String, serde_json::Value>> {
self.data.iter().map(|s| s.clone().serialize()).collect()
}
pub fn merge_data(&mut self, data: Vec<AppData>) {
self.data = data;
}
pub fn save_at<T: Display>(&self, path: T) {
let mut file = File::create(format!("{}", path)).unwrap();
let head = format!("{}\n{}\n", self.delimiter, self.keys.join(&self.delimiter));
file.write(head.as_bytes()).expect(&format!(
"Parse::save_file failed at {} \ncontent: {}",
path, head
));
for data in &self.data {
let line = self
.keys
.iter()
.map(|s| format!("{}", data.get(s).force_unwrap()))
.collect::<Vec<String>>()
.join(&self.delimiter);
file.write(line.as_bytes()).expect(&format!(
"Parse::save_file failed at {} \ncontent: {}",
path, line
));
file.write(b"\n").force_unwrap();
}
}
pub fn create_data(&self) -> AppData {
let mut map = HashMap::new();
for key in &self.keys {
map.insert(key.to_string(), FQLType::Null);
}
AppData::from(map)
}
pub fn init_data(&self, data: &mut AppData) {
data.0.clear();
for key in &self.keys {
data.0.insert(key.to_string(), FQLType::Null);
}
}
pub fn save(&self) {
if let Some(path) = &self.path {
self.save_at(&path);
}
}
}
#[cfg(test)]
mod test {
use crate::*;
use app::App;
#[test]
fn test_app_from_file() {
let app = App::from_file("E:/web/rust-web/fql_server/uestc/student-info.csv".to_string())
.unwrap();
println!("{:#?}", app);
}
#[test]
fn test_app_from() {
let mut app = App::from(
r"
,
year,month,day
2020,12,14
2019,05,12
2018,10,null
",
);
app.update(0, "day", FQLType::Int(15));
app.save_at("examples/save.fql");
println!("{:#?}", app);
}
}