use chrono::NaiveTime; use garde::Validate; use super::{ start_time_lies_before_end_time, Assignment, Availability, AvailabilityTime, Event, Function, }; #[derive(Validate)] #[garde(allow_unvalidated)] #[garde(context(AssignmentContext as ctx))] /// check before: event and availabillity and must exist and user of availabillity is in event location pub struct AssignmentChangeset { #[garde( custom(user_of_availability_has_function), custom(event_has_free_slot_for_function) )] pub function: Function, #[garde( custom(available_time_fits), custom(start_time_lies_before_end_time), custom(availabillity_not_already_assigned) )] pub time: (NaiveTime, NaiveTime), } pub struct AssignmentContext { pub event: Event, pub availabillity: Availability, pub user_function: Function, pub assignments_for_event: Vec, pub assignments_for_availabillity: Vec, } fn available_time_fits( value: &(NaiveTime, NaiveTime), context: &AssignmentContext, ) -> garde::Result { if let AvailabilityTime::Temporarily(start, end) = context.availabillity.time { if value.0 < start || value.1 > end { return Err(garde::Error::new( "time not made available can't be assigned", )); } } Ok(()) } fn user_of_availability_has_function( value: &Function, context: &AssignmentContext, ) -> garde::Result { if *value > context.user_function { return Err(garde::Error::new( "user has not the required function for this assignment", )); } Ok(()) } fn event_has_free_slot_for_function( _value: &Function, context: &AssignmentContext, ) -> garde::Result { let list: Vec<&Assignment> = context .assignments_for_event .iter() .filter(|a| { a.availabillity_id != context.availabillity.id && a.event_id != context.event.id }) .collect(); let a = list .iter() .filter(|a| a.function == Function::Posten) .count(); if match context.user_function { Function::Posten => a >= context.event.amount_of_posten as usize, Function::Fuehrungsassistent => context.event.voluntary_fuehrungsassistent && a >= 1, Function::Wachhabender => context.event.voluntary_wachhabender && a >= 1, } { return Err(garde::Error::new( "event already has enough assignments for this function", )); } Ok(()) } fn availabillity_not_already_assigned( value: &(NaiveTime, NaiveTime), context: &AssignmentContext, ) -> garde::Result { let list: Vec<&Assignment> = context .assignments_for_availabillity .iter() .filter(|a| { a.availabillity_id != context.availabillity.id && a.event_id != context.event.id }) .collect(); let has_start_time_during_assignment = |a: &Assignment| a.start_time >= value.0 && a.start_time <= value.1; let has_end_time_during_assignment = |a: &Assignment| a.end_time >= value.0 && a.end_time <= value.1; if list .iter() .any(|a| has_start_time_during_assignment(a) || has_end_time_during_assignment(a)) { return Err(garde::Error::new( "availabillity is already assigned for that time", )); } Ok(()) }