use crate::database::latest::trees::{categories, past_sessions}; use crate::routes::pages; use crate::status_json::StatusJson; use bincode::{deserialize, serialize}; use chrono::NaiveDateTime; use chrono::{Duration, Utc}; use rocket::http::Status; use rocket::request::{Form, FromForm}; use rocket::response::Redirect; use rocket::{post, uri, State}; use rocket_contrib::uuid::Uuid; use sled::Transactional; #[derive(FromForm)] pub struct NewCategory { name: String, color: String, } #[post("/create_category", data = "")] pub fn create_category( category: Form, db: State<'_, sled::Db>, ) -> Result { let category = category.into_inner(); let categories_tree = db.open_tree(categories::NAME)?; categories_tree.insert( serialize(&uuid::Uuid::new_v4())?, serialize(&categories::V { name: category.name, color: category.color, started: None, })?, )?; Ok(Status::Ok.into()) } #[post("/category//start_session")] pub fn start_session( category_uuid: Uuid, db: State<'_, sled::Db>, ) -> Result { toggle_category_session(category_uuid, true, db) } #[post("/category//end_session")] pub fn end_session(category_uuid: Uuid, db: State<'_, sled::Db>) -> Result { toggle_category_session(category_uuid, false, db) } #[post("/category//bump_session/minutes/")] pub fn bump_session( category_uuid: Uuid, minutes: i64, db: State<'_, sled::Db>, ) -> Result { let duration = Duration::minutes(minutes); let category_uuid_s = sled::IVec::from(serialize(&category_uuid.into_inner())?); let categories_tree = db.open_tree(categories::NAME)?; Ok((&categories_tree).transaction(|tx_categories| { match tx_categories.get(&category_uuid_s)? { None => return Ok(Err(Status::NotFound.into())), Some(data) => { let mut category: categories::V = deserialize(&data).unwrap(); match category.started.as_mut() { Some(started) => { *started -= duration; tx_categories.insert(&category_uuid_s, serialize(&category).unwrap())?; Ok(Ok(Redirect::to(uri!(pages::index)))) } None => { return Ok(Err(StatusJson::new( Status::BadRequest, "No active session", ))) } } } } })??) } pub fn toggle_category_session( category_uuid: Uuid, set_active: bool, db: State<'_, sled::Db>, ) -> Result { let category_uuid_s = sled::IVec::from(serialize(&category_uuid.into_inner())?); let categories_tree = db.open_tree(categories::NAME)?; let past_sessions_tree = db.open_tree(past_sessions::NAME)?; Ok((&categories_tree, &past_sessions_tree).transaction( |(tx_categories, tx_past_sessions)| { match tx_categories.get(&category_uuid_s)? { None => return Ok(Err(Status::NotFound)), Some(data) => { let mut category: categories::V = deserialize(&data).unwrap(); let now = Utc::now().naive_utc(); match (set_active, category.started.take()) { (false, Some(started)) => { // only save sessions longer than 5 minutes let duration = now - started; if duration > Duration::minutes(5) { let session_uuid = serialize(&uuid::Uuid::new_v4()).unwrap(); let past_session = past_sessions::V { category: category_uuid.into_inner(), started, ended: now, }; tx_past_sessions .insert(session_uuid, serialize(&past_session).unwrap())?; } } (true, None) => { category.started = Some(now); } _ => { // Category is already in the correct state return Ok(Ok(Status::Ok.into())); } } tx_categories.insert(&category_uuid_s, serialize(&category).unwrap())?; } } Ok(Ok(Status::Ok.into())) }, )??) } #[derive(Debug, FromForm)] pub struct EditSession { category: Uuid, started: String, ended: String, } #[post("/session//edit", data = "")] pub fn edit_session( session_uuid: Uuid, session: Form, db: State<'_, sled::Db>, ) -> Result { let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?); dbg!(&session); let session = past_sessions::V { category: session.category.into_inner(), started: NaiveDateTime::parse_from_str(&session.started, "%Y-%m-%d %H:%M")?, ended: NaiveDateTime::parse_from_str(&session.ended, "%Y-%m-%d %H:%M")?, }; if session.started >= session.ended { return Err(StatusJson::new( Status::BadRequest, "started must be earlier than ended", )); } db.open_tree(past_sessions::NAME)? .insert(session_uuid_s, serialize(&session)?)?; // FIXME: Uuid does not implement FromUriParam for some reason... File an issue? //Ok(Redirect::to(uri!(pages::session_edit: session_uuid))) Ok(Redirect::to(format!("/session/{}/edit", session_uuid))) } #[post("/session//delete")] pub fn delete_session(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result { let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?); let past_sessions_tree = db.open_tree(past_sessions::NAME)?; match past_sessions_tree.remove(session_uuid_s)? { Some(_) => Ok(Redirect::to(uri!(pages::history))), None => Err(Status::NotFound.into()), } // TODO: mark as deleted instead of removing // Ok(past_sessions_tree.transaction(|tx_past_sessions| { // match tx_past_sessions.get(&session_uuid_s)? { // None => return Ok(Err(Status::NotFound)), // Some(data) => { // let mut past_session: past_sessions::V = deserialize(&data).unwrap(); // } // } // Ok(Ok(Redirect::to(uri!(pages::history)))) // })??) }