use chrono::NaiveDate; use rocket::FromForm; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, SqlitePool}; #[derive(FromRow, Debug, Serialize, Deserialize)] pub struct TripDetails { pub id: i64, pub planned_starting_time: String, pub max_people: i64, pub day: String, pub notes: Option, pub allow_guests: bool, pub trip_type_id: Option, pub always_show: bool, } #[derive(FromForm, Serialize)] pub struct TripDetailsToAdd<'r> { //TODO: properly parse `planned_starting_time` pub planned_starting_time: &'r str, pub max_people: i32, pub day: String, //#[field(validate = range(1..))] TODO: fixme pub notes: Option<&'r str>, pub trip_type: Option, pub allow_guests: bool, pub always_show: bool, } impl TripDetails { pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option { sqlx::query_as!( TripDetails, " SELECT id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show FROM trip_details WHERE id like ? ", id ) .fetch_one(db) .await .ok() } /// Creates a new entry in `trip_details` and returns its id. pub async fn create(db: &SqlitePool, tripdetails: TripDetailsToAdd<'_>) -> i64 { let query = sqlx::query!( "INSERT INTO trip_details(planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show) VALUES(?, ?, ?, ?, ?, ?, ?)" , tripdetails.planned_starting_time, tripdetails.max_people, tripdetails.day, tripdetails.notes, tripdetails.allow_guests, tripdetails.trip_type, tripdetails.always_show ) .execute(db) .await .unwrap(); //Okay, TripDetails can only be created if self.id exists query.last_insert_rowid() } pub async fn is_full(&self, db: &SqlitePool) -> bool { let amount_currently_registered = sqlx::query!( "SELECT COUNT(*) as count FROM user_trip WHERE trip_details_id = ?", self.id ) .fetch_one(db) .await .unwrap(); //TODO: fixme let amount_currently_registered = i64::from(amount_currently_registered.count); amount_currently_registered >= self.max_people } pub async fn pinned_days(db: &SqlitePool, amount_days_to_skip: i64) -> Vec { let query = format!( "SELECT DISTINCT day FROM trip_details WHERE always_show=true AND day > datetime('now' , '+{} days') ORDER BY day;", amount_days_to_skip ); let days: Vec = sqlx::query_scalar(&query).fetch_all(db).await.unwrap(); days.into_iter() .map(|a| NaiveDate::parse_from_str(&a, "%Y-%m-%d").unwrap()) .collect() } } #[cfg(test)] mod test { use crate::{model::tripdetails::TripDetailsToAdd, testdb}; use super::TripDetails; use sqlx::SqlitePool; #[sqlx::test] fn test_find_true() { let pool = testdb!(); assert!(TripDetails::find_by_id(&pool, 1).await.is_some()); } #[sqlx::test] fn test_find_false() { let pool = testdb!(); assert!(TripDetails::find_by_id(&pool, 1337).await.is_none()); } #[sqlx::test] fn test_create() { let pool = testdb!(); assert_eq!( TripDetails::create( &pool, TripDetailsToAdd { planned_starting_time: "10:00".into(), max_people: 2, day: "1970-01-01".into(), notes: None, allow_guests: false, trip_type: None, always_show: false } ) .await, 3, ); assert_eq!( TripDetails::create( &pool, TripDetailsToAdd { planned_starting_time: "10:00".into(), max_people: 2, day: "1970-01-01".into(), notes: None, allow_guests: false, trip_type: None, always_show: false } ) .await, 4, ); } #[sqlx::test] fn test_false_full() { let pool = testdb!(); let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap(); assert_eq!(trip_details.is_full(&pool).await, false); } #[sqlx::test] fn test_true_full() { //TODO: register user for trip_details = 1; check if is_full returns true } //TODO: add new tripdetails test with trip_type != None }