Skip to content

Commit

Permalink
fix: allow deserialization of optional decimals (#139)
Browse files Browse the repository at this point in the history
the 'rust_decimal::serde::arbitrary_precision_option' macro
requires 'default' to be specified to allow deserializing
the objects when the value is not present
  • Loading branch information
j-white authored Jan 21, 2025
1 parent f655062 commit e39804b
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 11 deletions.
109 changes: 108 additions & 1 deletion src/tests/schema_validation/v2_0_1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2226,6 +2226,113 @@ mod tests {
}
assert!(compiled.is_valid(&instance));
}
/**
* Some optional fields including maxLimit of the VariableCharacteristicsType are not
* included in the payload in order to validate deserialization of optional fields.
*/
#[test]
fn validate_notify_report_request_from_json() {
let json = r#"{
"generatedAt": "2024-12-23T03:38:31.625Z",
"reportData": [
{
"component": {
"name": "AlignedDataCtrlr"
},
"variable": {
"name": "Interval"
},
"variableAttribute": [
{
"mutability": "ReadWrite",
"value": "10"
}
],
"variableCharacteristics": {
"dataType": "integer",
"supportsMonitoring": true,
"unit": "seconds"
}
},
{
"component": {
"name": "AlignedDataCtrlr"
},
"variable": {
"name": "Measurands"
},
"variableAttribute": [
{
"mutability": "ReadWrite",
"value": "Energy.Active.Import.Register"
}
],
"variableCharacteristics": {
"dataType": "MemberList",
"supportsMonitoring": true
}
},
{
"component": {
"name": "AlignedDataCtrlr"
},
"variable": {
"name": "TxEndedInterval"
},
"variableAttribute": [
{
"mutability": "ReadWrite",
"value": "10"
}
],
"variableCharacteristics": {
"dataType": "integer",
"supportsMonitoring": true,
"unit": "seconds"
}
},
{
"component": {
"name": "AlignedDataCtrlr"
},
"variable": {
"name": "TxEndedMeasurands"
},
"variableAttribute": [
{
"mutability": "ReadWrite",
"value": "Energy.Active.Import.Register"
}
],
"variableCharacteristics": {
"dataType": "MemberList",
"supportsMonitoring": true,
"minLimit": 0
}
}
],
"requestId": 1,
"seqNo": 0,
"tbc": true
}"#;

// verify that the JSON can be deserialized into a NotifyReportRequest object
let request: NotifyReportRequest = serde_json::from_str(json).unwrap();
assert_eq!(request.request_id, 1);

let schema = include_str!("schemas/v2.0.1/NotifyReportRequest.json");
let schema = serde_json::from_str(schema).unwrap();
let instance = serde_json::from_str(json).unwrap();
let compiled = Validator::new(&schema).expect("A valid schema");
let result = compiled.validate(&instance);
if let Err(errors) = result {
for error in errors {
println!("Validation error: {}", error);
println!("Instance path: {}", error.instance_path);
}
}
assert!(compiled.is_valid(&instance));
}
#[test]
fn validate_notify_report_request() {
let test = NotifyReportRequest {
Expand Down Expand Up @@ -2257,7 +2364,7 @@ mod tests {
unit: Some("unit".to_string()),
data_type: DataEnumType::String,
min_limit: Some(dec!(0.0)),
max_limit: Some(dec!(0.0)),
max_limit: None,
values_list: Some("values_list".to_string()),
supports_monitoring: false,
}),
Expand Down
7 changes: 5 additions & 2 deletions src/v1_6/types/charging_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ pub struct ChargingSchedule {
/// Required. List of ChargingSchedulePeriod elements defining maximum power or current usage over time. The startSchedule of the first ChargingSchedulePeriod SHALL always be 0.
pub charging_schedule_period: Vec<ChargingSchedulePeriod>,
/// Optional. Minimum charging rate supported by the electric vehicle. The unit of measure is defined by the chargingRateUnit. This parameter is intended to be used by a local smart charging algorithm to optimize the power allocation for in the case a charging process is inefficient at lower charging rates. Accepts at most one digit fraction (e.g. 8.1)
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
#[serde(
with = "rust_decimal::serde::arbitrary_precision_option",
skip_serializing_if = "Option::is_none",
default
)]
pub min_charging_rate: Option<Decimal>,
}
7 changes: 5 additions & 2 deletions src/v2_0_1/datatypes/charging_schedule_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ pub struct ChargingScheduleType {
/// Required. The unit of measure Limit is expressed in.
pub charging_rate_unit: ChargingRateUnitEnumType,
/// Optional. Minimum charging rate supported by the EV. The unit of measure is defined by the chargingRateUnit. This parameter is intended to be used by a local smart charging algorithm to optimize the power allocation for in the case a charging process is inefficient at lower charging rates. Accepts at most one digit fraction (e.g. 8.1)
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
#[serde(
with = "rust_decimal::serde::arbitrary_precision_option",
skip_serializing_if = "Option::is_none",
default
)]
pub min_charging_rate: Option<Decimal>,
/// Required. List of ChargingSchedulePeriod elements defining maximum power or current usage over time. The maximum number of periods, that is supported by the Charging Station, if less than 1024, is set by device model variable SmartChargingCtrlr.PeriodsPerSchedule
#[validate(length(min = 1))]
Expand Down
14 changes: 10 additions & 4 deletions src/v2_0_1/datatypes/variable_characteristics_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ pub struct VariableCharacteristicsType {
#[serde(skip_serializing_if = "Option::is_none")]
pub unit: Option<String>,
pub data_type: DataEnumType,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
#[serde(
with = "rust_decimal::serde::arbitrary_precision_option",
skip_serializing_if = "Option::is_none",
default
)]
pub min_limit: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
#[serde(
with = "rust_decimal::serde::arbitrary_precision_option",
skip_serializing_if = "Option::is_none",
default
)]
pub max_limit: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub values_list: Option<String>,
Expand Down
7 changes: 5 additions & 2 deletions src/v2_0_1/messages/transaction_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ pub struct TransactionEventRequest {
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct TransactionEventResponse {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
#[serde(
with = "rust_decimal::serde::arbitrary_precision_option",
skip_serializing_if = "Option::is_none",
default
)]
pub total_cost: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub charging_priority: Option<i32>,
Expand Down

0 comments on commit e39804b

Please sign in to comment.