diff --git a/Cargo.toml b/Cargo.toml index 8c6eecb..c487530 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,10 @@ default = ["console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2.84" -chrono = { version = "0.4.31", features = [ "clock" ] } +chrono = { version = "0.4.31", features = [ "clock", "serde" ] } +serde = { version = "1.0", features = [ "derive" ] } +serde_json = { version = "1.0", features = [ "std" ] } +serde_with = { version = "3.4.0", features = [ "std", "chrono_0_4", "json" ] } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..c6b68ef --- /dev/null +++ b/src/db.rs @@ -0,0 +1,37 @@ +pub mod json; + +use crate::models::*; +use std::io; +use serde_json; + +#[derive(Debug)] +pub enum Error { + Generic(String), + FsIo(String), + Json(String), +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Self::FsIo(e.to_string()) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self{ + Self::Json(e.to_string()) + } +} + +pub trait Storage { + fn add_category(&mut self, category: NewCategory) -> Result; + fn add_series(&mut self, category_id: i32, series: NewSeries) -> Result; + fn add_series_point(&mut self, category_id: i32, series_id: i32, series_point: NewSeriesPoint) -> Result; + //fn add_user(&mut self, id: i32, user: NewUser) -> Result; + fn get_category(&self, id: i32) -> Option; + //fn get_series(&self, id: i32) -> Result; + //fn get_user(&self, id: i32) -> Result; + //fn update_category(&mut self, id: i32, changeset: CategoryChangeset) -> Result<(), Error>; + //fn update_series(&mut self, id: i32, changeset: SeriesChangeset) -> Result<(), Error>; + //fn update_user(&mut self, id: i32, changeset: UserChangeset) -> Result<(), Error>; +} diff --git a/src/db/json.rs b/src/db/json.rs new file mode 100644 index 0000000..0734acc --- /dev/null +++ b/src/db/json.rs @@ -0,0 +1,136 @@ +use super::*; + +pub struct JsonDb { + pub value: String, +} + +/// JsonDb is single user. +impl JsonDb { + pub fn new(value: Option) -> Self { + match value { + Some(value) => JsonDb { value }, + None => JsonDb { value: String::from("[]") }, + } + } + + fn load(&self) -> Result, Error> { + match serde_json::from_str::>(&self.value) { + Ok(d) => Ok(d), + Err(e) => Err(Error::from(e)), + } + } + + fn save(&mut self, data: Vec) -> Result<(), Error> { + let data = serde_json::to_string_pretty(&data); + match data { + Ok(d) => { + self.value = d; + Ok(()) + }, + Err(e) => Err(Error::from(e)), + } + } +} + +impl Storage for JsonDb { + fn add_category(&mut self, category: NewCategory) -> Result { + let mut max_id = 1; + let mut data = self.load()?; + for cat in data.iter().clone() { + if cat.id > max_id { + max_id = cat.id + } + } + let created = Category::new(max_id+1, category); + data.push(created); + self.save(data)?; + + Ok(max_id + 1) + } + + fn add_series(&mut self, category_id: i32, series: NewSeries) -> Result { + let mut max_id = 1; + let mut cat: Option<&mut Category> = None; + + let mut data = self.load()?; + for cat_opt in data.iter_mut() { + if cat_opt.id == category_id { + cat = Some(cat_opt); + break; + } + } + + if let None = cat { + return Err(Error::Generic ("Unable to find category".to_owned())); + } + let cat = cat.unwrap(); + + + for series in cat.series.iter().clone() { + if series.id > max_id { + max_id = series.id; + } + } + + let created = Series::new(max_id+1, series); + cat.series.push(created); + self.save(data)?; + + Ok(max_id + 1) + } + + fn add_series_point(&mut self, category_id: i32, series_id: i32, series_point: NewSeriesPoint) -> Result { + let mut max_id = 1; + let mut cat: Option<&mut Category> = None; + let mut series: Option<&mut Series> = None; + + let mut data = self.load()?; + for cat_opt in data.iter_mut() { + if cat_opt.id == category_id { + cat = Some(cat_opt); + break; + } + } + + if let None = cat { + return Err(Error::Generic ("Unable to find category".to_owned())); + } + let cat = cat.unwrap(); + + for series_opt in cat.series.iter_mut() { + if series_opt.id == series_id { + series = Some(series_opt); + break; + } + } + + if let None = series { + return Err(Error::Generic ("Unable to find series".to_owned())); + } + let series: &mut Series = series.unwrap(); + + for point in series.points.iter().clone() { + if point.id > max_id { + max_id = point.id; + } + } + + let created = SeriesPoint::new(max_id + 1, series_point); + series.points.push(created); + self.save(data)?; + + Ok(max_id + 1) + } + + fn get_category(&self, id: i32) -> Option { + let data = self.load().ok()?; + + for cat_opt in data { + if cat_opt.id == id { + return Some(cat_opt); + } + } + + None + } +} diff --git a/src/lib.rs b/src/lib.rs index 16bcb9b..8be5d85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod utils; pub mod models; +pub mod db; use wasm_bindgen::prelude::*; diff --git a/src/models.rs b/src/models.rs index 55eb925..35b6a22 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,9 +1,11 @@ -pub mod category; -pub mod series; -pub mod series_point; -pub mod series_type; +mod category; +mod series; +mod series_point; +mod series_type; +mod user; pub use category::*; pub use series::*; pub use series_point::*; pub use series_type::*; +pub use user::*; diff --git a/src/models/category.rs b/src/models/category.rs index fb0c245..dc36e9b 100644 --- a/src/models/category.rs +++ b/src/models/category.rs @@ -1,18 +1,19 @@ use crate::models::Series; +use serde::{ Serialize, Deserialize }; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Category { - id: i32, - name: String, - series: Vec, + pub id: i32, + pub name: String, + pub series: Vec, } impl Category { - pub fn new(id: i32, name: String) -> Category { + pub fn new(id: i32, new: NewCategory) -> Category { Category { id, - name, - series: vec![], + name: new.name, + series: new.series, } } @@ -20,3 +21,13 @@ impl Category { self.series.push(series); } } + +pub struct CategoryChangeset { + pub name: Option, + pub series: Option>, +} + +pub struct NewCategory { + pub name: String, + pub series: Vec, +} diff --git a/src/models/series.rs b/src/models/series.rs index 1c61d88..aa3e043 100644 --- a/src/models/series.rs +++ b/src/models/series.rs @@ -1,23 +1,27 @@ use chrono; use super::series_point::SeriesPoint; +use serde::{ Serialize, Deserialize }; +use serde_with; -#[derive(Debug)] +#[serde_with::serde_as] +#[derive(Debug, Serialize, Deserialize)] pub struct Series { - id: i32, - name: String, - repeat: chrono::Duration, - good: bool, - points: Vec, + pub id: i32, + pub name: String, + #[serde_as(as = "serde_with::DurationSeconds")] + pub repeat: chrono::Duration, + pub good: bool, + pub points: Vec, } impl Series { - pub fn new(id: i32, name: String, repeat: chrono::Duration, good: bool) -> Series { + pub fn new(id: i32, series: NewSeries) -> Series { Series { id, - name, - repeat, - good, - points: vec![], + name: series.name, + repeat: series.repeat, + good: series.good, + points: series.points, } } @@ -25,3 +29,17 @@ impl Series { self.points.push(point); } } + +pub struct SeriesChangeset { + pub name: Option, + pub repeat: Option, + pub good: Option, + pub points: Option>, +} + +pub struct NewSeries { + pub name: String, + pub repeat: chrono::Duration, + pub good: bool, + pub points: Vec, +} diff --git a/src/models/series_point.rs b/src/models/series_point.rs index 55bfbd7..4eac7d3 100644 --- a/src/models/series_point.rs +++ b/src/models/series_point.rs @@ -1,19 +1,25 @@ use chrono; use super::series_type::SeriesType; +use serde::{ Serialize, Deserialize }; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct SeriesPoint { - id: i32, - timestamp: chrono::NaiveDateTime, - value: SeriesType, + pub id: i32, + pub timestamp: chrono::NaiveDateTime, + pub value: SeriesType, } impl SeriesPoint { - pub fn new(id: i32, timestamp: chrono::NaiveDateTime, value: SeriesType) -> SeriesPoint { + pub fn new(id: i32, new: NewSeriesPoint) -> SeriesPoint { SeriesPoint { id, - timestamp, - value + timestamp: new.timestamp, + value: new.value, } } } + +pub struct NewSeriesPoint { + pub timestamp: chrono::NaiveDateTime, + pub value: SeriesType, +} diff --git a/src/models/series_type.rs b/src/models/series_type.rs index 26a13be..64cf626 100644 --- a/src/models/series_type.rs +++ b/src/models/series_type.rs @@ -1,4 +1,6 @@ -#[derive(Debug)] +use serde::{ Serialize, Deserialize }; + +#[derive(Debug, Serialize, Deserialize)] pub enum SeriesType { Bool(bool), Count(u32), diff --git a/src/models/user.rs b/src/models/user.rs new file mode 100644 index 0000000..1366763 --- /dev/null +++ b/src/models/user.rs @@ -0,0 +1,18 @@ +use serde::{ Serialize, Deserialize }; + +#[derive(Debug, Serialize, Deserialize)] +pub struct User { + id: i32, + name: String, + email: String, +} + +pub struct UserChangeset { + name: Option, + email: Option, +} + +pub struct NewUser { + name: String, + email: String, +}