#[macro_use] extern crate rocket; mod models; use std::collections::HashMap; use std::ops::Deref; use chrono::Duration; use chrono::Local; use chrono::NaiveDate; use rocket::fairing::AdHoc; use rocket::form; use rocket::form::ValueField; use rocket::http::Cookie; use rocket::http::CookieJar; use rocket::request; use rocket::request::FromRequest; use rocket::request::Outcome; use rocket::response::Redirect; use rocket::Request; use rocket::{form::Form, fs::FileServer, State}; use rocket_dyn_templates::context; use rocket_dyn_templates::Template; use sea_orm::ColumnTrait; use sea_orm::ModelTrait; use sea_orm::QueryFilter; use sea_orm::{ ActiveModelTrait, Database, DatabaseConnection, EntityTrait, Order, QueryOrder, Set, }; use serde::Serialize; use crate::models::{day, trip, user}; #[derive(Serialize, Debug)] struct TripWithUser { trip: trip::Model, user: user::Model, } impl TripWithUser { async fn new(trip: trip::Model, db: &DatabaseConnection) -> Self { Self { trip: trip.clone(), user: trip .find_related(user::Entity) .one(db) .await .unwrap() .unwrap(), } } } #[derive(Serialize, Debug)] struct DayWithTrips { day: day::Model, trips: Vec, } impl DayWithTrips { async fn new(day: day::Model, db: &DatabaseConnection) -> Self { let mut trips = Vec::new(); for trip in day.find_related(trip::Entity).all(db).await.unwrap() { trips.push(TripWithUser::new(trip, db).await); } Self { day, trips } } } #[derive(Serialize)] struct Name(user::Model); #[rocket::async_trait] impl<'r> FromRequest<'r> for Name { type Error = rocket::Error; async fn from_request(req: &'r Request<'_>) -> request::Outcome { match req.cookies().get("name") { Some(name) => { let db = req.guard::<&'r State>(); let name = name.value(); let user = find_or_create_user(name, db.await.unwrap().inner()).await; Outcome::Success(Name(user)) } None => Outcome::Forward(()), //No cookie set } } } #[get("/")] async fn index(db: &State, name: Name) -> Template { let days: Vec = day::Entity::find() .filter(day::Column::Day.gte(format!("{}", Local::now().format("%Y-%m-%d")))) // don't show stuff from the past .order_by(day::Column::Day, Order::Asc) .all(db.inner()) .await .unwrap(); let mut dwu = HashMap::new(); for day in days { dwu.insert( format!("{}", day.day.format("%Y-%m-%d")), DayWithTrips::new(day, db.inner()).await, ); } let mut next_days = Vec::new(); for i in 0..6 { next_days.push(Local::now() + Duration::days(i)); } Template::render("index", context! { dwu, next_days, name }) } #[get("/", rank = 2)] fn name() -> Template { Template::render("name", context! {}) } #[derive(FromForm)] struct NameForm(String); #[post("/setname", data = "")] fn setname(cookies: &CookieJar<'_>, name: Form) -> Redirect { cookies.add(Cookie::new("name", name.0.clone())); Redirect::to("/") } #[derive(Debug)] struct NaiveDateForm(NaiveDate); impl<'v> rocket::form::FromFormField<'v> for NaiveDateForm { fn from_value(field: ValueField<'v>) -> form::Result<'v, NaiveDateForm> { let naivedate = chrono::NaiveDate::parse_from_str(&field.value, "%Y-%m-%d").unwrap(); //TODO: //fixme Ok(NaiveDateForm(naivedate)) } } impl Deref for NaiveDateForm { type Target = NaiveDate; fn deref(&self) -> &Self::Target { &self.0 } } #[derive(FromForm, Debug)] struct DayForm { day: NaiveDateForm, #[field(validate = range(0..20))] planned_amount_cox: i32, planned_starting_time: Option, open_registration: bool, } #[put("/day", data = "")] async fn create(db: &State, day: Form) -> Redirect { let new_day = day::ActiveModel { day: Set(*day.day), planned_amount_cox: Set(day.planned_amount_cox), planned_starting_time: Set(day.planned_starting_time.clone()), open_registration: Set(day.open_registration), }; let day: Option = day::Entity::find_by_id(*day.day) .one(db.inner()) .await .unwrap(); match day { Some(_) => { new_day.update(db.inner()).await.unwrap(); //TODO: fixme } None => { new_day.insert(db.inner()).await.unwrap(); //TODO: fixme } } Redirect::to("/") } #[derive(FromForm)] struct RegisterForm { day: NaiveDateForm, name: String, } async fn find_or_create_user(name: &str, db: &DatabaseConnection) -> user::Model { let user = user::Entity::find() .filter(user::Column::Name.eq(name)) .one(db) .await .unwrap(); match user { Some(user) => user, None => { let user = user::ActiveModel { name: Set(name.clone().into()), ..Default::default() }; user.insert(db).await.unwrap() } } } #[put("/register", data = "")] async fn register(db: &State, register: Form) -> Redirect { let day = day::Entity::find_by_id(*register.day) .one(db.inner()) .await .unwrap() .expect("There's no trip on this date (yet)"); let user = find_or_create_user(®ister.name, db.inner()).await; let day = format!("{}", day.day.format("%Y-%m-%d")); let trip = trip::ActiveModel { day: Set(day), user_id: Set(user.id), ..Default::default() }; trip.insert(db.inner()).await.unwrap(); Redirect::to("/") } #[launch] async fn rocket() -> _ { rocket::build() .attach(Template::fairing()) .manage(Database::connect("sqlite://db.sqlite").await.unwrap()) .mount("/public", FileServer::from("static/")) .mount("/", routes![index, create, register, name, setname]) } //#[tokio::main] //async fn main() { // println!("Hello, world!"); // let db = Database::connect("sqlite://db.sqlite").await.unwrap(); // // let day = day::ActiveModel { // day: Set("2023-02-08".into()), // ..Default::default() // }; // // //day.insert(&db).await.unwrap(); // // let a: Vec = day::Entity::find().all(&db).await.unwrap(); // println!("{:?}", a); // // db.close().await.unwrap(); //}