clean code with clippy

This commit is contained in:
philipp 2023-07-25 13:22:11 +02:00
parent 3765541674
commit a7789af713
10 changed files with 243 additions and 463 deletions

View File

@ -1,3 +1,4 @@
use rocket::FromForm;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, SqlitePool};
@ -18,36 +19,46 @@ pub struct Boat {
external: bool, external: bool,
} }
#[derive(FromForm)]
pub struct BoatToAdd<'r> {
pub name: &'r str,
pub amount_seats: i64,
pub year_built: Option<i64>,
pub boatbuilder: Option<&'r str>,
pub default_shipmaster_only_steering: bool,
pub skull: bool,
pub external: bool,
pub location_id: Option<i64>,
pub owner: Option<i64>,
}
#[derive(FromForm)]
pub struct BoatToUpdate<'r> {
pub id: i32,
pub name: &'r str,
pub amount_seats: i64,
pub year_built: Option<i64>,
pub boatbuilder: Option<&'r str>,
pub default_shipmaster_only_steering: bool,
pub skull: bool,
pub external: bool,
pub location_id: Option<i64>,
pub owner: Option<i64>,
}
impl Boat { impl Boat {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> { pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, skull, external
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, skull, external FROM boat
FROM boat WHERE id like ?",
WHERE id like ?
",
id id
) )
.fetch_one(db) .fetch_one(db)
.await .await
.ok() .ok()
} }
//
// pub async fn find_by_name(db: &SqlitePool, name: &str) -> Option<Self> {
// sqlx::query_as!(
// User,
// "
//SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access
//FROM user
//WHERE name like ?
// ",
// name
// )
// .fetch_one(db)
// .await
// .ok()
// }
pub async fn is_locked(&self, db: &SqlitePool) -> bool { pub async fn is_locked(&self, db: &SqlitePool) -> bool {
sqlx::query!("SELECT * FROM boat_damage WHERE boat_id=? AND lock_boat=true AND user_id_verified is null", self.id).fetch_optional(db).await.unwrap().is_some() sqlx::query!("SELECT * FROM boat_damage WHERE boat_id=? AND lock_boat=true AND user_id_verified is null", self.id).fetch_optional(db).await.unwrap().is_some()
@ -78,58 +89,35 @@ ORDER BY amount_seats DESC
.unwrap() //TODO: fixme .unwrap() //TODO: fixme
} }
pub async fn create( pub async fn create(db: &SqlitePool, boat: BoatToAdd<'_>) -> bool {
db: &SqlitePool,
name: &str,
amount_seats: i64,
year_built: Option<i64>,
boatbuilder: Option<&str>,
default_shipmaster_only_steering: bool,
skull: bool,
external: bool,
location_id: Option<i64>,
owner: Option<i64>,
) -> bool {
sqlx::query!( sqlx::query!(
"INSERT INTO boat(name, amount_seats, year_built, boatbuilder, default_shipmaster_only_steering, skull, external, location_id, owner) VALUES (?,?,?,?,?,?,?,?,?)", "INSERT INTO boat(name, amount_seats, year_built, boatbuilder, default_shipmaster_only_steering, skull, external, location_id, owner) VALUES (?,?,?,?,?,?,?,?,?)",
name, boat.name,
amount_seats, boat.amount_seats,
year_built, boat.year_built,
boatbuilder, boat.boatbuilder,
default_shipmaster_only_steering, boat.default_shipmaster_only_steering,
skull, boat.skull,
external, boat.external,
location_id, boat.location_id,
owner boat.owner
) )
.execute(db) .execute(db)
.await.is_ok() .await.is_ok()
} }
pub async fn update( pub async fn update(&self, db: &SqlitePool, boat: BoatToUpdate<'_>) -> bool {
&self,
db: &SqlitePool,
name: &str,
amount_seats: i64,
year_built: Option<i64>,
boatbuilder: Option<&str>,
default_shipmaster_only_steering: bool,
skull: bool,
external: bool,
location_id: Option<i64>,
owner: Option<i64>,
) -> bool {
sqlx::query!( sqlx::query!(
"UPDATE boat SET name=?, amount_seats=?, year_built=?, boatbuilder=?, default_shipmaster_only_steering=?, skull=?, external=?, location_id=?, owner=? WHERE id=?", "UPDATE boat SET name=?, amount_seats=?, year_built=?, boatbuilder=?, default_shipmaster_only_steering=?, skull=?, external=?, location_id=?, owner=? WHERE id=?",
name, boat.name,
amount_seats, boat.amount_seats,
year_built, boat.year_built,
boatbuilder, boat.boatbuilder,
default_shipmaster_only_steering, boat.default_shipmaster_only_steering,
skull, boat.skull,
external, boat.external,
location_id, boat.location_id,
owner, boat.owner,
self.id self.id
) )
.execute(db) .execute(db)
@ -141,13 +129,16 @@ ORDER BY amount_seats DESC
sqlx::query!("DELETE FROM boat WHERE id=?", self.id) sqlx::query!("DELETE FROM boat WHERE id=?", self.id)
.execute(db) .execute(db)
.await .await
.unwrap(); //Okay, because we can only create a User of a valid id .unwrap(); //Okay, because we can only create a Boat of a valid id
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{model::boat::Boat, testdb}; use crate::{
model::boat::{Boat, BoatToAdd},
testdb,
};
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -179,15 +170,17 @@ mod test {
assert_eq!( assert_eq!(
Boat::create( Boat::create(
&pool, &pool,
"new-boat-name".into(), BoatToAdd {
42, name: "new-boat-name".into(),
None, amount_seats: 42,
"Best Boatbuilder".into(), year_built: None,
true, boatbuilder: "Best Boatbuilder".into(),
true, default_shipmaster_only_steering: true,
false, skull: true,
Some(1), external: false,
None location_id: Some(1),
owner: None
}
) )
.await, .await,
true true
@ -201,15 +194,17 @@ mod test {
assert_eq!( assert_eq!(
Boat::create( Boat::create(
&pool, &pool,
"Haichenbach".into(), BoatToAdd {
42, name: "Haichenbach".into(),
None, amount_seats: 42,
"Best Boatbuilder".into(), year_built: None,
true, boatbuilder: "Best Boatbuilder".into(),
true, default_shipmaster_only_steering: true,
false, skull: true,
Some(1), external: false,
None location_id: Some(1),
owner: None
}
) )
.await, .await,
false false

View File

@ -24,20 +24,14 @@ impl Location {
} }
pub async fn all(db: &SqlitePool) -> Vec<Self> { pub async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!( sqlx::query_as!(Self, "SELECT id, name FROM location")
Self, .fetch_all(db)
" .await
SELECT id, name .unwrap() //TODO: fixme
FROM location
"
)
.fetch_all(db)
.await
.unwrap() //TODO: fixme
} }
pub async fn create(db: &SqlitePool, name: &str) -> bool { pub async fn create(db: &SqlitePool, name: &str) -> bool {
sqlx::query!("INSERT INTO location(name) VALUES (?)", name,) sqlx::query!("INSERT INTO location(name) VALUES (?)", name)
.execute(db) .execute(db)
.await .await
.is_ok() .is_ok()

View File

@ -1,6 +1,7 @@
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use rocket::FromForm;
use serde::Serialize; use serde::Serialize;
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{boat::Boat, rower::Rower, user::User}; use super::{boat::Boat, rower::Rower, user::User};
@ -19,6 +20,29 @@ pub struct Logbook {
pub logtype: Option<i64>, pub logtype: Option<i64>,
} }
#[derive(FromForm)]
pub struct LogToAdd {
pub boat_id: i32,
pub shipmaster: i64,
pub shipmaster_only_steering: bool,
pub departure: String,
pub arrival: Option<String>,
pub destination: Option<String>,
pub distance_in_km: Option<i64>,
pub comments: Option<String>,
pub logtype: Option<i64>,
pub rower: Vec<i64>,
}
#[derive(FromForm)]
pub struct LogToFinalize {
pub destination: String,
pub distance_in_km: i64,
pub comments: Option<String>,
pub logtype: Option<i64>,
pub rower: Vec<i64>,
}
#[derive(Serialize)] #[derive(Serialize)]
pub struct LogbookWithBoatAndRowers { pub struct LogbookWithBoatAndRowers {
#[serde(flatten)] #[serde(flatten)]
@ -56,21 +80,6 @@ impl Logbook {
.ok() .ok()
} }
// pub async fn find_by_name(db: &SqlitePool, name: &str) -> Option<Self> {
// sqlx::query_as!(
// User,
// "
//SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access
//FROM user
//WHERE name like ?
// ",
// name
// )
// .fetch_one(db)
// .await
// .ok()
// }
//
pub async fn on_water(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> { pub async fn on_water(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> {
let logs = sqlx::query_as!( let logs = sqlx::query_as!(
Logbook, Logbook,
@ -123,20 +132,8 @@ impl Logbook {
ret ret
} }
pub async fn create( pub async fn create(db: &SqlitePool, log: LogToAdd) -> Result<(), LogbookCreateError> {
db: &SqlitePool, let boat = match Boat::find_by_id(db, log.boat_id).await {
boat_id: i32,
shipmaster: i64,
shipmaster_only_steering: bool,
departure: NaiveDateTime,
arrival: Option<NaiveDateTime>,
destination: Option<String>,
distance_in_km: Option<i64>,
comments: Option<String>,
logtype: Option<i64>,
rower: Vec<i64>,
) -> Result<(), LogbookCreateError> {
let boat = match Boat::find_by_id(db, boat_id).await {
Some(b) => b, Some(b) => b,
None => { None => {
return Err(LogbookCreateError::BoatNotFound); return Err(LogbookCreateError::BoatNotFound);
@ -151,27 +148,44 @@ impl Logbook {
return Err(LogbookCreateError::BoatAlreadyOnWater); return Err(LogbookCreateError::BoatAlreadyOnWater);
} }
if rower.len() > boat.amount_seats as usize - 1 { if log.rower.len() > boat.amount_seats as usize - 1 {
return Err(LogbookCreateError::TooManyRowers( return Err(LogbookCreateError::TooManyRowers(
boat.amount_seats as usize, boat.amount_seats as usize,
rower.len() + 1, log.rower.len() + 1,
)); ));
} }
let mut tx = db.begin().await.unwrap();
let departure = NaiveDateTime::parse_from_str(&log.departure, "%Y-%m-%dT%H:%M").unwrap();
let arrival = log
.arrival
.map(|a| NaiveDateTime::parse_from_str(&a, "%Y-%m-%dT%H:%M").unwrap());
let inserted_row = sqlx::query!( let inserted_row = sqlx::query!(
"INSERT INTO logbook(boat_id, shipmaster, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype) VALUES (?,?,?,?,?,?,?,?,?) RETURNING id", "INSERT INTO logbook(boat_id, shipmaster, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype) VALUES (?,?,?,?,?,?,?,?,?) RETURNING id",
boat_id, shipmaster, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype log.boat_id,
log.shipmaster,
log.shipmaster_only_steering,
departure,
arrival,
log.destination,
log.distance_in_km,
log.comments,
log.logtype
) )
.fetch_one(db) .fetch_one(&mut tx)
.await.unwrap(); .await.unwrap();
for rower in &rower { for rower in &log.rower {
Rower::create(db, inserted_row.id, *rower).await; Rower::create(&mut tx, inserted_row.id, *rower).await;
} }
tx.commit().await.unwrap();
Ok(()) Ok(())
} }
async fn remove_rowers(&self, db: &SqlitePool) { async fn remove_rowers(&self, db: &mut Transaction<'_, Sqlite>) {
sqlx::query!("DELETE FROM rower WHERE logbook_id=?", self.id) sqlx::query!("DELETE FROM rower WHERE logbook_id=?", self.id)
.execute(db) .execute(db)
.await .await
@ -182,11 +196,7 @@ impl Logbook {
&self, &self,
db: &SqlitePool, db: &SqlitePool,
user: &User, user: &User,
destination: String, log: LogToFinalize,
distance_in_km: i64,
comments: Option<String>,
logtype: Option<i64>,
rower: Vec<i64>,
) -> Result<(), LogbookUpdateError> { ) -> Result<(), LogbookUpdateError> {
if user.id != self.shipmaster { if user.id != self.shipmaster {
return Err(LogbookUpdateError::NotYourEntry); return Err(LogbookUpdateError::NotYourEntry);
@ -194,35 +204,37 @@ impl Logbook {
let boat = Boat::find_by_id(db, self.boat_id as i32).await.unwrap(); //ok let boat = Boat::find_by_id(db, self.boat_id as i32).await.unwrap(); //ok
if rower.len() > boat.amount_seats as usize - 1 { if log.rower.len() > boat.amount_seats as usize - 1 {
return Err(LogbookUpdateError::TooManyRowers( return Err(LogbookUpdateError::TooManyRowers(
boat.amount_seats as usize, boat.amount_seats as usize,
rower.len() + 1, log.rower.len() + 1,
)); ));
} }
//TODO: check current date
let arrival = format!("{}", chrono::offset::Local::now().format("%Y-%m-%d %H:%M")); let arrival = format!("{}", chrono::offset::Local::now().format("%Y-%m-%d %H:%M"));
let mut tx = db.begin().await.unwrap();
sqlx::query!( sqlx::query!(
"UPDATE logbook SET destination=?, distance_in_km=?, comments=?, logtype=?, arrival=? WHERE id=?", "UPDATE logbook SET destination=?, distance_in_km=?, comments=?, logtype=?, arrival=? WHERE id=?",
destination, log.destination,
distance_in_km, log.distance_in_km,
comments, log.comments,
logtype, log.logtype,
arrival, arrival,
self.id self.id
) )
.execute(db) .execute(&mut tx)
.await.unwrap(); //TODO: fixme .await.unwrap(); //TODO: fixme
self.remove_rowers(db).await; self.remove_rowers(&mut tx).await;
for rower in &rower { for rower in &log.rower {
Rower::create(db, self.id, *rower).await; Rower::create(&mut tx, self.id, *rower).await;
} }
tx.commit().await.unwrap();
Ok(()) Ok(())
} }

View File

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{logbook::Logbook, user::User}; use super::{logbook::Logbook, user::User};
@ -10,21 +10,6 @@ pub struct Rower {
} }
impl Rower { impl Rower {
//pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
// sqlx::query_as!(
// Self,
// "
//SELECT id,boat_id,shipmaster,shipmaster_only_steering,departure,arrival,destination,distance_in_km,comments,logtype
//FROM logbook
//WHERE id like ?
// ",
// id
// )
// .fetch_one(db)
// .await
// .ok()
//}
pub async fn for_log(db: &SqlitePool, log: &Logbook) -> Vec<User> { pub async fn for_log(db: &SqlitePool, log: &Logbook) -> Vec<User> {
sqlx::query_as!( sqlx::query_as!(
User, User,
@ -40,9 +25,7 @@ impl Rower {
.unwrap() .unwrap()
} }
pub async fn create(db: &SqlitePool, logbook_id: i64, rower_id: i64) { pub async fn create(db: &mut Transaction<'_, Sqlite>, logbook_id: i64, rower_id: i64) {
//Check if boat is not locked
//Check if boat is already on water
let _ = sqlx::query!( let _ = sqlx::query!(
"INSERT INTO rower(logbook_id, rower_id) VALUES (?,?)", "INSERT INTO rower(logbook_id, rower_id) VALUES (?,?)",
logbook_id, logbook_id,
@ -52,83 +35,4 @@ impl Rower {
.await .await
.unwrap(); .unwrap();
} }
// pub async fn delete(&self, db: &SqlitePool) {
// sqlx::query!("DELETE FROM boat WHERE id=?", self.id)
// .execute(db)
// .await
// .unwrap(); //Okay, because we can only create a User of a valid id
// }
} }
//
//#[cfg(test)]
//mod test {
// use crate::{model::boat::Boat, testdb};
//
// use sqlx::SqlitePool;
//
// #[sqlx::test]
// fn test_find_correct_id() {
// let pool = testdb!();
// let boat = Boat::find_by_id(&pool, 1).await.unwrap();
// assert_eq!(boat.id, 1);
// }
//
// #[sqlx::test]
// fn test_find_wrong_id() {
// let pool = testdb!();
// let boat = Boat::find_by_id(&pool, 1337).await;
// assert!(boat.is_none());
// }
//
// #[sqlx::test]
// fn test_all() {
// let pool = testdb!();
// let res = Boat::all(&pool).await;
// assert!(res.len() > 3);
// }
//
// #[sqlx::test]
// fn test_succ_create() {
// let pool = testdb!();
//
// assert_eq!(
// Boat::create(
// &pool,
// "new-boat-name".into(),
// 42,
// None,
// "Best Boatbuilder".into(),
// true,
// true,
// false,
// Some(1),
// None
// )
// .await,
// true
// );
// }
//
// #[sqlx::test]
// fn test_duplicate_name_create() {
// let pool = testdb!();
//
// assert_eq!(
// Boat::create(
// &pool,
// "Haichenbach".into(),
// 42,
// None,
// "Best Boatbuilder".into(),
// true,
// true,
// false,
// Some(1),
// None
// )
// .await,
// false
// );
// }
//}

View File

@ -1,17 +1,31 @@
use chrono::NaiveDate; use chrono::NaiveDate;
use rocket::FromForm;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, SqlitePool};
#[derive(FromRow, Debug, Serialize, Deserialize)] #[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct TripDetails { pub struct TripDetails {
pub id: i64, pub id: i64,
planned_starting_time: String, pub planned_starting_time: String,
max_people: i64, pub max_people: i64,
day: String, pub day: String,
notes: Option<String>, pub notes: Option<String>,
pub allow_guests: bool, pub allow_guests: bool,
trip_type_id: Option<i64>, pub trip_type_id: Option<i64>,
always_show: bool, 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<i64>,
pub allow_guests: bool,
pub always_show: bool,
} }
impl TripDetails { impl TripDetails {
@ -31,25 +45,16 @@ WHERE id like ?
} }
/// Creates a new entry in `trip_details` and returns its id. /// Creates a new entry in `trip_details` and returns its id.
pub async fn create( pub async fn create(db: &SqlitePool, tripdetails: TripDetailsToAdd<'_>) -> i64 {
db: &SqlitePool,
planned_starting_time: &str,
max_people: i32,
day: &str,
notes: Option<&str>,
allow_guests: bool,
trip_type_id: Option<i64>,
always_show: bool,
) -> i64 {
let query = sqlx::query!( let query = sqlx::query!(
"INSERT INTO trip_details(planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show) VALUES(?, ?, ?, ?, ?, ?, ?)" , "INSERT INTO trip_details(planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show) VALUES(?, ?, ?, ?, ?, ?, ?)" ,
planned_starting_time, tripdetails.planned_starting_time,
max_people, tripdetails.max_people,
day, tripdetails.day,
notes, tripdetails.notes,
allow_guests, tripdetails.allow_guests,
trip_type_id, tripdetails.trip_type,
always_show tripdetails.always_show
) )
.execute(db) .execute(db)
.await .await
@ -87,7 +92,7 @@ ORDER BY day;",
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::testdb; use crate::{model::tripdetails::TripDetailsToAdd, testdb};
use super::TripDetails; use super::TripDetails;
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -113,13 +118,15 @@ mod test {
assert_eq!( assert_eq!(
TripDetails::create( TripDetails::create(
&pool, &pool,
"10:00".into(), TripDetailsToAdd {
2, planned_starting_time: "10:00".into(),
"1970-01-01".into(), max_people: 2,
None, day: "1970-01-01".into(),
false, notes: None,
None, allow_guests: false,
false trip_type: None,
always_show: false
}
) )
.await, .await,
3, 3,
@ -127,13 +134,15 @@ mod test {
assert_eq!( assert_eq!(
TripDetails::create( TripDetails::create(
&pool, &pool,
"10:00".into(), TripDetailsToAdd {
2, planned_starting_time: "10:00".into(),
"1970-01-01".into(), max_people: 2,
None, day: "1970-01-01".into(),
false, notes: None,
None, allow_guests: false,
false trip_type: None,
always_show: false
}
) )
.await, .await,
4, 4,

View File

@ -148,15 +148,15 @@ ORDER BY last_access DESC
} }
pub async fn login(db: &SqlitePool, name: &str, pw: &str) -> Result<Self, LoginError> { pub async fn login(db: &SqlitePool, name: &str, pw: &str) -> Result<Self, LoginError> {
Log::create(db, format!("User '{name}' is trying to login...").into()).await; Log::create(db, format!("User '{name}' is trying to login...")).await;
let name = name.trim(); // just to make sure... let name = name.trim(); // just to make sure...
let Some(user) = User::find_by_name(db, name).await else { let Some(user) = User::find_by_name(db, name).await else {
Log::create(db, format!("Username ({name}) not found").into()).await; Log::create(db, format!("Username ({name}) not found")).await;
return Err(LoginError::InvalidAuthenticationCombo); // Username not found return Err(LoginError::InvalidAuthenticationCombo); // Username not found
}; };
if user.deleted { if user.deleted {
Log::create(db, format!("User ({name}) already deleted.").into()).await; Log::create(db, format!("User ({name}) already deleted.")).await;
return Err(LoginError::InvalidAuthenticationCombo); //User existed sometime ago; has return Err(LoginError::InvalidAuthenticationCombo); //User existed sometime ago; has
//been deleted //been deleted
} }
@ -165,10 +165,10 @@ ORDER BY last_access DESC
Some(user_pw) => { Some(user_pw) => {
let password_hash = &Self::get_hashed_pw(pw); let password_hash = &Self::get_hashed_pw(pw);
if password_hash == user_pw { if password_hash == user_pw {
Log::create(db, format!("User {name} successfully logged in").into()).await; Log::create(db, format!("User {name} successfully logged in")).await;
return Ok(user); return Ok(user);
} }
Log::create(db, format!("User {name} supplied the wrong PW").into()).await; Log::create(db, format!("User {name} supplied the wrong PW")).await;
Err(LoginError::InvalidAuthenticationCombo) Err(LoginError::InvalidAuthenticationCombo)
} }
None => { None => {

View File

@ -1,5 +1,5 @@
use crate::model::{ use crate::model::{
boat::Boat, boat::{Boat, BoatToAdd, BoatToUpdate},
location::Location, location::Location,
user::{AdminUser, User}, user::{AdminUser, User},
}; };
@ -8,7 +8,7 @@ use rocket::{
get, post, get, post,
request::FlashMessage, request::FlashMessage,
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, FromForm, Route, State, routes, Route, State,
}; };
use rocket_dyn_templates::{tera::Context, Template}; use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -50,96 +50,39 @@ async fn delete(db: &State<SqlitePool>, _admin: AdminUser, boat: i32) -> Flash<R
} }
} }
#[derive(FromForm)]
struct BoatEditForm<'r> {
id: i32,
name: &'r str,
amount_seats: i64,
year_built: Option<i64>,
boatbuilder: Option<&'r str>,
default_shipmaster_only_steering: bool,
skull: bool,
external: bool,
location_id: Option<i64>,
owner: Option<i64>,
}
#[post("/boat", data = "<data>")] #[post("/boat", data = "<data>")]
async fn update( async fn update(
db: &State<SqlitePool>, db: &State<SqlitePool>,
data: Form<BoatEditForm<'_>>, data: Form<BoatToUpdate<'_>>,
_admin: AdminUser, _admin: AdminUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let boat = Boat::find_by_id(db, data.id).await; let boat = Boat::find_by_id(db, data.id).await;
let Some(boat) = boat else { let Some(boat) = boat else {
return Flash::error( return Flash::error(
Redirect::to("/admin/boat"), Redirect::to("/admin/boat"),
format!("Boat with ID {} does not exist!", data.id), "Boat does not exist!",
) )
}; };
if !boat if !boat.update(db, data.into_inner()).await {
.update( return Flash::error(Redirect::to("/admin/boat"), "Boat could not be updated!");
db,
data.name,
data.amount_seats,
data.year_built,
data.boatbuilder,
data.default_shipmaster_only_steering,
data.skull,
data.external,
data.location_id,
data.owner,
)
.await
{
return Flash::error(
Redirect::to("/admin/boat"),
format!("Boat with ID {} could not be updated!", data.id),
);
} }
Flash::success(Redirect::to("/admin/boat"), "Successfully updated boat") Flash::success(Redirect::to("/admin/boat"), "Successfully updated boat")
} }
#[derive(FromForm)]
struct BoatAddForm<'r> {
name: &'r str,
amount_seats: i64,
year_built: Option<i64>,
boatbuilder: Option<&'r str>,
default_shipmaster_only_steering: bool,
skull: bool,
external: bool,
location_id: Option<i64>,
owner: Option<i64>,
}
#[post("/boat/new", data = "<data>")] #[post("/boat/new", data = "<data>")]
async fn create( async fn create(
db: &State<SqlitePool>, db: &State<SqlitePool>,
data: Form<BoatAddForm<'_>>, data: Form<BoatToAdd<'_>>,
_admin: AdminUser, _admin: AdminUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
if Boat::create( if Boat::create(db, data.into_inner()).await {
db,
data.name,
data.amount_seats,
data.year_built,
data.boatbuilder,
data.default_shipmaster_only_steering,
data.skull,
data.external,
data.location_id,
data.owner,
)
.await
{
Flash::success(Redirect::to("/admin/boat"), "Successfully created boat") Flash::success(Redirect::to("/admin/boat"), "Successfully created boat")
} else { } else {
Flash::error( Flash::error(
Redirect::to("/admin/boat"), Redirect::to("/admin/boat"),
format!("Error while creating boat {} in DB", data.name), "Error while creating the boat in DB",
) )
} }
} }

View File

@ -4,22 +4,22 @@ use rocket::{
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, FromForm, Route, State, routes, FromForm, Route, State,
}; };
use serde::Serialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::{planned_event::PlannedEvent, tripdetails::TripDetails, user::AdminUser}; use crate::model::{
planned_event::PlannedEvent,
tripdetails::{TripDetails, TripDetailsToAdd},
user::AdminUser,
};
//TODO: add constraints (e.g. planned_amount_cox > 0) //TODO: add constraints (e.g. planned_amount_cox > 0)
#[derive(FromForm)] #[derive(FromForm, Serialize)]
struct AddPlannedEventForm<'r> { struct AddPlannedEventForm<'r> {
day: &'r str,
name: &'r str, name: &'r str,
planned_amount_cox: i32, planned_amount_cox: i32,
allow_guests: bool, #[serde(flatten)]
planned_starting_time: &'r str, tripdetails: TripDetailsToAdd<'r>,
max_people: i32,
always_show: bool,
notes: Option<&'r str>,
trip_type: Option<i64>,
} }
#[post("/planned-event", data = "<data>")] #[post("/planned-event", data = "<data>")]
@ -28,17 +28,9 @@ async fn create(
data: Form<AddPlannedEventForm<'_>>, data: Form<AddPlannedEventForm<'_>>,
_admin: AdminUser, _admin: AdminUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let trip_details_id = TripDetails::create( let data = data.into_inner();
db,
data.planned_starting_time, let trip_details_id = TripDetails::create(db, data.tripdetails).await;
data.max_people,
data.day,
data.notes,
data.allow_guests,
data.trip_type,
data.always_show,
)
.await;
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc. we let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc. we
//just created //just created

View File

@ -10,52 +10,29 @@ use crate::model::{
log::Log, log::Log,
planned_event::PlannedEvent, planned_event::PlannedEvent,
trip::{CoxHelpError, Trip, TripDeleteError, TripUpdateError}, trip::{CoxHelpError, Trip, TripDeleteError, TripUpdateError},
tripdetails::TripDetails, tripdetails::{TripDetails, TripDetailsToAdd},
user::CoxUser, user::CoxUser,
}; };
#[derive(FromForm)]
struct AddTripForm<'r> {
day: String,
//TODO: properly parse `planned_starting_time`
planned_starting_time: &'r str,
#[field(validate = range(1..))]
max_people: i32,
notes: Option<&'r str>,
trip_type: Option<i64>,
allow_guests: bool,
always_show: bool,
}
#[post("/trip", data = "<data>")] #[post("/trip", data = "<data>")]
async fn create( async fn create(
db: &State<SqlitePool>, db: &State<SqlitePool>,
data: Form<AddTripForm<'_>>, data: Form<TripDetailsToAdd<'_>>,
cox: CoxUser, cox: CoxUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let trip_details_id = TripDetails::create( let trip_details_id = TripDetails::create(db, data.into_inner()).await;
db,
data.planned_starting_time,
data.max_people,
&data.day,
data.notes,
data.allow_guests,
data.trip_type,
data.always_show,
)
.await;
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
//created //created
Trip::new_own(db, &cox, trip_details).await; Trip::new_own(db, &cox, trip_details).await; //TODO: fix
Log::create( //Log::create(
db, // db,
format!( // format!(
"Cox {} created trip on {} @ {} for {} rower", // "Cox {} created trip on {} @ {} for {} rower",
cox.name, data.day, data.planned_starting_time, data.max_people, // cox.name, trip_details.day, trip_details.planned_starting_time, trip_details.max_people,
), // ),
) //)
.await; //.await;
Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich erstellt.") Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich erstellt.")
} }
@ -402,7 +379,7 @@ mod test {
login.dispatch().await; login.dispatch().await;
let req = client.get("/join/1"); let req = client.get("/join/1");
let response = req.dispatch().await; let _ = req.dispatch().await;
let req = client.get("/cox/join/1"); let req = client.get("/cox/join/1");
let response = req.dispatch().await; let response = req.dispatch().await;

View File

@ -1,10 +1,9 @@
use chrono::NaiveDateTime;
use rocket::{ use rocket::{
form::Form, form::Form,
get, post, get, post,
request::FlashMessage, request::FlashMessage,
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, FromForm, Route, State, routes, Route, State,
}; };
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -12,7 +11,7 @@ use tera::Context;
use crate::model::{ use crate::model::{
boat::Boat, boat::Boat,
logbook::{Logbook, LogbookCreateError}, logbook::{LogToAdd, LogToFinalize, Logbook, LogbookCreateError},
logtype::LogType, logtype::LogType,
user::{AdminUser, User}, user::{AdminUser, User},
}; };
@ -47,40 +46,15 @@ async fn index(
Template::render("log", context.into_json()) Template::render("log", context.into_json())
} }
#[derive(FromForm)]
struct LogAddForm {
boat_id: i32,
shipmaster: i64,
shipmaster_only_steering: bool,
departure: String,
arrival: Option<String>,
destination: Option<String>,
distance_in_km: Option<i64>,
comments: Option<String>,
logtype: Option<i64>,
rower: Vec<i64>,
}
#[post("/", data = "<data>")] #[post("/", data = "<data>")]
async fn create( async fn create(
db: &State<SqlitePool>, db: &State<SqlitePool>,
data: Form<LogAddForm>, data: Form<LogToAdd>,
_adminuser: AdminUser, _adminuser: AdminUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
match Logbook::create( match Logbook::create(
db, db,
data.boat_id, data.into_inner()
data.shipmaster,
data.shipmaster_only_steering,
NaiveDateTime::parse_from_str(&data.departure, "%Y-%m-%dT%H:%M").unwrap(), //TODO: fix
data.arrival
.clone()
.map(|a| NaiveDateTime::parse_from_str(&a, "%Y-%m-%dT%H:%M").unwrap()), //TODO: fix
data.destination.clone(), //TODO: fix
data.distance_in_km,
data.comments.clone(), //TODO: fix
data.logtype,
data.rower.clone(), //TODO: fix
) )
.await .await
{ {
@ -92,19 +66,10 @@ async fn create(
} }
} }
#[derive(FromForm)]
struct LogHomeForm {
destination: String,
distance_in_km: i64,
comments: Option<String>,
logtype: Option<i64>,
rower: Vec<i64>,
}
#[post("/<logbook_id>", data = "<data>")] #[post("/<logbook_id>", data = "<data>")]
async fn home( async fn home(
db: &State<SqlitePool>, db: &State<SqlitePool>,
data: Form<LogHomeForm>, data: Form<LogToFinalize>,
logbook_id: i32, logbook_id: i32,
_adminuser: AdminUser, _adminuser: AdminUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
@ -116,18 +81,7 @@ async fn home(
) )
}; };
match logbook match logbook.home(db, &_adminuser.user, data.into_inner()).await {
.home(
db,
&_adminuser.user,
data.destination.clone(), //TODO: fixme
data.distance_in_km,
data.comments.clone(), //TODO: fixme
data.logtype,
data.rower.clone(), //TODO: fixme
)
.await
{
Ok(_) => Flash::success(Redirect::to("/log"), "Successfully updated log"), Ok(_) => Flash::success(Redirect::to("/log"), "Successfully updated log"),
Err(_) => Flash::error( Err(_) => Flash::error(
Redirect::to("/log"), Redirect::to("/log"),