Skip to content

Commit

Permalink
[wip] editoast: remove 'validator' use for RollingStockForm
Browse files Browse the repository at this point in the history
TODO:
* fix tests
* use Changeset<RollingStock> and #[serde(remote = "Self")] if possible
* maybe change error type returned by the free function
* remove any 'validator' use

Signed-off-by: Pierre-Etienne Bougué <[email protected]>
  • Loading branch information
bougue-pe committed Jan 9, 2025
1 parent c806a2f commit 090bcbc
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 93 deletions.
60 changes: 18 additions & 42 deletions editoast/src/client/import_rolling_stock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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<RollingStockModel> = 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(())
}
Expand Down
116 changes: 82 additions & 34 deletions editoast/src/models/rolling_stock_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;

Expand All @@ -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,
Expand Down Expand Up @@ -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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Inner {
pub railjson_version: Option<String>,
pub name: Option<String>,
pub effort_curves: Option<Json<EffortCurves>>,
pub metadata: Option<Json<Option<RollingStockMetadata>>>,
pub length: Option<f64>,
pub max_speed: Option<f64>,
pub startup_time: Option<f64>,
pub startup_acceleration: Option<f64>,
pub comfort_acceleration: Option<f64>,
pub const_gamma: Option<f64>,
pub etcs_brake_params: Option<Json<Option<EtcsBrakeParams>>>,
pub inertia_coefficient: Option<f64>,
pub base_power_class: Option<Option<String>>,
pub mass: Option<f64>,
pub rolling_resistance: Option<Json<RollingResistance>>,
pub loading_gauge: Option<i16>,
pub power_restrictions: Option<Json<HashMap<String, String>>>,
pub energy_sources: Option<Json<Vec<EnergySource>>>,
pub locked: Option<bool>,
pub electrical_power_startup_time: Option<Option<f64>>,
pub raise_pantograph_time: Option<Option<f64>>,
pub version: Option<i64>,
pub supported_signaling_systems: Option<Vec<Option<String>>>,
}

let changeset = Inner::deserialize(deserializer)?;

if changeset.effort_curves.is_none() {
return Err(serde::de::Error::custom("effort_curves is required"));
}

validate_rolling_stock::<D>(
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<f64>,
raise_pantograph_time: Option<f64>,
) -> 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(())
}
Expand Down
3 changes: 0 additions & 3 deletions editoast/src/views/rolling_stock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<RollingStockModel> = rolling_stock_form.into();
Expand Down Expand Up @@ -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
Expand Down
44 changes: 30 additions & 14 deletions editoast/src/views/rolling_stock/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -52,6 +52,32 @@ pub struct RollingStockForm {
pub metadata: Option<RollingStockMetadata>,
}

impl<'de> Deserialize<'de> for RollingStockForm {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let rolling_stock_form = RollingStockForm::deserialize(deserializer)?;

validate_rolling_stock::<D>(
&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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
RollingStockForm::serialize(self, serializer)
}
}

impl From<RollingStockForm> for Changeset<RollingStockModel> {
fn from(rolling_stock: RollingStockForm) -> Self {
RollingStockModel::changeset()
Expand Down Expand Up @@ -79,16 +105,6 @@ impl From<RollingStockForm> for Changeset<RollingStockModel> {
}
}

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<RollingStockModel> for RollingStockForm {
Expand Down

0 comments on commit 090bcbc

Please sign in to comment.