diff --git a/editoast/src/client/import_rolling_stock.rs b/editoast/src/client/import_rolling_stock.rs index 16999d09110..61928d77caf 100644 --- a/editoast/src/client/import_rolling_stock.rs +++ b/editoast/src/client/import_rolling_stock.rs @@ -9,12 +9,10 @@ use colored::Colorize as _; use editoast_models::DbConnectionPoolV2; use editoast_schemas::rolling_stock::RollingStock; use editoast_schemas::rolling_stock::TowedRollingStock; -use validator::ValidationErrorsKind; use crate::models::prelude::*; use crate::models::towed_rolling_stock::TowedRollingStockModel; use crate::models::RollingStockModel; -use crate::CliError; #[derive(Args, Debug)] #[command(about, long_about = "Import a rolling stock given a json file")] @@ -32,46 +30,24 @@ pub async fn import_rolling_stock( let rolling_stock: RollingStock = serde_json::from_reader(BufReader::new(rolling_stock_file))?; let rolling_stock: Changeset = rolling_stock.into(); - match rolling_stock.validate_imported_rolling_stock() { - Ok(()) => { - println!( - "🍞 Importing rolling stock {}", - rolling_stock - .name - .as_deref() - .unwrap_or("rolling stock without name") - .bold() - ); - let rolling_stock = rolling_stock - .locked(false) - .version(0) - .create(&mut db_pool.get().await?) - .await?; - println!( - "✅ Rolling stock {}[{}] saved!", - &rolling_stock.name.bold(), - &rolling_stock.id - ); - } - Err(e) => { - let mut error_message = "❌ Rolling stock was not created!".to_string(); - if let Some(ValidationErrorsKind::Field(field_errors)) = e.errors().get("__all__") { - for error in field_errors { - if &error.code == "electrical_power_startup_time" { - error_message.push_str( - "\nRolling stock is electrical, but electrical_power_startup_time is missing" - ); - } - if &error.code == "raise_pantograph_time" { - error_message.push_str( - "\nRolling stock is electrical, but raise_pantograph_time is missing" - ); - } - } - } - return Err(Box::new(CliError::new(2, error_message))); - } - }; + println!( + "🍞 Importing rolling stock {}", + rolling_stock + .name + .as_deref() + .unwrap_or("rolling stock without name") + .bold() + ); + let rolling_stock = rolling_stock + .locked(false) + .version(0) + .create(&mut db_pool.get().await?) + .await?; + println!( + "✅ Rolling stock {}[{}] saved!", + &rolling_stock.name.bold(), + &rolling_stock.id + ); } Ok(()) } diff --git a/editoast/src/models/rolling_stock_model.rs b/editoast/src/models/rolling_stock_model.rs index b8f59d838a9..c17f29f3c29 100644 --- a/editoast/src/models/rolling_stock_model.rs +++ b/editoast/src/models/rolling_stock_model.rs @@ -2,6 +2,7 @@ mod power_restrictions; use std::collections::HashMap; +use diesel_json::Json; use editoast_derive::Model; use editoast_schemas::rolling_stock::EffortCurves; use editoast_schemas::rolling_stock::EnergySource; @@ -13,10 +14,9 @@ use editoast_schemas::rolling_stock::RollingStockMetadata; use editoast_schemas::rolling_stock::RollingStockSupportedSignalingSystems; use power_restrictions::PowerRestriction; use serde::Deserialize; +use serde::Deserializer; use serde::Serialize; use utoipa::ToSchema; -use validator::ValidationError; -use validator::ValidationErrors; use crate::models::prelude::*; @@ -31,7 +31,7 @@ editoast_common::schemas! { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Model, ToSchema)] #[model(table = editoast_models::tables::rolling_stock)] #[model(gen(ops = crud, batch_ops = r, list))] -#[model(changeset(derive(Deserialize), public))] +#[model(changeset(public))] #[schema(as = RollingStock)] pub struct RollingStockModel { pub id: i64, @@ -75,50 +75,98 @@ pub struct RollingStockModel { pub supported_signaling_systems: RollingStockSupportedSignalingSystems, } -impl RollingStockModelChangeset { - pub fn validate_imported_rolling_stock(&self) -> std::result::Result<(), ValidationErrors> { - match &self.effort_curves { - Some(effort_curves) => validate_rolling_stock( - effort_curves, - self.electrical_power_startup_time.flatten(), - self.raise_pantograph_time.flatten(), - ) - .map_err(|e| { - let mut err = ValidationErrors::new(); - err.add("effort_curves", e); - err - }), - None => { - let mut err = ValidationErrors::new(); - err.add( - "effort_curves", - ValidationError::new("effort_curves is required"), - ); - Err(err) - } +impl<'de> Deserialize<'de> for RollingStockModelChangeset { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Inner { + pub railjson_version: Option, + pub name: Option, + pub effort_curves: Option>, + pub metadata: Option>>, + pub length: Option, + pub max_speed: Option, + pub startup_time: Option, + pub startup_acceleration: Option, + pub comfort_acceleration: Option, + pub const_gamma: Option, + pub etcs_brake_params: Option>>, + pub inertia_coefficient: Option, + pub base_power_class: Option>, + pub mass: Option, + pub rolling_resistance: Option>, + pub loading_gauge: Option, + pub power_restrictions: Option>>, + pub energy_sources: Option>>, + pub locked: Option, + pub electrical_power_startup_time: Option>, + pub raise_pantograph_time: Option>, + pub version: Option, + pub supported_signaling_systems: Option>>, + } + + let changeset = Inner::deserialize(deserializer)?; + + if changeset.effort_curves.is_none() { + return Err(serde::de::Error::custom("effort_curves is required")); } + + validate_rolling_stock::( + changeset.effort_curves.as_ref().unwrap(), + changeset.electrical_power_startup_time.flatten(), + changeset.raise_pantograph_time.flatten(), + )?; + + Ok(RollingStockModelChangeset { + railjson_version: changeset.railjson_version, + name: changeset.name, + effort_curves: changeset.effort_curves, + metadata: changeset.metadata, + length: changeset.length, + max_speed: changeset.max_speed, + startup_time: changeset.startup_time, + startup_acceleration: changeset.startup_acceleration, + comfort_acceleration: changeset.comfort_acceleration, + const_gamma: changeset.const_gamma, + etcs_brake_params: changeset.etcs_brake_params, + inertia_coefficient: changeset.inertia_coefficient, + base_power_class: changeset.base_power_class, + mass: changeset.mass, + rolling_resistance: changeset.rolling_resistance, + loading_gauge: changeset.loading_gauge, + power_restrictions: changeset.power_restrictions, + energy_sources: changeset.energy_sources, + locked: changeset.locked, + electrical_power_startup_time: changeset.electrical_power_startup_time, + raise_pantograph_time: changeset.raise_pantograph_time, + version: changeset.version, + supported_signaling_systems: changeset.supported_signaling_systems, + }) } } -pub fn validate_rolling_stock( +pub fn validate_rolling_stock<'de, D>( effort_curves: &EffortCurves, electrical_power_startup_time: Option, raise_pantograph_time: Option, -) -> std::result::Result<(), ValidationError> { +) -> Result<(), D::Error> +where + D: Deserializer<'de>, +{ if !effort_curves.is_electric() { return Ok(()); } if electrical_power_startup_time.is_none() { - let mut error = ValidationError::new("electrical_power_startup_time"); - error.message = - Some("electrical_power_startup_time is required for electric rolling stocks".into()); - return Err(error); + return Err(serde::de::Error::custom( + "electrical_power_startup_time is required for electric rolling stocks", + )); } if raise_pantograph_time.is_none() { - let mut error = ValidationError::new("raise_pantograph_time"); - error.message = - Some("raise_pantograph_time is required for electric rolling stocks".into()); - return Err(error); + return Err(serde::de::Error::custom( + "raise_pantograph_time is required for electric rolling stocks", + )); } Ok(()) } diff --git a/editoast/src/views/rolling_stock.rs b/editoast/src/views/rolling_stock.rs index 3285101baed..41816faceb6 100644 --- a/editoast/src/views/rolling_stock.rs +++ b/editoast/src/views/rolling_stock.rs @@ -31,7 +31,6 @@ use strum::Display; use thiserror::Error; use utoipa::IntoParams; use utoipa::ToSchema; -use validator::Validate; use crate::error::InternalError; use crate::error::Result; @@ -323,7 +322,6 @@ async fn create( if !authorized { return Err(AuthorizationError::Forbidden.into()); } - rolling_stock_form.validate()?; let conn = &mut db_pool.get().await?; let rolling_stock_name = rolling_stock_form.name.clone(); let rolling_stock_changeset: Changeset = rolling_stock_form.into(); @@ -361,7 +359,6 @@ async fn update( if !authorized { return Err(AuthorizationError::Forbidden.into()); } - rolling_stock_form.validate()?; let name = rolling_stock_form.name.clone(); let new_rolling_stock = db_pool diff --git a/editoast/src/views/rolling_stock/form.rs b/editoast/src/views/rolling_stock/form.rs index 32cacfef05e..2c54dc548f0 100644 --- a/editoast/src/views/rolling_stock/form.rs +++ b/editoast/src/views/rolling_stock/form.rs @@ -8,18 +8,18 @@ use editoast_schemas::rolling_stock::RollingStockMetadata; use editoast_schemas::rolling_stock::RollingStockSupportedSignalingSystems; use editoast_schemas::rolling_stock::ROLLING_STOCK_RAILJSON_VERSION; use serde::Deserialize; +use serde::Deserializer; use serde::Serialize; +use serde::Serializer; use utoipa::ToSchema; -use validator::Validate; -use validator::ValidationError; use crate::models::rolling_stock_model::validate_rolling_stock; use crate::models::Changeset; use crate::models::Model; use crate::models::RollingStockModel; -#[derive(Debug, Clone, Deserialize, Serialize, ToSchema, Validate)] -#[validate(schema(function = "validate_rolling_stock_form"))] +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] +#[serde(remote = "Self")] pub struct RollingStockForm { pub name: String, pub effort_curves: EffortCurves, @@ -52,6 +52,32 @@ pub struct RollingStockForm { pub metadata: Option, } +impl<'de> Deserialize<'de> for RollingStockForm { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let rolling_stock_form = RollingStockForm::deserialize(deserializer)?; + + validate_rolling_stock::( + &rolling_stock_form.effort_curves, + rolling_stock_form.electrical_power_startup_time, + rolling_stock_form.raise_pantograph_time, + )?; + + Ok(rolling_stock_form) + } +} + +impl Serialize for RollingStockForm { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + RollingStockForm::serialize(self, serializer) + } +} + impl From for Changeset { fn from(rolling_stock: RollingStockForm) -> Self { RollingStockModel::changeset() @@ -79,16 +105,6 @@ impl From for Changeset { } } -fn validate_rolling_stock_form( - rolling_stock_form: &RollingStockForm, -) -> std::result::Result<(), ValidationError> { - validate_rolling_stock( - &rolling_stock_form.effort_curves, - rolling_stock_form.electrical_power_startup_time, - rolling_stock_form.raise_pantograph_time, - ) -} - // Used in some tests where we import a rolling stock as a fixture #[cfg(test)] impl From for RollingStockForm {