From f9671cee34d09df1e19e9fc42048a927cca3cca3 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Mon, 8 Jul 2024 11:05:50 +0100 Subject: [PATCH 01/28] feat: replace strings with enums for co2eIntensityThroughput --- gen/schemas/hoc.json | 2 +- gen/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gen/schemas/hoc.json b/gen/schemas/hoc.json index ce22183..59c1138 100644 --- a/gen/schemas/hoc.json +++ b/gen/schemas/hoc.json @@ -20,7 +20,7 @@ "$ref": "#/definitions/HocCo2eIntensityThroughput" }, "co2eIntensityWTW": { - "$ref": "#/definitions/Decimal" + "$ref": "#/definitions/HocCo2eIntensityThroughput" }, "description": { "type": [ diff --git a/gen/src/lib.rs b/gen/src/lib.rs index be79234..fe1bcda 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -184,7 +184,7 @@ pub struct Hoc { pub packaging_or_tr_eq_amount: Option, pub energy_carriers: NonEmptyVec, #[serde(rename = "co2eIntensityWTW")] - pub co2e_intensity_wtw: WrappedDecimal, + pub co2e_intensity_wtw: HocCo2eIntensityThroughput, #[serde(rename = "co2eIntensityTTW")] pub co2e_intensity_ttw: WrappedDecimal, pub co2e_intensity_throughput: HocCo2eIntensityThroughput, From 3cb16441095ed82e09e483fbf3883cd44ba20325 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 2 Jul 2024 18:31:37 +0100 Subject: [PATCH 02/28] feat: implement Arbitrary for TCE --- gen/Cargo.lock | 34 +++++++++++ gen/Cargo.toml | 2 + gen/src/lib.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 189 insertions(+), 2 deletions(-) diff --git a/gen/Cargo.lock b/gen/Cargo.lock index f805543..e176436 100644 --- a/gen/Cargo.lock +++ b/gen/Cargo.lock @@ -164,6 +164,16 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -231,6 +241,8 @@ version = "0.1.0" dependencies = [ "chrono", "pact-data-model", + "quickcheck", + "quickcheck_macros", "regex", "rust_decimal", "rust_decimal_macros", @@ -377,6 +389,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand", +] + +[[package]] +name = "quickcheck_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.36" diff --git a/gen/Cargo.toml b/gen/Cargo.toml index d0a60cc..97cc1b0 100644 --- a/gen/Cargo.toml +++ b/gen/Cargo.toml @@ -14,3 +14,5 @@ serde_json = "1.0.96" rust_decimal = "1.35.0" rust_decimal_macros = "^1.20" regex = "1.10.4" +quickcheck = "1" +quickcheck_macros = "1" diff --git a/gen/src/lib.rs b/gen/src/lib.rs index fe1bcda..a713bb0 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,6 +1,8 @@ //! iLEAP Data Model Extension data model -use chrono::{DateTime, Utc}; +use chrono::{DateTime, Duration, Utc}; use pact_data_model::{GeographicScope, WrappedDecimal}; +use quickcheck::Arbitrary; +use rust_decimal::Decimal; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -376,7 +378,7 @@ impl From for Locode { if s.len() == 5 { Locode(s) } else { - panic!("LOCODE must be 5 characters long") + panic!("LOCODE must be 5 characters long, got '{s}'") } } } @@ -415,3 +417,152 @@ impl From for GlecDataQualityIndex { } } } + +impl Arbitrary for Tce { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Tce { + tce_id: String::arbitrary(g), + toc_id: Option::::arbitrary(g), + hoc_id: Option::::arbitrary(g), + shipment_id: String::arbitrary(g), + consignment_id: Option::::arbitrary(g), + mass: Decimal::from(u32::arbitrary(g)).into(), + packaging_or_tr_eq_type: Option::::arbitrary(g), + packaging_or_tr_eq_amount: Option::::arbitrary(g), + distance: GlecDistance::arbitrary(g), + origin: Option::::arbitrary(g), + destination: Option::::arbitrary(g), + transport_activity: arbitrary_wrapped_decimal(g), + departure_at: Option::>::from( + Utc::now() + Duration::days(u8::arbitrary(g) as i64), + ), + arrival_at: Option::>::from( + Utc::now() + Duration::days(u8::arbitrary(g) as i64), + ), + flight_no: Option::::arbitrary(g), + voyage_no: Option::::arbitrary(g), + incoterms: Option::::arbitrary(g), + co2e_wtw: Decimal::from(u32::arbitrary(g)).into(), + co2e_ttw: Decimal::from(u32::arbitrary(g)).into(), + nox_ttw: arbitrary_option_wrapped_decimal(g), + sox_ttw: arbitrary_option_wrapped_decimal(g), + ch4_ttw: arbitrary_option_wrapped_decimal(g), + pm_ttw: arbitrary_option_wrapped_decimal(g), + } + } +} + +impl Arbitrary for GlecDistance { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let glec_distance = &[ + GlecDistance::Actual(Decimal::from(u32::arbitrary(g)).into()), + GlecDistance::Gcd(Decimal::from(u32::arbitrary(g)).into()), + GlecDistance::Sfd(Decimal::from(u32::arbitrary(g)).into()), + ]; + + g.choose(glec_distance).unwrap().to_owned() + } +} + +impl Arbitrary for Location { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Location { + street: Option::::arbitrary(g), + zip: Option::::arbitrary(g), + city: String::arbitrary(g), + country: GeographicScope::Country { + geography_country: pact_data_model::ISO3166CC(String::arbitrary(g)), + }, + iata: Option::::arbitrary(g), + locode: Option::::arbitrary(g), + uic: Option::::arbitrary(g), + lat: arbitrary_option_wrapped_decimal(g), + lng: arbitrary_option_wrapped_decimal(g), + } + } +} + +impl Arbitrary for IataCode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mut s = String::new(); + + for _ in 0..3 { + let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; + s.push(ascii_capital) + } + + IataCode::from(s) + } +} + +impl Arbitrary for Locode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mut s = String::new(); + + for _ in 0..5 { + // 65..90 - ASCII A to Z + let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; + s.push(ascii_capital) + } + + Locode::from(s) + } +} + +impl Arbitrary for UicCode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mut s = String::new(); + + for _ in 0..2 { + let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; + s.push(ascii_capital) + } + + UicCode::from(s) + } +} + +fn arbitrary_wrapped_decimal(g: &mut quickcheck::Gen) -> WrappedDecimal { + Decimal::from(u32::arbitrary(g)).into() +} + +fn arbitrary_option_wrapped_decimal(g: &mut quickcheck::Gen) -> Option { + let option = &[Some(arbitrary_wrapped_decimal(g)), None]; + + g.choose(option).unwrap().to_owned() +} + +impl Arbitrary for Incoterms { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let incoterms = &[ + Incoterms::Exw, + Incoterms::Fca, + Incoterms::Cpt, + Incoterms::Cip, + Incoterms::Dap, + Incoterms::Dpu, + Incoterms::Ddp, + Incoterms::Fas, + Incoterms::Fob, + Incoterms::Cfr, + Incoterms::Cif, + ]; + + g.choose(incoterms).unwrap().to_owned() + } +} + +#[cfg(test)] +mod tests { + use crate::Tce; + use quickcheck_macros::quickcheck; + + #[quickcheck] + fn serialize_and_deserialize(tce: Tce) -> bool { + let serialized = serde_json::to_string(&tce).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + deserialized == tce + // true + } +} From 768bef9b89ac670ab043a79f46270c394871f3f8 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 3 Jul 2024 12:59:53 +0100 Subject: [PATCH 03/28] fix: incoterms serialization annotations --- gen/src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index a713bb0..271937e 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,10 +1,13 @@ //! iLEAP Data Model Extension data model +use std::iter; + use chrono::{DateTime, Duration, Utc}; use pact_data_model::{GeographicScope, WrappedDecimal}; use quickcheck::Arbitrary; use rust_decimal::Decimal; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use serde_json::map::Iter; #[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, PartialEq)] #[serde(rename_all = "camelCase")] @@ -418,10 +421,48 @@ impl From for GlecDataQualityIndex { } } +// #[derive(Clone)] +// pub struct AToZAndNumString(String); + +// impl AToZAndNumString { +// pub fn as_bytes(&self) -> &[u8] { +// self.0.as_bytes() +// } + +// pub fn len(&self) -> usize { +// self.0.len() +// } +// } + +// impl Arbitrary for AToZAndNumString { +// fn arbitrary(g: &mut quickcheck::Gen) -> Self { +// let s: Vec = Vec::arbitrary(g); +// let s: String = s +// .into_iter() +// .map(|_| g.choose(&['a'..='z', '0'..='9']).unwrap()) +// .collect(); +// Self(s) +// } + +// fn shrink(&self) -> Box> { +// let s = self.0.clone(); +// let range = 0..self.len(); +// let shrunk: Vec<_> = range +// .into_iter() +// .map(|len| Self(s[0..len].to_string())) +// .collect(); +// Box::new(shrunk.into_iter()) +// } +// } + +fn formatted_arbitrary_string(fixed: String, g: &mut quickcheck::Gen) -> String { + fixed + &String::arbitrary(g) +} + impl Arbitrary for Tce { fn arbitrary(g: &mut quickcheck::Gen) -> Self { Tce { - tce_id: String::arbitrary(g), + tce_id: formatted_arbitrary_string("tce-".to_string(), g), toc_id: Option::::arbitrary(g), hoc_id: Option::::arbitrary(g), shipment_id: String::arbitrary(g), @@ -514,8 +555,8 @@ impl Arbitrary for UicCode { let mut s = String::new(); for _ in 0..2 { - let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; - s.push(ascii_capital) + let int = (u8::arbitrary(g) % 9) + 1; + s.push(int as char) } UicCode::from(s) @@ -562,6 +603,12 @@ mod tests { let serialized = serde_json::to_string(&tce).unwrap(); let deserialized = serde_json::from_str::(&serialized).unwrap(); + if deserialized != tce { + println!("tce: {tce:?}"); + println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + } + deserialized == tce // true } From 531b3961ceaabd4e4a3e996f3e6499d884900fa9 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 3 Jul 2024 18:06:40 +0100 Subject: [PATCH 04/28] feat: implement Arbitrary for Toc --- gen/src/lib.rs | 254 ++++++++++++++++++++++++++++++++++++++++-------- gen/src/main.rs | 15 ++- 2 files changed, 222 insertions(+), 47 deletions(-) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 271937e..a6cb752 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -164,6 +164,7 @@ pub enum FlightLength { #[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq, Clone)] #[serde(rename_all = "camelCase")] +// TODO: use a floating point or a decimal instead. pub struct GlecDataQualityIndex(pub u8); #[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq, Clone)] @@ -421,48 +422,202 @@ impl From for GlecDataQualityIndex { } } -// #[derive(Clone)] -// pub struct AToZAndNumString(String); - -// impl AToZAndNumString { -// pub fn as_bytes(&self) -> &[u8] { -// self.0.as_bytes() -// } - -// pub fn len(&self) -> usize { -// self.0.len() -// } -// } - -// impl Arbitrary for AToZAndNumString { -// fn arbitrary(g: &mut quickcheck::Gen) -> Self { -// let s: Vec = Vec::arbitrary(g); -// let s: String = s -// .into_iter() -// .map(|_| g.choose(&['a'..='z', '0'..='9']).unwrap()) -// .collect(); -// Self(s) -// } - -// fn shrink(&self) -> Box> { -// let s = self.0.clone(); -// let range = 0..self.len(); -// let shrunk: Vec<_> = range -// .into_iter() -// .map(|len| Self(s[0..len].to_string())) -// .collect(); -// Box::new(shrunk.into_iter()) -// } -// } - -fn formatted_arbitrary_string(fixed: String, g: &mut quickcheck::Gen) -> String { - fixed + &String::arbitrary(g) +#[derive(Clone)] +pub struct LowerAToZNumDash(String); + +impl LowerAToZNumDash { + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl Arbitrary for LowerAToZNumDash { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let s: Vec<()> = Vec::arbitrary(g); + let s: String = s + .into_iter() + .map(|_| { + // ASCII characters -, 0..9, a..z + let i = u8::arbitrary(g) % 37; + match i { + 0 => '-', + 1..=10 => (i as u8 + 47) as char, + _ => (i as u8 + 86) as char, + } + }) + .collect(); + Self(s) + } + + fn shrink(&self) -> Box> { + let s = self.0.clone(); + let range = 0..self.len(); + let shrunk: Vec<_> = range + .into_iter() + .map(|len| Self(s[0..len].to_string())) + .collect(); + Box::new(shrunk.into_iter()) + } +} + +fn formatted_arbitrary_string(fixed: &str, g: &mut quickcheck::Gen) -> String { + fixed.to_string() + &LowerAToZNumDash::arbitrary(g).0 +} + +impl Arbitrary for Toc { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Toc { + toc_id: formatted_arbitrary_string("toc-", g), + is_verified: bool::arbitrary(g), + is_accredited: bool::arbitrary(g), + description: Option::::arbitrary(g), + mode: TransportMode::arbitrary(g), + load_factor: Option::::arbitrary(g), + empty_distance_factor: Option::::arbitrary(g), + temperature_control: Option::::arbitrary(g), + truck_loading_sequence: Option::::arbitrary(g), + air_shipping_option: Option::::arbitrary(g), + flight_length: Option::::arbitrary(g), + energy_carriers: NonEmptyVec::::arbitrary(g), + co2e_intensity_wtw: arbitrary_wrapped_decimal(g), + co2e_intensity_ttw: arbitrary_wrapped_decimal(g), + co2e_intensity_throughput: String::arbitrary(g), + glec_data_quality_index: Option::::arbitrary(g), + } + } +} + +impl Arbitrary for NonEmptyVec { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + NonEmptyVec(vec![T::arbitrary(g)]) + } +} + +impl Arbitrary for TransportMode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let transport_mode = &[ + TransportMode::Road, + TransportMode::Rail, + TransportMode::Air, + TransportMode::Sea, + TransportMode::InlandWaterway, + ]; + + g.choose(transport_mode).unwrap().to_owned() + } +} + +impl Arbitrary for TemperatureControl { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let temperature_control = &[ + TemperatureControl::Ambient, + TemperatureControl::Refrigerated, + TemperatureControl::Mixed, + ]; + + g.choose(temperature_control).unwrap().to_owned() + } +} + +impl Arbitrary for TruckLoadingSequence { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let truck_loading_sequence = &[TruckLoadingSequence::Ftl, TruckLoadingSequence::Ltl]; + + g.choose(truck_loading_sequence).unwrap().to_owned() + } +} + +impl Arbitrary for AirShippingOption { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let air_shipping_option = &[ + AirShippingOption::BellyFreight, + AirShippingOption::Freighter, + ]; + + g.choose(air_shipping_option).unwrap().to_owned() + } +} + +impl Arbitrary for FlightLength { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let flight_length = &[FlightLength::ShortHaul, FlightLength::LongHaul]; + + g.choose(flight_length).unwrap().to_owned() + } +} + +impl Arbitrary for EnergyCarrier { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + EnergyCarrier { + energy_carrier: EnergyCarrierType::arbitrary(g), + feedstocks: Option::>::arbitrary(g), + energy_consumption: arbitrary_option_wrapped_decimal(g), + energy_consumption_unit: Option::::arbitrary(g), + emission_factor_wtw: arbitrary_wrapped_decimal(g), + emission_factor_ttw: arbitrary_wrapped_decimal(g), + } + } +} + +impl Arbitrary for EnergyCarrierType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let energy_carrier = &[ + EnergyCarrierType::Diesel, + EnergyCarrierType::Hvo, + EnergyCarrierType::Petrol, + EnergyCarrierType::Cng, + EnergyCarrierType::Lng, + EnergyCarrierType::Lpg, + EnergyCarrierType::Hfo, + EnergyCarrierType::Mgo, + EnergyCarrierType::AviationFuel, + EnergyCarrierType::Hydrogen, + EnergyCarrierType::Methanol, + EnergyCarrierType::Electric, + ]; + + g.choose(energy_carrier).unwrap().to_owned() + } +} + +impl Arbitrary for Feedstock { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Feedstock { + feedstock: FeedstockType::arbitrary(g), + feedstock_percentage: Option::::arbitrary(g), + region_provenance: Option::::arbitrary(g), + } + } +} + +impl Arbitrary for FeedstockType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let feedstock = &[ + FeedstockType::Fossil, + FeedstockType::NaturalGas, + FeedstockType::Grid, + FeedstockType::RenewableElectricity, + FeedstockType::CookingOil, + ]; + + g.choose(feedstock).unwrap().to_owned() + } +} + +impl Arbitrary for GlecDataQualityIndex { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + GlecDataQualityIndex(u8::arbitrary(g) % 5) + } } impl Arbitrary for Tce { fn arbitrary(g: &mut quickcheck::Gen) -> Self { Tce { - tce_id: formatted_arbitrary_string("tce-".to_string(), g), + tce_id: formatted_arbitrary_string("tce-", g), toc_id: Option::::arbitrary(g), hoc_id: Option::::arbitrary(g), shipment_id: String::arbitrary(g), @@ -595,21 +750,34 @@ impl Arbitrary for Incoterms { #[cfg(test)] mod tests { - use crate::Tce; + use crate::{Tce, Toc}; use quickcheck_macros::quickcheck; #[quickcheck] - fn serialize_and_deserialize(tce: Tce) -> bool { + fn ser_and_deser_tce(tce: Tce) -> bool { let serialized = serde_json::to_string(&tce).unwrap(); let deserialized = serde_json::from_str::(&serialized).unwrap(); - if deserialized != tce { - println!("tce: {tce:?}"); - println!("serialized: {serialized}"); + println!("tce: {tce:?}"); + println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + + deserialized == tce + // true + } + + #[quickcheck] + fn ser_and_deser_toc(toc: Toc) -> bool { + let serialized = serde_json::to_string(&toc).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + if deserialized != toc { + println!("toc: {toc:?}"); + // println!("serialized: {serialized}"); println!("deserialized: {deserialized:?}"); } - deserialized == tce + deserialized == toc // true } } diff --git a/gen/src/main.rs b/gen/src/main.rs index 0cba613..f42d397 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -4,12 +4,19 @@ use schemars::schema_for; use serde_json::to_string_pretty; use std::fs::File; use std::io::{Error, Write}; +use quickcheck::{Arbitrary, Gen}; fn main() -> Result<(), Error> { - generate_schema::()?; - generate_schema::()?; - generate_schema::()?; - generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; + + let mut og = Gen::new(10); + + let tce = Tce::arbitrary(&mut og); + + println!("tce: {tce:?}"); Ok(()) } From 235889ede1ff9d0793e72e3e51d256451e4722e1 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Thu, 4 Jul 2024 10:44:25 +0100 Subject: [PATCH 05/28] feat: implement Arbitrary for Hoc and ShipmentFootprint --- gen/schemas/hoc.json | 13 +++--- gen/schemas/tad.json | 13 +++--- gen/schemas/toc.json | 13 +++--- gen/src/lib.rs | 99 +++++++++++++++++++++++++++++++++++++++++--- gen/src/main.rs | 16 +++---- 5 files changed, 126 insertions(+), 28 deletions(-) diff --git a/gen/schemas/hoc.json b/gen/schemas/hoc.json index 59c1138..2938208 100644 --- a/gen/schemas/hoc.json +++ b/gen/schemas/hoc.json @@ -178,11 +178,14 @@ "$ref": "#/definitions/FeedstockType" }, "feedstockPercentage": { - "type": [ - "number", - "null" - ], - "format": "double" + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] }, "regionProvenance": { "type": [ diff --git a/gen/schemas/tad.json b/gen/schemas/tad.json index b852542..1d0427e 100644 --- a/gen/schemas/tad.json +++ b/gen/schemas/tad.json @@ -126,11 +126,14 @@ "$ref": "#/definitions/FeedstockType" }, "feedstockPercentage": { - "type": [ - "number", - "null" - ], - "format": "double" + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] }, "regionProvenance": { "type": [ diff --git a/gen/schemas/toc.json b/gen/schemas/toc.json index 91bd058..5d388db 100644 --- a/gen/schemas/toc.json +++ b/gen/schemas/toc.json @@ -189,11 +189,14 @@ "$ref": "#/definitions/FeedstockType" }, "feedstockPercentage": { - "type": [ - "number", - "null" - ], - "format": "double" + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] }, "regionProvenance": { "type": [ diff --git a/gen/src/lib.rs b/gen/src/lib.rs index a6cb752..cb84130 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,13 +1,12 @@ //! iLEAP Data Model Extension data model use std::iter; -use chrono::{DateTime, Duration, Utc}; +use chrono::{format, DateTime, Duration, Utc}; use pact_data_model::{GeographicScope, WrappedDecimal}; use quickcheck::Arbitrary; use rust_decimal::Decimal; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use serde_json::map::Iter; #[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, PartialEq)] #[serde(rename_all = "camelCase")] @@ -340,7 +339,7 @@ pub enum EnergyCarrierType { pub struct Feedstock { pub feedstock: FeedstockType, #[serde(skip_serializing_if = "Option::is_none")] - pub feedstock_percentage: Option, + pub feedstock_percentage: Option, #[serde(skip_serializing_if = "Option::is_none")] pub region_provenance: Option, } @@ -468,6 +467,67 @@ fn formatted_arbitrary_string(fixed: &str, g: &mut quickcheck::Gen) -> String { fixed.to_string() + &LowerAToZNumDash::arbitrary(g).0 } +impl Arbitrary for ShipmentFootprint { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + ShipmentFootprint { + mass: String::arbitrary(g), + volume: Option::::arbitrary(g), + number_of_items: Option::::arbitrary(g), + type_of_items: Option::::arbitrary(g), + shipment_id: formatted_arbitrary_string("shipment-", g), + tces: NonEmptyVec::::arbitrary(g), + } + } +} + +impl Arbitrary for Hoc { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Hoc { + hoc_id: formatted_arbitrary_string("hoc-", g), + description: Option::::arbitrary(g), + is_verified: bool::arbitrary(g), + is_accredited: bool::arbitrary(g), + hub_type: HubType::arbitrary(g), + temperature_control: Option::::arbitrary(g), + hub_location: Option::::arbitrary(g), + inbound_transport_mode: Option::::arbitrary(g), + outbound_transport_mode: Option::::arbitrary(g), + packaging_or_tr_eq_type: Option::::arbitrary(g), + packaging_or_tr_eq_amount: Option::::arbitrary(g), + energy_carriers: NonEmptyVec::::arbitrary(g), + co2e_intensity_wtw: arbitrary_wrapped_decimal(g), + co2e_intensity_ttw: arbitrary_wrapped_decimal(g), + co2e_intensity_throughput: String::arbitrary(g), + } + } +} + +impl Arbitrary for HubType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let hub_type = &[ + HubType::Transshipment, + HubType::StorageAndTransshipment, + HubType::Warehouse, + HubType::LiquidBulkterminal, + HubType::MaritimeContainerterminal, + ]; + + g.choose(hub_type).unwrap().to_owned() + } +} + +impl Arbitrary for PackagingOrTrEqType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let packaging_or_tr_eq_type = &[ + PackagingOrTrEqType::Box, + PackagingOrTrEqType::Pallet, + PackagingOrTrEqType::Container, + ]; + + g.choose(packaging_or_tr_eq_type).unwrap().to_owned() + } +} + impl Arbitrary for Toc { fn arbitrary(g: &mut quickcheck::Gen) -> Self { Toc { @@ -588,7 +648,7 @@ impl Arbitrary for Feedstock { fn arbitrary(g: &mut quickcheck::Gen) -> Self { Feedstock { feedstock: FeedstockType::arbitrary(g), - feedstock_percentage: Option::::arbitrary(g), + feedstock_percentage: arbitrary_option_wrapped_decimal(g), region_provenance: Option::::arbitrary(g), } } @@ -750,7 +810,7 @@ impl Arbitrary for Incoterms { #[cfg(test)] mod tests { - use crate::{Tce, Toc}; + use crate::{Hoc, ShipmentFootprint, Tce, Toc}; use quickcheck_macros::quickcheck; #[quickcheck] @@ -780,4 +840,33 @@ mod tests { deserialized == toc // true } + + #[quickcheck] + fn ser_and_deser_hoc(hoc: Hoc) -> bool { + let serialized = serde_json::to_string(&hoc).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + if deserialized != hoc { + println!("toc: {hoc:?}"); + // println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + } + + deserialized == hoc + // true + } + + #[quickcheck] + fn ser_and_deser_ship_foot(ship_foot: ShipmentFootprint) { + let serialized = serde_json::to_string(&ship_foot).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + if deserialized != ship_foot { + println!("ship_foot: {ship_foot:?}"); + // println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + } + + assert_eq!(deserialized, ship_foot); + } } diff --git a/gen/src/main.rs b/gen/src/main.rs index f42d397..48e9711 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -1,22 +1,22 @@ use ileap_extension::*; +use quickcheck::{Arbitrary, Gen}; use regex::Regex; use schemars::schema_for; use serde_json::to_string_pretty; use std::fs::File; use std::io::{Error, Write}; -use quickcheck::{Arbitrary, Gen}; fn main() -> Result<(), Error> { - // generate_schema::()?; - // generate_schema::()?; - // generate_schema::()?; - // generate_schema::()?; + generate_schema::()?; + generate_schema::()?; + generate_schema::()?; + generate_schema::()?; - let mut og = Gen::new(10); + // let mut og = Gen::new(10); - let tce = Tce::arbitrary(&mut og); + // let tce = Tce::arbitrary(&mut og); - println!("tce: {tce:?}"); + // println!("tce: {tce:?}"); Ok(()) } From 38d523ca772cd4c801bee1123e1bf2279783a701 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Thu, 4 Jul 2024 15:27:27 +0100 Subject: [PATCH 06/28] feat: generate minimally consistent arbitrary tce --- gen/Cargo.lock | 2 +- gen/Cargo.toml | 4 +- gen/src/arbitrary_impls.rs | 508 +++++++++++++++++++++++++++++++++++++ gen/src/lib.rs | 458 +-------------------------------- gen/src/main.rs | 14 +- 5 files changed, 521 insertions(+), 465 deletions(-) create mode 100644 gen/src/arbitrary_impls.rs diff --git a/gen/Cargo.lock b/gen/Cargo.lock index e176436..cca6185 100644 --- a/gen/Cargo.lock +++ b/gen/Cargo.lock @@ -312,7 +312,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "pact-data-model" version = "0.1.0" -source = "git+https://github.com/sine-fdn/pact-data-model.git#619a3b5753a3cc1e4b5189f8d59a5b5e633ff81c" +source = "git+https://github.com/sine-fdn/pact-data-model.git?branch=wrapped_decimal#5ef50003b0c71c8d509e6a30ccecc030e9a661e2" dependencies = [ "chrono", "rust_decimal", diff --git a/gen/Cargo.toml b/gen/Cargo.toml index 97cc1b0..e0954c3 100644 --- a/gen/Cargo.toml +++ b/gen/Cargo.toml @@ -6,10 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -pact-data-model = { git = "https://github.com/sine-fdn/pact-data-model.git" } +pact-data-model = { git = "https://github.com/sine-fdn/pact-data-model.git", branch = "wrapped_decimal" } chrono = { version = "0.4", features = ["serde"] } schemars = { version = "0.8", features = ["chrono"] } -serde = { version = "1.0.203", features = ["derive"]} +serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.96" rust_decimal = "1.35.0" rust_decimal_macros = "^1.20" diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs new file mode 100644 index 0000000..31387c2 --- /dev/null +++ b/gen/src/arbitrary_impls.rs @@ -0,0 +1,508 @@ +use crate::*; +use chrono::Duration; +use quickcheck::Arbitrary; +use rust_decimal::prelude::ToPrimitive; +use rust_decimal::Decimal; + +#[derive(Clone)] +pub struct LowerAToZNumDash(String); + +impl LowerAToZNumDash { + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl Arbitrary for LowerAToZNumDash { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let s: Vec<()> = Vec::arbitrary(g); + let s: String = s + .into_iter() + .map(|_| { + // ASCII characters -, 0..9, a..z + let i = u8::arbitrary(g) % 37; + match i { + 0 => '-', + 1..=10 => (i as u8 + 47) as char, + _ => (i as u8 + 86) as char, + } + }) + .collect(); + Self(s) + } + + fn shrink(&self) -> Box> { + let s = self.0.clone(); + let range = 0..self.len(); + let shrunk: Vec<_> = range + .into_iter() + .map(|len| Self(s[0..len].to_string())) + .collect(); + Box::new(shrunk.into_iter()) + } +} + +fn formatted_arbitrary_string(fixed: &str, g: &mut quickcheck::Gen) -> String { + fixed.to_string() + &LowerAToZNumDash::arbitrary(g).0 +} + +impl Arbitrary for ShipmentFootprint { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + ShipmentFootprint { + mass: String::arbitrary(g), + volume: Option::::arbitrary(g), + number_of_items: Option::::arbitrary(g), + type_of_items: Option::::arbitrary(g), + shipment_id: formatted_arbitrary_string("shipment-", g), + tces: NonEmptyVec::::arbitrary(g), + } + } +} + +impl Arbitrary for Hoc { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Hoc { + hoc_id: formatted_arbitrary_string("hoc-", g), + description: Option::::arbitrary(g), + is_verified: bool::arbitrary(g), + is_accredited: bool::arbitrary(g), + hub_type: HubType::arbitrary(g), + temperature_control: Option::::arbitrary(g), + hub_location: Option::::arbitrary(g), + inbound_transport_mode: Option::::arbitrary(g), + outbound_transport_mode: Option::::arbitrary(g), + packaging_or_tr_eq_type: Option::::arbitrary(g), + packaging_or_tr_eq_amount: Option::::arbitrary(g), + energy_carriers: NonEmptyVec::::arbitrary(g), + co2e_intensity_wtw: arbitrary_wrapped_decimal(g), + co2e_intensity_ttw: arbitrary_wrapped_decimal(g), + co2e_intensity_throughput: String::arbitrary(g), + } + } +} + +impl Arbitrary for HubType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let hub_type = &[ + HubType::Transshipment, + HubType::StorageAndTransshipment, + HubType::Warehouse, + HubType::LiquidBulkterminal, + HubType::MaritimeContainerterminal, + ]; + + g.choose(hub_type).unwrap().to_owned() + } +} + +impl Arbitrary for PackagingOrTrEqType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let packaging_or_tr_eq_type = &[ + PackagingOrTrEqType::Box, + PackagingOrTrEqType::Pallet, + PackagingOrTrEqType::Container, + ]; + + g.choose(packaging_or_tr_eq_type).unwrap().to_owned() + } +} + +impl Arbitrary for Toc { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Toc { + toc_id: formatted_arbitrary_string("toc-", g), + is_verified: bool::arbitrary(g), + is_accredited: bool::arbitrary(g), + description: Option::::arbitrary(g), + mode: TransportMode::arbitrary(g), + load_factor: Option::::arbitrary(g), + empty_distance_factor: Option::::arbitrary(g), + temperature_control: Option::::arbitrary(g), + truck_loading_sequence: Option::::arbitrary(g), + air_shipping_option: Option::::arbitrary(g), + flight_length: Option::::arbitrary(g), + energy_carriers: NonEmptyVec::::arbitrary(g), + co2e_intensity_wtw: arbitrary_wrapped_decimal(g), + co2e_intensity_ttw: arbitrary_wrapped_decimal(g), + co2e_intensity_throughput: String::arbitrary(g), + glec_data_quality_index: Option::::arbitrary(g), + } + } +} + +impl Arbitrary for NonEmptyVec { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + NonEmptyVec(vec![T::arbitrary(g)]) + } +} + +impl Arbitrary for TransportMode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let transport_mode = &[ + TransportMode::Road, + TransportMode::Rail, + TransportMode::Air, + TransportMode::Sea, + TransportMode::InlandWaterway, + ]; + + g.choose(transport_mode).unwrap().to_owned() + } +} + +impl Arbitrary for TemperatureControl { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let temperature_control = &[ + TemperatureControl::Ambient, + TemperatureControl::Refrigerated, + TemperatureControl::Mixed, + ]; + + g.choose(temperature_control).unwrap().to_owned() + } +} + +impl Arbitrary for TruckLoadingSequence { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let truck_loading_sequence = &[TruckLoadingSequence::Ftl, TruckLoadingSequence::Ltl]; + + g.choose(truck_loading_sequence).unwrap().to_owned() + } +} + +impl Arbitrary for AirShippingOption { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let air_shipping_option = &[ + AirShippingOption::BellyFreight, + AirShippingOption::Freighter, + ]; + + g.choose(air_shipping_option).unwrap().to_owned() + } +} + +impl Arbitrary for FlightLength { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let flight_length = &[FlightLength::ShortHaul, FlightLength::LongHaul]; + + g.choose(flight_length).unwrap().to_owned() + } +} + +impl Arbitrary for EnergyCarrier { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + EnergyCarrier { + energy_carrier: EnergyCarrierType::arbitrary(g), + feedstocks: Option::>::arbitrary(g), + energy_consumption: arbitrary_option_wrapped_decimal(g), + energy_consumption_unit: Option::::arbitrary(g), + emission_factor_wtw: arbitrary_wrapped_decimal(g), + emission_factor_ttw: arbitrary_wrapped_decimal(g), + } + } +} + +impl Arbitrary for EnergyCarrierType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let energy_carrier = &[ + EnergyCarrierType::Diesel, + EnergyCarrierType::Hvo, + EnergyCarrierType::Petrol, + EnergyCarrierType::Cng, + EnergyCarrierType::Lng, + EnergyCarrierType::Lpg, + EnergyCarrierType::Hfo, + EnergyCarrierType::Mgo, + EnergyCarrierType::AviationFuel, + EnergyCarrierType::Hydrogen, + EnergyCarrierType::Methanol, + EnergyCarrierType::Electric, + ]; + + g.choose(energy_carrier).unwrap().to_owned() + } +} + +impl Arbitrary for Feedstock { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Feedstock { + feedstock: FeedstockType::arbitrary(g), + feedstock_percentage: arbitrary_option_wrapped_decimal(g), + region_provenance: Option::::arbitrary(g), + } + } +} + +impl Arbitrary for FeedstockType { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let feedstock = &[ + FeedstockType::Fossil, + FeedstockType::NaturalGas, + FeedstockType::Grid, + FeedstockType::RenewableElectricity, + FeedstockType::CookingOil, + ]; + + g.choose(feedstock).unwrap().to_owned() + } +} + +impl Arbitrary for GlecDataQualityIndex { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + GlecDataQualityIndex(u8::arbitrary(g) % 5) + } +} + +impl Arbitrary for Tce { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let toc_id = if bool::arbitrary(g) { + Some(formatted_arbitrary_string("toc-", g)) + } else { + None + }; + + let hoc_id = match toc_id { + Some(_) => None, + None => Some(formatted_arbitrary_string("hoc-", g)), + }; + + let mass = arbitrary_wrapped_decimal(g); + let glec_distance = GlecDistance::arbitrary(g); + + let distance = match &glec_distance { + GlecDistance::Actual(d) => d, + GlecDistance::Gcd(d) => d, + GlecDistance::Sfd(d) => d, + }; + + let transport_activity = WrappedDecimal::from(mass.0 * distance.0); + + let departure_at = + Option::>::from(Utc::now() + Duration::days(u8::arbitrary(g) as i64)); + + let arrival_at = match departure_at { + None => None, + Some(departure) => { + // Assuming an average speed of 100 km/h, calculate the arrival time based on the + // distance, rounded. + let hours = (distance.0 / Decimal::from(100)).round().to_i64().unwrap(); + + Some(departure + Duration::hours(hours)) + } + }; + + let co2e_wtw = WrappedDecimal::from(match toc_id { + // TODO: toc.co2e_intensity_wtw is currently hardcoded, based on the toc-road-1 example. + Some(_) => Decimal::new(116, 3) * transport_activity.0, + // TODO: if toc_id is None, then we must use hoc.co2e_intensity_wtw; this is currently + // hardcoded, based on the hoc-transshipment-1 example. However, I do not know how to + // calculate it. + None => Decimal::new(33, 0), + }); + + let co2e_ttw = WrappedDecimal::from(match toc_id { + // TODO: toc.co2e_intensity_ttw is currently hardcoded, based on the toc-road-1 example. + Some(_) => Decimal::new(89, 3) * transport_activity.0, + // TODO: if toc_id is None, then we must use hoc.co2e_intensity_ttw; this is currently + // hardcoded, based on the hoc-transshipment-1 example. However, I do not know how to + // calculate it. + None => Decimal::new(10, 0), + }); + + Tce { + tce_id: formatted_arbitrary_string("tce-", g), + toc_id, + hoc_id, + shipment_id: formatted_arbitrary_string("shipment-", g), + // TODO: consignment_id is currently None for simplicity. + consignment_id: None, + mass, + packaging_or_tr_eq_type: Option::::arbitrary(g), + packaging_or_tr_eq_amount: Option::::arbitrary(g), + distance: glec_distance, + // TODO: origin and destination are currently None to avoid an inconsistencies with the + // distance field. In order to fix this, we need to ensure that either the distance is + // calculated from the origin and destination or that the origin and destination are set + // based on the distance. + origin: None, + destination: None, + transport_activity, + departure_at, + arrival_at, + // TODO: flight_no and voyage_no are currently None for simplicity. + flight_no: None, + voyage_no: None, + incoterms: Option::::arbitrary(g), + co2e_wtw, + co2e_ttw, + // TODO: all the following fields are currently None for simplicity. + nox_ttw: None, + sox_ttw: None, + ch4_ttw: None, + pm_ttw: None, + } + } +} + +impl Arbitrary for GlecDistance { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let glec_distance = &[ + GlecDistance::Actual(Decimal::from(u16::arbitrary(g)).into()), + GlecDistance::Gcd(Decimal::from(u16::arbitrary(g)).into()), + GlecDistance::Sfd(Decimal::from(u16::arbitrary(g)).into()), + ]; + + g.choose(glec_distance).unwrap().to_owned() + } +} + +impl Arbitrary for Location { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + Location { + street: Option::::arbitrary(g), + zip: Option::::arbitrary(g), + city: String::arbitrary(g), + country: GeographicScope::Country { + geography_country: pact_data_model::ISO3166CC(String::arbitrary(g)), + }, + iata: Option::::arbitrary(g), + locode: Option::::arbitrary(g), + uic: Option::::arbitrary(g), + lat: arbitrary_option_wrapped_decimal(g), + lng: arbitrary_option_wrapped_decimal(g), + } + } +} + +impl Arbitrary for IataCode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mut s = String::new(); + + for _ in 0..3 { + let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; + s.push(ascii_capital) + } + + IataCode::from(s) + } +} + +impl Arbitrary for Locode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mut s = String::new(); + + for _ in 0..5 { + // 65..90 - ASCII A to Z + let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; + s.push(ascii_capital) + } + + Locode::from(s) + } +} + +impl Arbitrary for UicCode { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mut s = String::new(); + + for _ in 0..2 { + let int = (u8::arbitrary(g) % 9) + 1; + s.push(int as char) + } + + UicCode::from(s) + } +} + +fn arbitrary_wrapped_decimal(g: &mut quickcheck::Gen) -> WrappedDecimal { + Decimal::from(u16::arbitrary(g)).into() +} + +fn arbitrary_option_wrapped_decimal(g: &mut quickcheck::Gen) -> Option { + let option = &[Some(arbitrary_wrapped_decimal(g)), None]; + + g.choose(option).unwrap().to_owned() +} + +impl Arbitrary for Incoterms { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let incoterms = &[ + Incoterms::Exw, + Incoterms::Fca, + Incoterms::Cpt, + Incoterms::Cip, + Incoterms::Dap, + Incoterms::Dpu, + Incoterms::Ddp, + Incoterms::Fas, + Incoterms::Fob, + Incoterms::Cfr, + Incoterms::Cif, + ]; + + g.choose(incoterms).unwrap().to_owned() + } +} + +#[cfg(test)] +mod tests { + use crate::{Hoc, ShipmentFootprint, Tce, Toc}; + use quickcheck_macros::quickcheck; + + #[quickcheck] + fn ser_and_deser_tce(tce: Tce) -> bool { + let serialized = serde_json::to_string(&tce).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + println!("tce: {tce:?}"); + println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + + deserialized == tce + // true + } + + #[quickcheck] + fn ser_and_deser_toc(toc: Toc) -> bool { + let serialized = serde_json::to_string(&toc).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + if deserialized != toc { + println!("toc: {toc:?}"); + // println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + } + + deserialized == toc + // true + } + + #[quickcheck] + fn ser_and_deser_hoc(hoc: Hoc) -> bool { + let serialized = serde_json::to_string(&hoc).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + if deserialized != hoc { + println!("toc: {hoc:?}"); + // println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + } + + deserialized == hoc + // true + } + + #[quickcheck] + fn ser_and_deser_ship_foot(ship_foot: ShipmentFootprint) { + let serialized = serde_json::to_string(&ship_foot).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + if deserialized != ship_foot { + println!("ship_foot: {ship_foot:?}"); + // println!("serialized: {serialized}"); + println!("deserialized: {deserialized:?}"); + } + + assert_eq!(deserialized, ship_foot); + } +} diff --git a/gen/src/lib.rs b/gen/src/lib.rs index cb84130..cfc31d2 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,13 +1,11 @@ //! iLEAP Data Model Extension data model -use std::iter; - -use chrono::{format, DateTime, Duration, Utc}; +use chrono::{DateTime, Utc}; use pact_data_model::{GeographicScope, WrappedDecimal}; -use quickcheck::Arbitrary; -use rust_decimal::Decimal; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +mod arbitrary_impls; + #[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ShipmentFootprint { @@ -420,453 +418,3 @@ impl From for GlecDataQualityIndex { } } } - -#[derive(Clone)] -pub struct LowerAToZNumDash(String); - -impl LowerAToZNumDash { - pub fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() - } - - pub fn len(&self) -> usize { - self.0.len() - } -} - -impl Arbitrary for LowerAToZNumDash { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let s: Vec<()> = Vec::arbitrary(g); - let s: String = s - .into_iter() - .map(|_| { - // ASCII characters -, 0..9, a..z - let i = u8::arbitrary(g) % 37; - match i { - 0 => '-', - 1..=10 => (i as u8 + 47) as char, - _ => (i as u8 + 86) as char, - } - }) - .collect(); - Self(s) - } - - fn shrink(&self) -> Box> { - let s = self.0.clone(); - let range = 0..self.len(); - let shrunk: Vec<_> = range - .into_iter() - .map(|len| Self(s[0..len].to_string())) - .collect(); - Box::new(shrunk.into_iter()) - } -} - -fn formatted_arbitrary_string(fixed: &str, g: &mut quickcheck::Gen) -> String { - fixed.to_string() + &LowerAToZNumDash::arbitrary(g).0 -} - -impl Arbitrary for ShipmentFootprint { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - ShipmentFootprint { - mass: String::arbitrary(g), - volume: Option::::arbitrary(g), - number_of_items: Option::::arbitrary(g), - type_of_items: Option::::arbitrary(g), - shipment_id: formatted_arbitrary_string("shipment-", g), - tces: NonEmptyVec::::arbitrary(g), - } - } -} - -impl Arbitrary for Hoc { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - Hoc { - hoc_id: formatted_arbitrary_string("hoc-", g), - description: Option::::arbitrary(g), - is_verified: bool::arbitrary(g), - is_accredited: bool::arbitrary(g), - hub_type: HubType::arbitrary(g), - temperature_control: Option::::arbitrary(g), - hub_location: Option::::arbitrary(g), - inbound_transport_mode: Option::::arbitrary(g), - outbound_transport_mode: Option::::arbitrary(g), - packaging_or_tr_eq_type: Option::::arbitrary(g), - packaging_or_tr_eq_amount: Option::::arbitrary(g), - energy_carriers: NonEmptyVec::::arbitrary(g), - co2e_intensity_wtw: arbitrary_wrapped_decimal(g), - co2e_intensity_ttw: arbitrary_wrapped_decimal(g), - co2e_intensity_throughput: String::arbitrary(g), - } - } -} - -impl Arbitrary for HubType { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let hub_type = &[ - HubType::Transshipment, - HubType::StorageAndTransshipment, - HubType::Warehouse, - HubType::LiquidBulkterminal, - HubType::MaritimeContainerterminal, - ]; - - g.choose(hub_type).unwrap().to_owned() - } -} - -impl Arbitrary for PackagingOrTrEqType { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let packaging_or_tr_eq_type = &[ - PackagingOrTrEqType::Box, - PackagingOrTrEqType::Pallet, - PackagingOrTrEqType::Container, - ]; - - g.choose(packaging_or_tr_eq_type).unwrap().to_owned() - } -} - -impl Arbitrary for Toc { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - Toc { - toc_id: formatted_arbitrary_string("toc-", g), - is_verified: bool::arbitrary(g), - is_accredited: bool::arbitrary(g), - description: Option::::arbitrary(g), - mode: TransportMode::arbitrary(g), - load_factor: Option::::arbitrary(g), - empty_distance_factor: Option::::arbitrary(g), - temperature_control: Option::::arbitrary(g), - truck_loading_sequence: Option::::arbitrary(g), - air_shipping_option: Option::::arbitrary(g), - flight_length: Option::::arbitrary(g), - energy_carriers: NonEmptyVec::::arbitrary(g), - co2e_intensity_wtw: arbitrary_wrapped_decimal(g), - co2e_intensity_ttw: arbitrary_wrapped_decimal(g), - co2e_intensity_throughput: String::arbitrary(g), - glec_data_quality_index: Option::::arbitrary(g), - } - } -} - -impl Arbitrary for NonEmptyVec { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - NonEmptyVec(vec![T::arbitrary(g)]) - } -} - -impl Arbitrary for TransportMode { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let transport_mode = &[ - TransportMode::Road, - TransportMode::Rail, - TransportMode::Air, - TransportMode::Sea, - TransportMode::InlandWaterway, - ]; - - g.choose(transport_mode).unwrap().to_owned() - } -} - -impl Arbitrary for TemperatureControl { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let temperature_control = &[ - TemperatureControl::Ambient, - TemperatureControl::Refrigerated, - TemperatureControl::Mixed, - ]; - - g.choose(temperature_control).unwrap().to_owned() - } -} - -impl Arbitrary for TruckLoadingSequence { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let truck_loading_sequence = &[TruckLoadingSequence::Ftl, TruckLoadingSequence::Ltl]; - - g.choose(truck_loading_sequence).unwrap().to_owned() - } -} - -impl Arbitrary for AirShippingOption { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let air_shipping_option = &[ - AirShippingOption::BellyFreight, - AirShippingOption::Freighter, - ]; - - g.choose(air_shipping_option).unwrap().to_owned() - } -} - -impl Arbitrary for FlightLength { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let flight_length = &[FlightLength::ShortHaul, FlightLength::LongHaul]; - - g.choose(flight_length).unwrap().to_owned() - } -} - -impl Arbitrary for EnergyCarrier { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - EnergyCarrier { - energy_carrier: EnergyCarrierType::arbitrary(g), - feedstocks: Option::>::arbitrary(g), - energy_consumption: arbitrary_option_wrapped_decimal(g), - energy_consumption_unit: Option::::arbitrary(g), - emission_factor_wtw: arbitrary_wrapped_decimal(g), - emission_factor_ttw: arbitrary_wrapped_decimal(g), - } - } -} - -impl Arbitrary for EnergyCarrierType { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let energy_carrier = &[ - EnergyCarrierType::Diesel, - EnergyCarrierType::Hvo, - EnergyCarrierType::Petrol, - EnergyCarrierType::Cng, - EnergyCarrierType::Lng, - EnergyCarrierType::Lpg, - EnergyCarrierType::Hfo, - EnergyCarrierType::Mgo, - EnergyCarrierType::AviationFuel, - EnergyCarrierType::Hydrogen, - EnergyCarrierType::Methanol, - EnergyCarrierType::Electric, - ]; - - g.choose(energy_carrier).unwrap().to_owned() - } -} - -impl Arbitrary for Feedstock { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - Feedstock { - feedstock: FeedstockType::arbitrary(g), - feedstock_percentage: arbitrary_option_wrapped_decimal(g), - region_provenance: Option::::arbitrary(g), - } - } -} - -impl Arbitrary for FeedstockType { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let feedstock = &[ - FeedstockType::Fossil, - FeedstockType::NaturalGas, - FeedstockType::Grid, - FeedstockType::RenewableElectricity, - FeedstockType::CookingOil, - ]; - - g.choose(feedstock).unwrap().to_owned() - } -} - -impl Arbitrary for GlecDataQualityIndex { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - GlecDataQualityIndex(u8::arbitrary(g) % 5) - } -} - -impl Arbitrary for Tce { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - Tce { - tce_id: formatted_arbitrary_string("tce-", g), - toc_id: Option::::arbitrary(g), - hoc_id: Option::::arbitrary(g), - shipment_id: String::arbitrary(g), - consignment_id: Option::::arbitrary(g), - mass: Decimal::from(u32::arbitrary(g)).into(), - packaging_or_tr_eq_type: Option::::arbitrary(g), - packaging_or_tr_eq_amount: Option::::arbitrary(g), - distance: GlecDistance::arbitrary(g), - origin: Option::::arbitrary(g), - destination: Option::::arbitrary(g), - transport_activity: arbitrary_wrapped_decimal(g), - departure_at: Option::>::from( - Utc::now() + Duration::days(u8::arbitrary(g) as i64), - ), - arrival_at: Option::>::from( - Utc::now() + Duration::days(u8::arbitrary(g) as i64), - ), - flight_no: Option::::arbitrary(g), - voyage_no: Option::::arbitrary(g), - incoterms: Option::::arbitrary(g), - co2e_wtw: Decimal::from(u32::arbitrary(g)).into(), - co2e_ttw: Decimal::from(u32::arbitrary(g)).into(), - nox_ttw: arbitrary_option_wrapped_decimal(g), - sox_ttw: arbitrary_option_wrapped_decimal(g), - ch4_ttw: arbitrary_option_wrapped_decimal(g), - pm_ttw: arbitrary_option_wrapped_decimal(g), - } - } -} - -impl Arbitrary for GlecDistance { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let glec_distance = &[ - GlecDistance::Actual(Decimal::from(u32::arbitrary(g)).into()), - GlecDistance::Gcd(Decimal::from(u32::arbitrary(g)).into()), - GlecDistance::Sfd(Decimal::from(u32::arbitrary(g)).into()), - ]; - - g.choose(glec_distance).unwrap().to_owned() - } -} - -impl Arbitrary for Location { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - Location { - street: Option::::arbitrary(g), - zip: Option::::arbitrary(g), - city: String::arbitrary(g), - country: GeographicScope::Country { - geography_country: pact_data_model::ISO3166CC(String::arbitrary(g)), - }, - iata: Option::::arbitrary(g), - locode: Option::::arbitrary(g), - uic: Option::::arbitrary(g), - lat: arbitrary_option_wrapped_decimal(g), - lng: arbitrary_option_wrapped_decimal(g), - } - } -} - -impl Arbitrary for IataCode { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let mut s = String::new(); - - for _ in 0..3 { - let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; - s.push(ascii_capital) - } - - IataCode::from(s) - } -} - -impl Arbitrary for Locode { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let mut s = String::new(); - - for _ in 0..5 { - // 65..90 - ASCII A to Z - let ascii_capital = ((u8::arbitrary(g) % 26) + 65) as char; - s.push(ascii_capital) - } - - Locode::from(s) - } -} - -impl Arbitrary for UicCode { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let mut s = String::new(); - - for _ in 0..2 { - let int = (u8::arbitrary(g) % 9) + 1; - s.push(int as char) - } - - UicCode::from(s) - } -} - -fn arbitrary_wrapped_decimal(g: &mut quickcheck::Gen) -> WrappedDecimal { - Decimal::from(u32::arbitrary(g)).into() -} - -fn arbitrary_option_wrapped_decimal(g: &mut quickcheck::Gen) -> Option { - let option = &[Some(arbitrary_wrapped_decimal(g)), None]; - - g.choose(option).unwrap().to_owned() -} - -impl Arbitrary for Incoterms { - fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let incoterms = &[ - Incoterms::Exw, - Incoterms::Fca, - Incoterms::Cpt, - Incoterms::Cip, - Incoterms::Dap, - Incoterms::Dpu, - Incoterms::Ddp, - Incoterms::Fas, - Incoterms::Fob, - Incoterms::Cfr, - Incoterms::Cif, - ]; - - g.choose(incoterms).unwrap().to_owned() - } -} - -#[cfg(test)] -mod tests { - use crate::{Hoc, ShipmentFootprint, Tce, Toc}; - use quickcheck_macros::quickcheck; - - #[quickcheck] - fn ser_and_deser_tce(tce: Tce) -> bool { - let serialized = serde_json::to_string(&tce).unwrap(); - let deserialized = serde_json::from_str::(&serialized).unwrap(); - - println!("tce: {tce:?}"); - println!("serialized: {serialized}"); - println!("deserialized: {deserialized:?}"); - - deserialized == tce - // true - } - - #[quickcheck] - fn ser_and_deser_toc(toc: Toc) -> bool { - let serialized = serde_json::to_string(&toc).unwrap(); - let deserialized = serde_json::from_str::(&serialized).unwrap(); - - if deserialized != toc { - println!("toc: {toc:?}"); - // println!("serialized: {serialized}"); - println!("deserialized: {deserialized:?}"); - } - - deserialized == toc - // true - } - - #[quickcheck] - fn ser_and_deser_hoc(hoc: Hoc) -> bool { - let serialized = serde_json::to_string(&hoc).unwrap(); - let deserialized = serde_json::from_str::(&serialized).unwrap(); - - if deserialized != hoc { - println!("toc: {hoc:?}"); - // println!("serialized: {serialized}"); - println!("deserialized: {deserialized:?}"); - } - - deserialized == hoc - // true - } - - #[quickcheck] - fn ser_and_deser_ship_foot(ship_foot: ShipmentFootprint) { - let serialized = serde_json::to_string(&ship_foot).unwrap(); - let deserialized = serde_json::from_str::(&serialized).unwrap(); - - if deserialized != ship_foot { - println!("ship_foot: {ship_foot:?}"); - // println!("serialized: {serialized}"); - println!("deserialized: {deserialized:?}"); - } - - assert_eq!(deserialized, ship_foot); - } -} diff --git a/gen/src/main.rs b/gen/src/main.rs index 48e9711..b193860 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -7,16 +7,16 @@ use std::fs::File; use std::io::{Error, Write}; fn main() -> Result<(), Error> { - generate_schema::()?; - generate_schema::()?; - generate_schema::()?; - generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; - // let mut og = Gen::new(10); + let mut og = Gen::new(10); - // let tce = Tce::arbitrary(&mut og); + let tce = Tce::arbitrary(&mut og); - // println!("tce: {tce:?}"); + println!("tce: {tce:?}"); Ok(()) } From 57e7ba194b75240d241bf00c67e57ebcce4e691d Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Thu, 4 Jul 2024 16:42:45 +0100 Subject: [PATCH 07/28] feat: generate random tce with toc or hoc as inputs --- gen/src/main.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/gen/src/main.rs b/gen/src/main.rs index b193860..5cf76f9 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -1,4 +1,5 @@ use ileap_extension::*; +use pact_data_model::WrappedDecimal; use quickcheck::{Arbitrary, Gen}; use regex::Regex; use schemars::schema_for; @@ -6,6 +7,39 @@ use serde_json::to_string_pretty; use std::fs::File; use std::io::{Error, Write}; +pub(crate) fn update_arbitrary_tce(tce: &mut Tce, toc: Option, hoc: Option) -> Tce { + tce.toc_id = match toc.clone() { + Some(toc) => Some(toc.toc_id), + None => None, + }; + + tce.hoc_id = match hoc.clone() { + Some(hoc) => Some(hoc.hoc_id), + None => None, + }; + + if (toc.is_none() && hoc.is_none()) && toc.is_some() && hoc.is_some() { + panic!("Either Toc or Hoc, but not both, must be provided"); + } + + match tce.toc_id { + Some(_) => { + let toc = toc.unwrap(); + tce.co2e_wtw = + WrappedDecimal::from(toc.co2e_intensity_wtw.0 * tce.transport_activity.0); + tce.co2e_ttw = + WrappedDecimal::from(toc.co2e_intensity_ttw.0 * tce.transport_activity.0); + } + None => { + let hoc = hoc.unwrap(); + tce.co2e_wtw = WrappedDecimal::from(hoc.co2e_intensity_wtw); + tce.co2e_ttw = WrappedDecimal::from(hoc.co2e_intensity_ttw); + } + } + + tce.to_owned() +} + fn main() -> Result<(), Error> { // generate_schema::()?; // generate_schema::()?; @@ -14,9 +48,17 @@ fn main() -> Result<(), Error> { let mut og = Gen::new(10); - let tce = Tce::arbitrary(&mut og); + let mut tce_1 = Tce::arbitrary(&mut og); + let toc = Toc::arbitrary(&mut og); + + let mut tce_2 = Tce::arbitrary(&mut og); + let hoc = Hoc::arbitrary(&mut og); + + let tce_toc = update_arbitrary_tce(&mut tce_1, Some(toc), None); + let tce_hoc = update_arbitrary_tce(&mut tce_2, None, Some(hoc)); - println!("tce: {tce:?}"); + println!("tce_toc: {tce_toc:?}"); + println!("tce_hoc: {tce_hoc:?}"); Ok(()) } From 111b33a551721ce8be3869364cd9b6bea07ee4aa Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Thu, 4 Jul 2024 18:03:08 +0100 Subject: [PATCH 08/28] feat: improve toc's energy carrier (and feedstocks) logic --- gen/src/arbitrary_impls.rs | 113 ++++++++++++++++++++++++++++++++++--- gen/src/main.rs | 2 + 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 31387c2..e61ef4f 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -107,21 +107,41 @@ impl Arbitrary for PackagingOrTrEqType { } } +fn arbitrary_option_factor(g: &mut quickcheck::Gen) -> Option { + let rand_num = u8::arbitrary(g) % 10 + 1; + let rand_factor: Decimal = Decimal::new(rand_num as i64, 1); + + Some(rand_factor.to_string()) +} + impl Arbitrary for Toc { fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mode = TransportMode::arbitrary(g); + + let (air_shipping_option, flight_length) = match mode { + TransportMode::Air => ( + Option::::arbitrary(g), + Option::::arbitrary(g), + ), + _ => (None, None), + }; + + let energy_carriers = NonEmptyVec::::arbitrary(g); + Toc { toc_id: formatted_arbitrary_string("toc-", g), is_verified: bool::arbitrary(g), is_accredited: bool::arbitrary(g), - description: Option::::arbitrary(g), - mode: TransportMode::arbitrary(g), - load_factor: Option::::arbitrary(g), - empty_distance_factor: Option::::arbitrary(g), + // TODO: description is currently None for simplicity. + description: None, + mode, + load_factor: arbitrary_option_factor(g), + empty_distance_factor: arbitrary_option_factor(g), temperature_control: Option::::arbitrary(g), truck_loading_sequence: Option::::arbitrary(g), - air_shipping_option: Option::::arbitrary(g), - flight_length: Option::::arbitrary(g), - energy_carriers: NonEmptyVec::::arbitrary(g), + air_shipping_option, + flight_length, + energy_carriers, co2e_intensity_wtw: arbitrary_wrapped_decimal(g), co2e_intensity_ttw: arbitrary_wrapped_decimal(g), co2e_intensity_throughput: String::arbitrary(g), @@ -191,9 +211,84 @@ impl Arbitrary for FlightLength { impl Arbitrary for EnergyCarrier { fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let energy_carrier = EnergyCarrierType::arbitrary(g); + + let feedstocks = Option::>::arbitrary(g); + + let Some(mut feedstocks) = feedstocks else { + return EnergyCarrier { + energy_carrier, + feedstocks, + // TODO: energy_consumption and energy_consumption_unit are currently None for simplicity. + energy_consumption: None, + energy_consumption_unit: None, + emission_factor_wtw: arbitrary_wrapped_decimal(g), + emission_factor_ttw: arbitrary_wrapped_decimal(g), + }; + }; + + // TODO: verify which feedstocks make sense for each energy carrier. + feedstocks = feedstocks + .iter() + .filter(|f| match energy_carrier { + EnergyCarrierType::Diesel => f.feedstock == FeedstockType::Fossil, + EnergyCarrierType::Hvo => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Petrol => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Cng => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Lng => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Lpg => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Hfo => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Mgo => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::AviationFuel => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Hydrogen => f.feedstock == FeedstockType::CookingOil, + EnergyCarrierType::Methanol => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Electric => { + f.feedstock == FeedstockType::Grid + || f.feedstock == FeedstockType::RenewableElectricity + } + }) + .cloned() + .collect::>(); + EnergyCarrier { - energy_carrier: EnergyCarrierType::arbitrary(g), - feedstocks: Option::>::arbitrary(g), + energy_carrier, + feedstocks: Some(feedstocks), energy_consumption: arbitrary_option_wrapped_decimal(g), energy_consumption_unit: Option::::arbitrary(g), emission_factor_wtw: arbitrary_wrapped_decimal(g), diff --git a/gen/src/main.rs b/gen/src/main.rs index 5cf76f9..5a2f1b3 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -51,6 +51,8 @@ fn main() -> Result<(), Error> { let mut tce_1 = Tce::arbitrary(&mut og); let toc = Toc::arbitrary(&mut og); + print!("toc: {toc:?}"); + let mut tce_2 = Tce::arbitrary(&mut og); let hoc = Hoc::arbitrary(&mut og); From 6ee88e00a5867d4bc68cbad0e33b26889966303c Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Mon, 8 Jul 2024 11:14:46 +0100 Subject: [PATCH 09/28] feat: improve arbitrary toc and hoc gen --- gen/Cargo.lock | 2 +- gen/Cargo.toml | 2 +- gen/src/arbitrary_impls.rs | 89 ++++++++++++++++++++++++++++++++------ gen/src/lib.rs | 2 +- 4 files changed, 79 insertions(+), 16 deletions(-) diff --git a/gen/Cargo.lock b/gen/Cargo.lock index cca6185..a2c3c92 100644 --- a/gen/Cargo.lock +++ b/gen/Cargo.lock @@ -312,7 +312,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "pact-data-model" version = "0.1.0" -source = "git+https://github.com/sine-fdn/pact-data-model.git?branch=wrapped_decimal#5ef50003b0c71c8d509e6a30ccecc030e9a661e2" +source = "git+https://github.com/sine-fdn/pact-data-model.git#e7454aa722db1e2c43076fe051a727c953df0b73" dependencies = [ "chrono", "rust_decimal", diff --git a/gen/Cargo.toml b/gen/Cargo.toml index e0954c3..59471c6 100644 --- a/gen/Cargo.toml +++ b/gen/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -pact-data-model = { git = "https://github.com/sine-fdn/pact-data-model.git", branch = "wrapped_decimal" } +pact-data-model = { git = "https://github.com/sine-fdn/pact-data-model.git" } chrono = { version = "0.4", features = ["serde"] } schemars = { version = "0.8", features = ["chrono"] } serde = { version = "1.0.203", features = ["derive"] } diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index e61ef4f..35a0812 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -61,33 +61,86 @@ impl Arbitrary for ShipmentFootprint { impl Arbitrary for Hoc { fn arbitrary(g: &mut quickcheck::Gen) -> Self { + fn gen_diff_transport_modes( + g: &mut quickcheck::Gen, + ) -> (Option, Option) { + let inbound = Some(TransportMode::arbitrary(g)); + let outbound = Some(TransportMode::arbitrary(g)); + + // TODO: verify if this requirement is correct. + if inbound == outbound { + (inbound, Some(TransportMode::arbitrary(g))) + } else { + (inbound, outbound) + } + } + + let hub_type = HubType::arbitrary(g); + + let (inbound_transport_mode, outbound_transport_mode) = match hub_type { + HubType::Transshipment => gen_diff_transport_modes(g), + + HubType::StorageAndTransshipment => gen_diff_transport_modes(g), + HubType::Warehouse => (Some(TransportMode::Road), Some(TransportMode::Road)), + HubType::LiquidBulkTerminal => ( + Some(TransportMode::arbitrary(g)), + Some(TransportMode::arbitrary(g)), + ), + HubType::MaritimeContainerterminal => { + let inbound = Option::::arbitrary(g); + let outbound = Option::::arbitrary(g); + + if inbound.clone().unwrap() != TransportMode::Sea + && outbound.clone().unwrap() != TransportMode::Sea + { + (Some(TransportMode::Sea), Some(TransportMode::Sea)) + } else { + (inbound, outbound) + } + } + }; + Hoc { hoc_id: formatted_arbitrary_string("hoc-", g), - description: Option::::arbitrary(g), + // TODO: description is currently None for simplicity. + description: None, is_verified: bool::arbitrary(g), is_accredited: bool::arbitrary(g), - hub_type: HubType::arbitrary(g), + hub_type, temperature_control: Option::::arbitrary(g), - hub_location: Option::::arbitrary(g), - inbound_transport_mode: Option::::arbitrary(g), - outbound_transport_mode: Option::::arbitrary(g), + // TODO: hub_location is currently None for simplicity. + hub_location: None, + inbound_transport_mode, + outbound_transport_mode, packaging_or_tr_eq_type: Option::::arbitrary(g), - packaging_or_tr_eq_amount: Option::::arbitrary(g), + // TODO: packaging_or_tr_eq_amount is currently None for simplicity. + packaging_or_tr_eq_amount: None, energy_carriers: NonEmptyVec::::arbitrary(g), co2e_intensity_wtw: arbitrary_wrapped_decimal(g), co2e_intensity_ttw: arbitrary_wrapped_decimal(g), - co2e_intensity_throughput: String::arbitrary(g), + co2e_intensity_throughput: HocCo2eIntensityThroughput::arbitrary(g), } } } +impl Arbitrary for HocCo2eIntensityThroughput { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let hoc_co2e_intensity_throughput = &[ + HocCo2eIntensityThroughput::TEU, + HocCo2eIntensityThroughput::Tonnes, + ]; + + g.choose(hoc_co2e_intensity_throughput).unwrap().to_owned() + } +} + impl Arbitrary for HubType { fn arbitrary(g: &mut quickcheck::Gen) -> Self { let hub_type = &[ HubType::Transshipment, HubType::StorageAndTransshipment, HubType::Warehouse, - HubType::LiquidBulkterminal, + HubType::LiquidBulkTerminal, HubType::MaritimeContainerterminal, ]; @@ -126,8 +179,6 @@ impl Arbitrary for Toc { _ => (None, None), }; - let energy_carriers = NonEmptyVec::::arbitrary(g); - Toc { toc_id: formatted_arbitrary_string("toc-", g), is_verified: bool::arbitrary(g), @@ -141,15 +192,27 @@ impl Arbitrary for Toc { truck_loading_sequence: Option::::arbitrary(g), air_shipping_option, flight_length, - energy_carriers, + energy_carriers: NonEmptyVec::::arbitrary(g), co2e_intensity_wtw: arbitrary_wrapped_decimal(g), co2e_intensity_ttw: arbitrary_wrapped_decimal(g), - co2e_intensity_throughput: String::arbitrary(g), - glec_data_quality_index: Option::::arbitrary(g), + co2e_intensity_throughput: TocCo2eIntensityThroughput::arbitrary(g), + // TODO: glec_data_quality_index is currently None for simplicity. + glec_data_quality_index: None, } } } +impl Arbitrary for TocCo2eIntensityThroughput { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let toc_co2e_intensity_throughput = &[ + TocCo2eIntensityThroughput::Tkm, + TocCo2eIntensityThroughput::TEUkm, + ]; + + g.choose(toc_co2e_intensity_throughput).unwrap().to_owned() + } +} + impl Arbitrary for NonEmptyVec { fn arbitrary(g: &mut quickcheck::Gen) -> Self { NonEmptyVec(vec![T::arbitrary(g)]) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index cfc31d2..462304b 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -187,7 +187,7 @@ pub struct Hoc { pub packaging_or_tr_eq_amount: Option, pub energy_carriers: NonEmptyVec, #[serde(rename = "co2eIntensityWTW")] - pub co2e_intensity_wtw: HocCo2eIntensityThroughput, + pub co2e_intensity_wtw: WrappedDecimal, #[serde(rename = "co2eIntensityTTW")] pub co2e_intensity_ttw: WrappedDecimal, pub co2e_intensity_throughput: HocCo2eIntensityThroughput, From c5bc47b3a0df77cb4e2659a9472e7a6d346b0f81 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Mon, 8 Jul 2024 11:53:22 +0100 Subject: [PATCH 10/28] fix: MaritimeContainer arbitrary --- gen/src/arbitrary_impls.rs | 30 ++++++++++++++++++++++-------- gen/src/main.rs | 4 +++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 35a0812..e9d85e9 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -90,13 +90,17 @@ impl Arbitrary for Hoc { let inbound = Option::::arbitrary(g); let outbound = Option::::arbitrary(g); - if inbound.clone().unwrap() != TransportMode::Sea - && outbound.clone().unwrap() != TransportMode::Sea - { - (Some(TransportMode::Sea), Some(TransportMode::Sea)) - } else { - (inbound, outbound) - } + let (inbound, outbound) = match (inbound.clone(), outbound.clone()) { + (None, None) => (inbound, outbound), + (Some(TransportMode::Sea), _) => (inbound, outbound), + (_, Some(TransportMode::Sea)) => (inbound, outbound), + _ => ( + Option::::arbitrary(g), + Option::::arbitrary(g), + ), + }; + + (inbound, outbound) } }; @@ -383,9 +387,19 @@ impl Arbitrary for EnergyCarrierType { impl Arbitrary for Feedstock { fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let feedstock_percentage = arbitrary_option_wrapped_decimal(g); + + let feedstock_percentage = match feedstock_percentage { + None => None, + Some(f) => { + let decimal = (f.0 / Decimal::from(u16::MAX)).round_dp(1); + Some(WrappedDecimal::from(decimal)) + } + }; + Feedstock { feedstock: FeedstockType::arbitrary(g), - feedstock_percentage: arbitrary_option_wrapped_decimal(g), + feedstock_percentage, region_provenance: Option::::arbitrary(g), } } diff --git a/gen/src/main.rs b/gen/src/main.rs index 5a2f1b3..6584479 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -51,11 +51,13 @@ fn main() -> Result<(), Error> { let mut tce_1 = Tce::arbitrary(&mut og); let toc = Toc::arbitrary(&mut og); - print!("toc: {toc:?}"); + println!("toc: {toc:?}"); let mut tce_2 = Tce::arbitrary(&mut og); let hoc = Hoc::arbitrary(&mut og); + println!("hoc: {hoc:?}"); + let tce_toc = update_arbitrary_tce(&mut tce_1, Some(toc), None); let tce_hoc = update_arbitrary_tce(&mut tce_2, None, Some(hoc)); From 5e89c902443c526bfef807512c19545e702d247a Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Thu, 11 Jul 2024 18:23:41 +0100 Subject: [PATCH 11/28] feat: implement logic to generate coherent set of ShipmentFootprints --- gen/schemas/hoc.json | 21 +++++-- gen/schemas/toc.json | 19 +++++- gen/src/arbitrary_impls.rs | 60 +++++++++++++++---- gen/src/lib.rs | 11 +++- gen/src/main.rs | 116 ++++++++++++++++++++----------------- 5 files changed, 155 insertions(+), 72 deletions(-) diff --git a/gen/schemas/hoc.json b/gen/schemas/hoc.json index 2938208..96afec8 100644 --- a/gen/schemas/hoc.json +++ b/gen/schemas/hoc.json @@ -20,7 +20,7 @@ "$ref": "#/definitions/HocCo2eIntensityThroughput" }, "co2eIntensityWTW": { - "$ref": "#/definitions/HocCo2eIntensityThroughput" + "$ref": "#/definitions/Decimal" }, "description": { "type": [ @@ -135,9 +135,13 @@ ] }, "energyConsumptionUnit": { - "type": [ - "string", - "null" + "anyOf": [ + { + "$ref": "#/definitions/EnergyConsumptionUnit" + }, + { + "type": "null" + } ] }, "feedstocks": { @@ -168,6 +172,15 @@ "Electric" ] }, + "EnergyConsumptionUnit": { + "type": "string", + "enum": [ + "l", + "kg", + "kWh", + "mJ" + ] + }, "Feedstock": { "type": "object", "required": [ diff --git a/gen/schemas/toc.json b/gen/schemas/toc.json index 5d388db..6e54090 100644 --- a/gen/schemas/toc.json +++ b/gen/schemas/toc.json @@ -146,9 +146,13 @@ ] }, "energyConsumptionUnit": { - "type": [ - "string", - "null" + "anyOf": [ + { + "$ref": "#/definitions/EnergyConsumptionUnit" + }, + { + "type": "null" + } ] }, "feedstocks": { @@ -179,6 +183,15 @@ "Electric" ] }, + "EnergyConsumptionUnit": { + "type": "string", + "enum": [ + "l", + "kg", + "kWh", + "mJ" + ] + }, "Feedstock": { "type": "object", "required": [ diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index e9d85e9..9ba5752 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -49,10 +49,12 @@ fn formatted_arbitrary_string(fixed: &str, g: &mut quickcheck::Gen) -> String { impl Arbitrary for ShipmentFootprint { fn arbitrary(g: &mut quickcheck::Gen) -> Self { ShipmentFootprint { - mass: String::arbitrary(g), - volume: Option::::arbitrary(g), - number_of_items: Option::::arbitrary(g), - type_of_items: Option::::arbitrary(g), + // TODO: the max of u32 is 4_294_967_295. Would this be too large a mass (in kg)? + mass: format!("{}", u32::arbitrary(g)), + // TODO: volume, number_of_items, and type_of_items are currently None for simplicity. + volume: None, + number_of_items: None, + type_of_items: None, shipment_id: formatted_arbitrary_string("shipment-", g), tces: NonEmptyVec::::arbitrary(g), } @@ -219,7 +221,13 @@ impl Arbitrary for TocCo2eIntensityThroughput { impl Arbitrary for NonEmptyVec { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - NonEmptyVec(vec![T::arbitrary(g)]) + let num = u8::arbitrary(g) % 10 + 1; + + let mut vec = vec![]; + for _ in 0..num { + vec.push(T::arbitrary(g)); + } + NonEmptyVec(vec) } } @@ -282,18 +290,36 @@ impl Arbitrary for EnergyCarrier { let feedstocks = Option::>::arbitrary(g); + // let mut total_percentage: Decimal = Decimal::from(0); + + // TODO: refactor to make drier. let Some(mut feedstocks) = feedstocks else { return EnergyCarrier { energy_carrier, feedstocks, - // TODO: energy_consumption and energy_consumption_unit are currently None for simplicity. - energy_consumption: None, - energy_consumption_unit: None, + energy_consumption: arbitrary_option_wrapped_decimal(g), + energy_consumption_unit: Option::::arbitrary(g), emission_factor_wtw: arbitrary_wrapped_decimal(g), emission_factor_ttw: arbitrary_wrapped_decimal(g), }; }; + let total_percentage: Decimal = feedstocks + .iter() + .map(|f| { + f.feedstock_percentage + .as_ref() + .map(|p| p.0) + .unwrap_or(Decimal::from(0)) + }) + .sum(); + + if total_percentage > Decimal::from(1) { + for feedstock in &mut feedstocks { + feedstock.feedstock_percentage = None + } + } + // TODO: verify which feedstocks make sense for each energy carrier. feedstocks = feedstocks .iter() @@ -357,7 +383,7 @@ impl Arbitrary for EnergyCarrier { energy_carrier, feedstocks: Some(feedstocks), energy_consumption: arbitrary_option_wrapped_decimal(g), - energy_consumption_unit: Option::::arbitrary(g), + energy_consumption_unit: Option::::arbitrary(g), emission_factor_wtw: arbitrary_wrapped_decimal(g), emission_factor_ttw: arbitrary_wrapped_decimal(g), } @@ -385,6 +411,19 @@ impl Arbitrary for EnergyCarrierType { } } +impl Arbitrary for EnergyConsumptionUnit { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let energy_consumption_unit = &[ + EnergyConsumptionUnit::KWh, + EnergyConsumptionUnit::MJ, + EnergyConsumptionUnit::Kg, + EnergyConsumptionUnit::L, + ]; + + g.choose(energy_consumption_unit).unwrap().to_owned() + } +} + impl Arbitrary for Feedstock { fn arbitrary(g: &mut quickcheck::Gen) -> Self { let feedstock_percentage = arbitrary_option_wrapped_decimal(g); @@ -400,7 +439,8 @@ impl Arbitrary for Feedstock { Feedstock { feedstock: FeedstockType::arbitrary(g), feedstock_percentage, - region_provenance: Option::::arbitrary(g), + // TODO: region_provenance is currently None for simplicity. + region_provenance: None, } } } diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 462304b..2cc0c28 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -302,7 +302,7 @@ pub struct EnergyCarrier { #[serde(skip_serializing_if = "Option::is_none")] pub energy_consumption: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub energy_consumption_unit: Option, + pub energy_consumption_unit: Option, #[serde(rename = "emissionFactorWTW")] pub emission_factor_wtw: WrappedDecimal, #[serde(rename = "emissionFactorTTW")] @@ -332,6 +332,15 @@ pub enum EnergyCarrierType { Electric, } +#[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq, Clone)] +#[serde(rename_all = "camelCase")] +pub enum EnergyConsumptionUnit { + L, + Kg, + KWh, + MJ, +} + #[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq, Clone)] #[serde(rename_all = "camelCase")] pub struct Feedstock { diff --git a/gen/src/main.rs b/gen/src/main.rs index 6584479..7134bd5 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -2,67 +2,20 @@ use ileap_extension::*; use pact_data_model::WrappedDecimal; use quickcheck::{Arbitrary, Gen}; use regex::Regex; +use rust_decimal::Decimal; use schemars::schema_for; use serde_json::to_string_pretty; use std::fs::File; use std::io::{Error, Write}; - -pub(crate) fn update_arbitrary_tce(tce: &mut Tce, toc: Option, hoc: Option) -> Tce { - tce.toc_id = match toc.clone() { - Some(toc) => Some(toc.toc_id), - None => None, - }; - - tce.hoc_id = match hoc.clone() { - Some(hoc) => Some(hoc.hoc_id), - None => None, - }; - - if (toc.is_none() && hoc.is_none()) && toc.is_some() && hoc.is_some() { - panic!("Either Toc or Hoc, but not both, must be provided"); - } - - match tce.toc_id { - Some(_) => { - let toc = toc.unwrap(); - tce.co2e_wtw = - WrappedDecimal::from(toc.co2e_intensity_wtw.0 * tce.transport_activity.0); - tce.co2e_ttw = - WrappedDecimal::from(toc.co2e_intensity_ttw.0 * tce.transport_activity.0); - } - None => { - let hoc = hoc.unwrap(); - tce.co2e_wtw = WrappedDecimal::from(hoc.co2e_intensity_wtw); - tce.co2e_ttw = WrappedDecimal::from(hoc.co2e_intensity_ttw); - } - } - - tce.to_owned() -} +use std::str::FromStr; fn main() -> Result<(), Error> { - // generate_schema::()?; - // generate_schema::()?; - // generate_schema::()?; - // generate_schema::()?; - - let mut og = Gen::new(10); - - let mut tce_1 = Tce::arbitrary(&mut og); - let toc = Toc::arbitrary(&mut og); + generate_schema::()?; + generate_schema::()?; + generate_schema::()?; + generate_schema::()?; - println!("toc: {toc:?}"); - - let mut tce_2 = Tce::arbitrary(&mut og); - let hoc = Hoc::arbitrary(&mut og); - - println!("hoc: {hoc:?}"); - - let tce_toc = update_arbitrary_tce(&mut tce_1, Some(toc), None); - let tce_hoc = update_arbitrary_tce(&mut tce_2, None, Some(hoc)); - - println!("tce_toc: {tce_toc:?}"); - println!("tce_hoc: {tce_hoc:?}"); + generate_demo_data()?; Ok(()) } @@ -92,3 +45,58 @@ fn generate_schema() -> Result<(), Error> { Ok(()) } + +fn generate_demo_data() -> Result<(), Error> { + let mut og = Gen::new(10); + + let mut shipment_footprints = vec![]; + let mut tocs = vec![]; + let mut hocs = vec![]; + for _ in 0..1 { + let mut ship_foot = ShipmentFootprint::arbitrary(&mut og); + + let mut tces = vec![]; + for tce in ship_foot.tces.0.iter() { + let mut tce = tce.to_owned(); + tce.shipment_id = ship_foot.shipment_id.clone(); + + tce.mass = WrappedDecimal::from(Decimal::from_str(&ship_foot.mass).unwrap()); + + if (tce.toc_id.is_none() && tce.hoc_id.is_none()) + || tce.toc_id.is_some() && tce.hoc_id.is_some() + { + panic!("Either Toc or Hoc, but not both, must be provided"); + } else if tce.toc_id.is_some() { + let mut toc = Toc::arbitrary(&mut og); + toc.toc_id = tce.toc_id.clone().unwrap(); + tocs.push(toc.clone()); + + tce.toc_id = Some(toc.toc_id.clone()); + tce.co2e_wtw = + WrappedDecimal::from(toc.co2e_intensity_wtw.0 * tce.transport_activity.0); + tce.co2e_ttw = + WrappedDecimal::from(toc.co2e_intensity_ttw.0 * tce.transport_activity.0); + } else { + let mut hoc = Hoc::arbitrary(&mut og); + hoc.hoc_id = tce.hoc_id.clone().unwrap(); + hocs.push(hoc.clone()); + + tce.hoc_id = Some(hoc.hoc_id.clone()); + + // TODO: Implement TCE HOC emissions calculations. + tce.co2e_wtw = WrappedDecimal::from(hoc.co2e_intensity_wtw); + tce.co2e_ttw = WrappedDecimal::from(hoc.co2e_intensity_ttw); + } + + tces.push(tce); + } + ship_foot.tces = NonEmptyVec::from(tces); + shipment_footprints.push(ship_foot); + } + + println!("{shipment_footprints:?}"); + println!("{tocs:?}"); + println!("{hocs:?}"); + + Ok(()) +} From d661b94e26566e03adc2e96c676180a7a44e3aad Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 27 Aug 2024 16:23:25 +0100 Subject: [PATCH 12/28] feat: implement prev_tce_ids ordering --- gen/Cargo.lock | 233 ++++++++++++++++++++++--------------- gen/src/arbitrary_impls.rs | 2 + gen/src/main.rs | 24 ++-- 3 files changed, 154 insertions(+), 105 deletions(-) diff --git a/gen/Cargo.lock b/gen/Cargo.lock index a2c3c92..e35e321 100644 --- a/gen/Cargo.lock +++ b/gen/Cargo.lock @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" @@ -81,7 +81,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.76", "syn_derive", ] @@ -113,17 +113,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -154,9 +163,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "dyn-clone" @@ -253,9 +262,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -269,30 +278,30 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-traits" @@ -312,7 +321,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "pact-data-model" version = "0.1.0" -source = "git+https://github.com/sine-fdn/pact-data-model.git#e7454aa722db1e2c43076fe051a727c953df0b73" +source = "git+https://github.com/sine-fdn/pact-data-model.git#4b6b38f8d3e8acacb1b5b06efe4fa88e2a0a4b54" dependencies = [ "chrono", "rust_decimal", @@ -324,15 +333,18 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] @@ -362,9 +374,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -413,9 +425,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -458,9 +470,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -470,9 +482,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -481,9 +493,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rend" @@ -496,9 +508,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ "bitvec", "bytecheck", @@ -514,9 +526,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ "proc-macro2", "quote", @@ -525,9 +537,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", "borsh", @@ -541,9 +553,9 @@ dependencies = [ [[package]] name = "rust_decimal_macros" -version = "1.34.2" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e418701588729bef95e7a655f2b483ad64bb97c46e8e79fde83efd92aaab6d82" +checksum = "da991f231869f34268415a49724c6578e740ad697ba0999199d6f22b3949332c" dependencies = [ "quote", "rust_decimal", @@ -577,7 +589,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.66", + "syn 2.0.76", ] [[package]] @@ -588,22 +600,22 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.76", ] [[package]] @@ -614,20 +626,27 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.76", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "simdutf8" version = "0.1.4" @@ -647,9 +666,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", @@ -665,7 +684,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.76", ] [[package]] @@ -676,9 +695,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -691,15 +710,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "toml_datetime", @@ -714,9 +733,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "serde", @@ -724,9 +743,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -736,34 +755,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.76", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -771,22 +791,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.76", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "windows-core" @@ -799,9 +819,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -815,57 +835,57 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -878,3 +898,24 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.76", +] diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 9ba5752..6099a64 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -523,6 +523,8 @@ impl Arbitrary for Tce { Tce { tce_id: formatted_arbitrary_string("tce-", g), + // Empty vec by default, populated by the generator function on main. + prev_tce_ids: Some(vec![]), toc_id, hoc_id, shipment_id: formatted_arbitrary_string("shipment-", g), diff --git a/gen/src/main.rs b/gen/src/main.rs index 7134bd5..b2cf13f 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -10,10 +10,10 @@ use std::io::{Error, Write}; use std::str::FromStr; fn main() -> Result<(), Error> { - generate_schema::()?; - generate_schema::()?; - generate_schema::()?; - generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; + // generate_schema::()?; generate_demo_data()?; @@ -55,11 +55,17 @@ fn generate_demo_data() -> Result<(), Error> { for _ in 0..1 { let mut ship_foot = ShipmentFootprint::arbitrary(&mut og); - let mut tces = vec![]; - for tce in ship_foot.tces.0.iter() { + let mut tces: Vec = vec![]; + let mut prev_tces: Vec = vec![]; + for (i, tce) in ship_foot.tces.0.iter().enumerate() { let mut tce = tce.to_owned(); tce.shipment_id = ship_foot.shipment_id.clone(); + if i != 0 { + prev_tces.push(tces[i - 1].tce_id.clone()); + tce.prev_tce_ids = Some(prev_tces.clone()); + } + tce.mass = WrappedDecimal::from(Decimal::from_str(&ship_foot.mass).unwrap()); if (tce.toc_id.is_none() && tce.hoc_id.is_none()) @@ -94,9 +100,9 @@ fn generate_demo_data() -> Result<(), Error> { shipment_footprints.push(ship_foot); } - println!("{shipment_footprints:?}"); - println!("{tocs:?}"); - println!("{hocs:?}"); + println!("{shipment_footprints:#?}"); + // println!("{tocs:?}"); + // println!("{hocs:?}"); Ok(()) } From b1cf87d04e846e4f1b8079817edf66ea27450da6 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 27 Aug 2024 17:58:04 +0100 Subject: [PATCH 13/28] feat: include TCE HOC emissions calculations --- gen/src/arbitrary_impls.rs | 39 +++++++++++--------------------------- gen/src/main.rs | 14 +++++++++++--- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 6099a64..1a610b0 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -49,8 +49,8 @@ fn formatted_arbitrary_string(fixed: &str, g: &mut quickcheck::Gen) -> String { impl Arbitrary for ShipmentFootprint { fn arbitrary(g: &mut quickcheck::Gen) -> Self { ShipmentFootprint { - // TODO: the max of u32 is 4_294_967_295. Would this be too large a mass (in kg)? - mass: format!("{}", u32::arbitrary(g)), + // Using u16 to avoid unreadably large + mass: format!("{}", u16::arbitrary(g)), // TODO: volume, number_of_items, and type_of_items are currently None for simplicity. volume: None, number_of_items: None, @@ -221,7 +221,8 @@ impl Arbitrary for TocCo2eIntensityThroughput { impl Arbitrary for NonEmptyVec { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let num = u8::arbitrary(g) % 10 + 1; + // Restricting to 1..5 elements in order to avoid unreadably large data. + let num = u8::arbitrary(g) % 5 + 1; let mut vec = vec![]; for _ in 0..num { @@ -503,24 +504,6 @@ impl Arbitrary for Tce { } }; - let co2e_wtw = WrappedDecimal::from(match toc_id { - // TODO: toc.co2e_intensity_wtw is currently hardcoded, based on the toc-road-1 example. - Some(_) => Decimal::new(116, 3) * transport_activity.0, - // TODO: if toc_id is None, then we must use hoc.co2e_intensity_wtw; this is currently - // hardcoded, based on the hoc-transshipment-1 example. However, I do not know how to - // calculate it. - None => Decimal::new(33, 0), - }); - - let co2e_ttw = WrappedDecimal::from(match toc_id { - // TODO: toc.co2e_intensity_ttw is currently hardcoded, based on the toc-road-1 example. - Some(_) => Decimal::new(89, 3) * transport_activity.0, - // TODO: if toc_id is None, then we must use hoc.co2e_intensity_ttw; this is currently - // hardcoded, based on the hoc-transshipment-1 example. However, I do not know how to - // calculate it. - None => Decimal::new(10, 0), - }); - Tce { tce_id: formatted_arbitrary_string("tce-", g), // Empty vec by default, populated by the generator function on main. @@ -528,8 +511,7 @@ impl Arbitrary for Tce { toc_id, hoc_id, shipment_id: formatted_arbitrary_string("shipment-", g), - // TODO: consignment_id is currently None for simplicity. - consignment_id: None, + consignment_id: Some(formatted_arbitrary_string("consignment-", g)), mass, packaging_or_tr_eq_type: Option::::arbitrary(g), packaging_or_tr_eq_amount: Option::::arbitrary(g), @@ -543,13 +525,14 @@ impl Arbitrary for Tce { transport_activity, departure_at, arrival_at, - // TODO: flight_no and voyage_no are currently None for simplicity. + incoterms: Option::::arbitrary(g), + // co2eWTW and co2eTTW are populated by the generator function on main, based on the + // emissions profile of the TOC/HOC. + co2e_wtw: Decimal::from(0).into(), + co2e_ttw: Decimal::from(0).into(), + // TODO: the following fields are currently None for simplicity. flight_no: None, voyage_no: None, - incoterms: Option::::arbitrary(g), - co2e_wtw, - co2e_ttw, - // TODO: all the following fields are currently None for simplicity. nox_ttw: None, sox_ttw: None, ch4_ttw: None, diff --git a/gen/src/main.rs b/gen/src/main.rs index b2cf13f..89020bc 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -55,6 +55,7 @@ fn generate_demo_data() -> Result<(), Error> { for _ in 0..1 { let mut ship_foot = ShipmentFootprint::arbitrary(&mut og); + // TODO: ensure that two HOCs do not follow one another. let mut tces: Vec = vec![]; let mut prev_tces: Vec = vec![]; for (i, tce) in ship_foot.tces.0.iter().enumerate() { @@ -89,9 +90,16 @@ fn generate_demo_data() -> Result<(), Error> { tce.hoc_id = Some(hoc.hoc_id.clone()); - // TODO: Implement TCE HOC emissions calculations. - tce.co2e_wtw = WrappedDecimal::from(hoc.co2e_intensity_wtw); - tce.co2e_ttw = WrappedDecimal::from(hoc.co2e_intensity_ttw); + tce.distance = GlecDistance::Actual(Decimal::from(0).into()); + tce.transport_activity = Decimal::from(0).into(); + + // TODO: Double-check divisor + tce.co2e_wtw = WrappedDecimal::from( + (hoc.co2e_intensity_wtw.0 * tce.mass.0) / Decimal::from(1000000), + ); + tce.co2e_ttw = WrappedDecimal::from( + (hoc.co2e_intensity_ttw.0 * tce.mass.0) / Decimal::from(1000000), + ); } tces.push(tce); From de4492cc57ff97d3a694e478863ab4ede68335c1 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 28 Aug 2024 09:18:32 +0100 Subject: [PATCH 14/28] chore: merge main --- gen/Cargo.lock | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gen/Cargo.lock b/gen/Cargo.lock index 0db4b57..8f4c926 100644 --- a/gen/Cargo.lock +++ b/gen/Cargo.lock @@ -127,9 +127,12 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" From bf801139cf781c3df469527560f2bdf0ed508339 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 28 Aug 2024 09:34:13 +0100 Subject: [PATCH 15/28] feat: generate complete Shipment ProductFootprint --- gen/src/lib.rs | 2 +- gen/src/main.rs | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 741c2b6..c613bd8 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -524,7 +524,7 @@ pub fn to_pcf( .tces .0 .iter() - .fold(Decimal::from(0), |acc, tce| acc + tce.co2e_wtw.0) + .fold(Decimal::from(0), |acc, tce| acc + tce.transport_activity.0) .into(), p_cf_excluding_biogenic: shipment .tces diff --git a/gen/src/main.rs b/gen/src/main.rs index 461542b..ebd74f0 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -1,5 +1,5 @@ use ileap_extension::*; -use pact_data_model::WrappedDecimal; +use pact_data_model::{Urn, WrappedDecimal}; use quickcheck::{Arbitrary, Gen}; use regex::Regex; use rust_decimal::Decimal; @@ -34,8 +34,8 @@ fn generate_schema() -> Result<(), Error> { let schema = schema_for!(T); - let schema_json = - to_string_pretty(&schema).unwrap_or_else(|_| panic!("Failed to serialize {type_name} schema")); + let schema_json = to_string_pretty(&schema) + .unwrap_or_else(|_| panic!("Failed to serialize {type_name} schema")); let mut schema_file = File::create(format!("./schemas/{schema_name}.json"))?; @@ -105,6 +105,15 @@ fn generate_demo_data() -> Result<(), Error> { tces.push(tce); } ship_foot.tces = NonEmptyVec::from(tces); + + let ship_foot = to_pcf( + ILeapType::ShipmentFootprint(ship_foot), + "SINE Foundation", + "urn:sine:example", + Some(HocTeuContainerSize::Normal), + Some(vec![CharFactors::Ar6]), + ); + shipment_footprints.push(ship_foot); } From 035e71ac28ec675f09767cc0480e4e112ea7d9ed Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 28 Aug 2024 16:36:53 +0100 Subject: [PATCH 16/28] feat: generate TOC and HOC Footprints and improve TC logic --- gen/src/arbitrary_impls.rs | 11 +++-- gen/src/lib.rs | 11 +++++ gen/src/main.rs | 93 ++++++++++++++++++++++++++++---------- 3 files changed, 85 insertions(+), 30 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 1a610b0..7fda289 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -221,7 +221,7 @@ impl Arbitrary for TocCo2eIntensityThroughput { impl Arbitrary for NonEmptyVec { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - // Restricting to 1..5 elements in order to avoid unreadably large data. + // Restricting to 1..5 elements. let num = u8::arbitrary(g) % 5 + 1; let mut vec = vec![]; @@ -544,9 +544,10 @@ impl Arbitrary for Tce { impl Arbitrary for GlecDistance { fn arbitrary(g: &mut quickcheck::Gen) -> Self { let glec_distance = &[ - GlecDistance::Actual(Decimal::from(u16::arbitrary(g)).into()), - GlecDistance::Gcd(Decimal::from(u16::arbitrary(g)).into()), - GlecDistance::Sfd(Decimal::from(u16::arbitrary(g)).into()), + // Dividing u16 by 2 to avoid unreadably large data. + GlecDistance::Actual(Decimal::from(u16::arbitrary(g) / 2).into()), + GlecDistance::Gcd(Decimal::from(u16::arbitrary(g) / 2).into()), + GlecDistance::Sfd(Decimal::from(u16::arbitrary(g) / 2).into()), ]; g.choose(glec_distance).unwrap().to_owned() @@ -612,7 +613,7 @@ impl Arbitrary for UicCode { } fn arbitrary_wrapped_decimal(g: &mut quickcheck::Gen) -> WrappedDecimal { - Decimal::from(u16::arbitrary(g)).into() + Decimal::from(u16::arbitrary(g)).round_dp(2).into() } fn arbitrary_option_wrapped_decimal(g: &mut quickcheck::Gen) -> Option { diff --git a/gen/src/lib.rs b/gen/src/lib.rs index c613bd8..9848ab4 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -262,6 +262,16 @@ pub enum GlecDistance { Sfd(WrappedDecimal), } +impl GlecDistance { + pub fn get_distance(&self) -> Decimal { + match self { + GlecDistance::Actual(decimal) => decimal.0, + GlecDistance::Gcd(decimal) => decimal.0, + GlecDistance::Sfd(decimal) => decimal.0, + } + } +} + #[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq, Clone)] #[serde(rename_all = "camelCase")] pub struct Location { @@ -531,6 +541,7 @@ pub fn to_pcf( .0 .iter() .fold(Decimal::from(0), |acc, tce| acc + tce.co2e_wtw.0) + .round_dp(2) .into(), }, ILeapType::Toc(ref toc) => MappedFields { diff --git a/gen/src/main.rs b/gen/src/main.rs index ebd74f0..ffabaff 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -55,38 +55,39 @@ fn generate_demo_data() -> Result<(), Error> { for _ in 0..1 { let mut ship_foot = ShipmentFootprint::arbitrary(&mut og); - // TODO: ensure that two HOCs do not follow one another. let mut tces: Vec = vec![]; let mut prev_tces: Vec = vec![]; - for (i, tce) in ship_foot.tces.0.iter().enumerate() { - let mut tce = tce.to_owned(); - tce.shipment_id = ship_foot.shipment_id.clone(); - if i != 0 { - prev_tces.push(tces[i - 1].tce_id.clone()); - tce.prev_tce_ids = Some(prev_tces.clone()); - } - - tce.mass = WrappedDecimal::from(Decimal::from_str(&ship_foot.mass).unwrap()); + let mut i = 0; + let limit = u8::arbitrary(&mut og) % 5 + 1; + loop { + let mut tce = Tce::arbitrary(&mut og); if (tce.toc_id.is_none() && tce.hoc_id.is_none()) || tce.toc_id.is_some() && tce.hoc_id.is_some() { panic!("Either Toc or Hoc, but not both, must be provided"); - } else if tce.toc_id.is_some() { - let mut toc = Toc::arbitrary(&mut og); - toc.toc_id = tce.toc_id.clone().unwrap(); - tocs.push(toc.clone()); + } + + if let Some(prev_tce) = tces.last() { + // Updates prevTceIds for the current TCE + prev_tces.push(prev_tce.tce_id.clone()); + tce.prev_tce_ids = Some(prev_tces.clone()); + + // Avoids having two HOCs follow one another + if prev_tce.hoc_id.is_some() && tce.hoc_id.is_some() { + continue; + } + }; + + if tce.hoc_id.is_some() { + // Avoids having an HOC as the first or the last TCE + if i == 0 || i == limit - 1 { + continue; + } - tce.toc_id = Some(toc.toc_id.clone()); - tce.co2e_wtw = - WrappedDecimal::from(toc.co2e_intensity_wtw.0 * tce.transport_activity.0); - tce.co2e_ttw = - WrappedDecimal::from(toc.co2e_intensity_ttw.0 * tce.transport_activity.0); - } else { let mut hoc = Hoc::arbitrary(&mut og); hoc.hoc_id = tce.hoc_id.clone().unwrap(); - hocs.push(hoc.clone()); tce.hoc_id = Some(hoc.hoc_id.clone()); @@ -95,15 +96,57 @@ fn generate_demo_data() -> Result<(), Error> { // TODO: Double-check divisor tce.co2e_wtw = WrappedDecimal::from( - (hoc.co2e_intensity_wtw.0 * tce.mass.0) / Decimal::from(1000000), + ((hoc.co2e_intensity_wtw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), + ); + tce.co2e_ttw = WrappedDecimal::from( + ((hoc.co2e_intensity_ttw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), + ); + + let hoc = to_pcf( + ILeapType::Hoc(hoc), + "SINE Foundation", + "urn:sine:example", + Some(HocTeuContainerSize::Normal), + Some(vec![CharFactors::Ar6]), + ); + + hocs.push(hoc); + } + + if tce.toc_id.is_some() { + let mut toc = Toc::arbitrary(&mut og); + toc.toc_id = tce.toc_id.clone().unwrap(); + + tce.transport_activity = (tce.mass.0 * tce.distance.get_distance()) + .round_dp(2) + .into(); + + tce.toc_id = Some(toc.toc_id.clone()); + tce.co2e_wtw = WrappedDecimal::from( + (toc.co2e_intensity_wtw.0 * tce.transport_activity.0).round_dp(2), ); tce.co2e_ttw = WrappedDecimal::from( - (hoc.co2e_intensity_ttw.0 * tce.mass.0) / Decimal::from(1000000), + (toc.co2e_intensity_ttw.0 * tce.transport_activity.0).round_dp(2), + ); + + let toc = to_pcf( + ILeapType::Toc(toc), + "SINE Foundation", + "urn:sine:example", + None, + Some(vec![CharFactors::Ar6]), ); + + tocs.push(toc.clone()); } tces.push(tce); + i += 1; + if i == limit { + break; + } } + ship_foot.tces = NonEmptyVec::from(tces); let ship_foot = to_pcf( @@ -118,8 +161,8 @@ fn generate_demo_data() -> Result<(), Error> { } println!("{shipment_footprints:#?}"); - // println!("{tocs:?}"); - // println!("{hocs:?}"); + println!("{tocs:#?}"); + println!("{hocs:#?}"); Ok(()) } From 8a70b0acb188a1bf00e049e1324b57bc402bead8 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 28 Aug 2024 17:13:34 +0100 Subject: [PATCH 17/28] refactor: make code dryer and remove unnecessary comments --- gen/src/arbitrary_impls.rs | 203 +++++++++++++++++-------------------- gen/src/main.rs | 8 +- 2 files changed, 97 insertions(+), 114 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 7fda289..387bc0b 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -51,12 +51,12 @@ impl Arbitrary for ShipmentFootprint { ShipmentFootprint { // Using u16 to avoid unreadably large mass: format!("{}", u16::arbitrary(g)), - // TODO: volume, number_of_items, and type_of_items are currently None for simplicity. + shipment_id: formatted_arbitrary_string("shipment-", g), + tces: NonEmptyVec::::arbitrary(g), + // Currently None for simplicity. volume: None, number_of_items: None, type_of_items: None, - shipment_id: formatted_arbitrary_string("shipment-", g), - tces: NonEmptyVec::::arbitrary(g), } } } @@ -69,7 +69,6 @@ impl Arbitrary for Hoc { let inbound = Some(TransportMode::arbitrary(g)); let outbound = Some(TransportMode::arbitrary(g)); - // TODO: verify if this requirement is correct. if inbound == outbound { (inbound, Some(TransportMode::arbitrary(g))) } else { @@ -80,8 +79,9 @@ impl Arbitrary for Hoc { let hub_type = HubType::arbitrary(g); let (inbound_transport_mode, outbound_transport_mode) = match hub_type { + // TODO: verify whether Transshipment and StorageAndTransshipment require different + // inbound and outbound transport modes. HubType::Transshipment => gen_diff_transport_modes(g), - HubType::StorageAndTransshipment => gen_diff_transport_modes(g), HubType::Warehouse => (Some(TransportMode::Road), Some(TransportMode::Road)), HubType::LiquidBulkTerminal => ( @@ -108,23 +108,21 @@ impl Arbitrary for Hoc { Hoc { hoc_id: formatted_arbitrary_string("hoc-", g), - // TODO: description is currently None for simplicity. - description: None, is_verified: bool::arbitrary(g), is_accredited: bool::arbitrary(g), hub_type, temperature_control: Option::::arbitrary(g), - // TODO: hub_location is currently None for simplicity. - hub_location: None, inbound_transport_mode, outbound_transport_mode, packaging_or_tr_eq_type: Option::::arbitrary(g), - // TODO: packaging_or_tr_eq_amount is currently None for simplicity. - packaging_or_tr_eq_amount: None, energy_carriers: NonEmptyVec::::arbitrary(g), co2e_intensity_wtw: arbitrary_wrapped_decimal(g), co2e_intensity_ttw: arbitrary_wrapped_decimal(g), co2e_intensity_throughput: HocCo2eIntensityThroughput::arbitrary(g), + // Currently None for simplicity. + description: None, + hub_location: None, + packaging_or_tr_eq_amount: None, } } } @@ -189,8 +187,6 @@ impl Arbitrary for Toc { toc_id: formatted_arbitrary_string("toc-", g), is_verified: bool::arbitrary(g), is_accredited: bool::arbitrary(g), - // TODO: description is currently None for simplicity. - description: None, mode, load_factor: arbitrary_option_factor(g), empty_distance_factor: arbitrary_option_factor(g), @@ -202,7 +198,8 @@ impl Arbitrary for Toc { co2e_intensity_wtw: arbitrary_wrapped_decimal(g), co2e_intensity_ttw: arbitrary_wrapped_decimal(g), co2e_intensity_throughput: TocCo2eIntensityThroughput::arbitrary(g), - // TODO: glec_data_quality_index is currently None for simplicity. + // Currently None for simplicity. + description: None, glec_data_quality_index: None, } } @@ -289,100 +286,91 @@ impl Arbitrary for EnergyCarrier { fn arbitrary(g: &mut quickcheck::Gen) -> Self { let energy_carrier = EnergyCarrierType::arbitrary(g); - let feedstocks = Option::>::arbitrary(g); - - // let mut total_percentage: Decimal = Decimal::from(0); - - // TODO: refactor to make drier. - let Some(mut feedstocks) = feedstocks else { - return EnergyCarrier { - energy_carrier, - feedstocks, - energy_consumption: arbitrary_option_wrapped_decimal(g), - energy_consumption_unit: Option::::arbitrary(g), - emission_factor_wtw: arbitrary_wrapped_decimal(g), - emission_factor_ttw: arbitrary_wrapped_decimal(g), - }; - }; - - let total_percentage: Decimal = feedstocks - .iter() - .map(|f| { - f.feedstock_percentage - .as_ref() - .map(|p| p.0) - .unwrap_or(Decimal::from(0)) - }) - .sum(); + let feedstocks = match Option::>::arbitrary(g) { + Some(mut feedstocks) => { + let total_percentage: Decimal = feedstocks + .iter() + .map(|f| { + f.feedstock_percentage + .as_ref() + .map(|p| p.0) + .unwrap_or(Decimal::from(0)) + }) + .sum(); + + if total_percentage > Decimal::from(1) { + for feedstock in &mut feedstocks { + feedstock.feedstock_percentage = None + } + } - if total_percentage > Decimal::from(1) { - for feedstock in &mut feedstocks { - feedstock.feedstock_percentage = None + // TODO: verify which feedstocks make sense for each energy carrier. + feedstocks = feedstocks + .iter() + .filter(|f| match energy_carrier { + EnergyCarrierType::Diesel => f.feedstock == FeedstockType::Fossil, + EnergyCarrierType::Hvo => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Petrol => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Cng => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Lng => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Lpg => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Hfo => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Mgo => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::AviationFuel => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Hydrogen => f.feedstock == FeedstockType::CookingOil, + EnergyCarrierType::Methanol => { + f.feedstock == FeedstockType::Fossil + || f.feedstock == FeedstockType::NaturalGas + || f.feedstock == FeedstockType::CookingOil + } + EnergyCarrierType::Electric => { + f.feedstock == FeedstockType::Grid + || f.feedstock == FeedstockType::RenewableElectricity + } + }) + .cloned() + .collect::>(); + + Some(feedstocks) } - } - - // TODO: verify which feedstocks make sense for each energy carrier. - feedstocks = feedstocks - .iter() - .filter(|f| match energy_carrier { - EnergyCarrierType::Diesel => f.feedstock == FeedstockType::Fossil, - EnergyCarrierType::Hvo => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Petrol => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Cng => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Lng => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Lpg => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Hfo => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Mgo => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::AviationFuel => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Hydrogen => f.feedstock == FeedstockType::CookingOil, - EnergyCarrierType::Methanol => { - f.feedstock == FeedstockType::Fossil - || f.feedstock == FeedstockType::NaturalGas - || f.feedstock == FeedstockType::CookingOil - } - EnergyCarrierType::Electric => { - f.feedstock == FeedstockType::Grid - || f.feedstock == FeedstockType::RenewableElectricity - } - }) - .cloned() - .collect::>(); + None => None, + }; EnergyCarrier { energy_carrier, - feedstocks: Some(feedstocks), + feedstocks, energy_consumption: arbitrary_option_wrapped_decimal(g), energy_consumption_unit: Option::::arbitrary(g), emission_factor_wtw: arbitrary_wrapped_decimal(g), @@ -440,7 +428,7 @@ impl Arbitrary for Feedstock { Feedstock { feedstock: FeedstockType::arbitrary(g), feedstock_percentage, - // TODO: region_provenance is currently None for simplicity. + // Currently None for simplicity. region_provenance: None, } } @@ -530,7 +518,7 @@ impl Arbitrary for Tce { // emissions profile of the TOC/HOC. co2e_wtw: Decimal::from(0).into(), co2e_ttw: Decimal::from(0).into(), - // TODO: the following fields are currently None for simplicity. + // Currently None for simplicity. flight_no: None, voyage_no: None, nox_ttw: None, @@ -657,7 +645,6 @@ mod tests { println!("deserialized: {deserialized:?}"); deserialized == tce - // true } #[quickcheck] @@ -667,7 +654,6 @@ mod tests { if deserialized != toc { println!("toc: {toc:?}"); - // println!("serialized: {serialized}"); println!("deserialized: {deserialized:?}"); } @@ -682,12 +668,10 @@ mod tests { if deserialized != hoc { println!("toc: {hoc:?}"); - // println!("serialized: {serialized}"); println!("deserialized: {deserialized:?}"); } deserialized == hoc - // true } #[quickcheck] @@ -697,7 +681,6 @@ mod tests { if deserialized != ship_foot { println!("ship_foot: {ship_foot:?}"); - // println!("serialized: {serialized}"); println!("deserialized: {deserialized:?}"); } diff --git a/gen/src/main.rs b/gen/src/main.rs index ffabaff..6b69249 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -10,10 +10,10 @@ use std::io::{Error, Write}; use std::str::FromStr; fn main() -> Result<(), Error> { - // generate_schema::()?; - // generate_schema::()?; - // generate_schema::()?; - // generate_schema::()?; + generate_schema::()?; + generate_schema::()?; + generate_schema::()?; + generate_schema::()?; generate_demo_data()?; From d43be8f2244ada895abe2ad29286bca47cd817c3 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 28 Aug 2024 17:14:39 +0100 Subject: [PATCH 18/28] fix: clippy warnings --- gen/src/arbitrary_impls.rs | 4 ++-- gen/src/lib.rs | 18 ++++++++---------- gen/src/main.rs | 3 +-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 387bc0b..3ea6b1d 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -23,8 +23,8 @@ impl Arbitrary for LowerAToZNumDash { let i = u8::arbitrary(g) % 37; match i { 0 => '-', - 1..=10 => (i as u8 + 47) as char, - _ => (i as u8 + 86) as char, + 1..=10 => (i + 47) as char, + _ => (i + 86) as char, } }) .collect(); diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 9848ab4..4c31beb 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,12 +1,12 @@ //! iLEAP Data Model Extension data model -use chrono::{Date, DateTime, Utc}; +use chrono::{DateTime, Utc}; use pact_data_model::{ CarbonFootprint, CharacterizationFactors, CompanyIdSet, CrossSectoralStandard, CrossSectoralStandardSet, DataModelExtension, DeclaredUnit, ExemptedEmissionsPercent, GeographicScope, IpccCharacterizationFactorsSource, PfId, PfStatus, ProductFootprint, ProductIdSet, SpecVersionString, Urn, VersionInteger, WrappedDecimal, }; -use rust_decimal::{prelude::ToPrimitive, Decimal}; +use rust_decimal::{Decimal}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -534,15 +534,13 @@ pub fn to_pcf( .tces .0 .iter() - .fold(Decimal::from(0), |acc, tce| acc + tce.transport_activity.0) - .into(), + .fold(Decimal::from(0), |acc, tce| acc + tce.transport_activity.0), p_cf_excluding_biogenic: shipment .tces .0 .iter() .fold(Decimal::from(0), |acc, tce| acc + tce.co2e_wtw.0) - .round_dp(2) - .into(), + .round_dp(2), }, ILeapType::Toc(ref toc) => MappedFields { product_id_type: "toc".to_string(), @@ -593,7 +591,7 @@ pub fn to_pcf( spec_version: SpecVersionString("2.2.0".to_string()), preceding_pf_ids: None, version: VersionInteger(1), - created: DateTime::from(Utc::now()), + created: Utc::now(), updated: None, status: PfStatus::Active, status_comment: None, @@ -622,7 +620,7 @@ pub fn to_pcf( i_luc_ghg_emissions: None, biogenic_carbon_withdrawal: None, aircraft_ghg_emissions: None, - characterization_factors: characterization_factors, + characterization_factors, ipcc_characterization_factors_sources: characterization_factors_sources.into(), cross_sectoral_standards_used: CrossSectoralStandardSet(vec![ CrossSectoralStandard::ISO14083, @@ -630,8 +628,8 @@ pub fn to_pcf( product_or_sector_specific_rules: None, // TODO: get clarity on whether GLEC should be specified biogenic_accounting_methodology: None, boundary_processes_description: "".to_string(), - reference_period_start: DateTime::from(Utc::now()), - reference_period_end: DateTime::from(Utc::now() + chrono::Duration::days(364)), + reference_period_start: Utc::now(), + reference_period_end: (Utc::now() + chrono::Duration::days(364)), geographic_scope: None, secondary_emission_factor_sources: None, exempted_emissions_percent: ExemptedEmissionsPercent(0.into()), diff --git a/gen/src/main.rs b/gen/src/main.rs index 6b69249..06676ad 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -1,5 +1,5 @@ use ileap_extension::*; -use pact_data_model::{Urn, WrappedDecimal}; +use pact_data_model::WrappedDecimal; use quickcheck::{Arbitrary, Gen}; use regex::Regex; use rust_decimal::Decimal; @@ -7,7 +7,6 @@ use schemars::schema_for; use serde_json::to_string_pretty; use std::fs::File; use std::io::{Error, Write}; -use std::str::FromStr; fn main() -> Result<(), Error> { generate_schema::()?; From 7e910d6d16380631992c612dde83cee1533ecf77 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Wed, 28 Aug 2024 17:21:22 +0100 Subject: [PATCH 19/28] refactor: turn generate_random_data into public library function --- gen/src/lib.rs | 134 +++++++++++++++++++++++++++++++++++++++++++++++- gen/src/main.rs | 126 --------------------------------------------- 2 files changed, 133 insertions(+), 127 deletions(-) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 4c31beb..00b9487 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,4 +1,7 @@ //! iLEAP Data Model Extension data model +use core::num; +use std::error::Error; + use chrono::{DateTime, Utc}; use pact_data_model::{ CarbonFootprint, CharacterizationFactors, CompanyIdSet, CrossSectoralStandard, @@ -6,7 +9,8 @@ use pact_data_model::{ GeographicScope, IpccCharacterizationFactorsSource, PfId, PfStatus, ProductFootprint, ProductIdSet, SpecVersionString, Urn, VersionInteger, WrappedDecimal, }; -use rust_decimal::{Decimal}; +use quickcheck::{Arbitrary, Gen}; +use rust_decimal::Decimal; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -654,3 +658,131 @@ pub fn to_pcf( }]), } } + +pub fn gen_rnd_demo_data() -> Result, Box> { + let mut og = Gen::new(10); + + let mut shipment_footprints = vec![]; + let mut tocs = vec![]; + let mut hocs = vec![]; + + let num_of_shipments = u8::arbitrary(&mut og) % 5 + 1; + for _ in 0..num_of_shipments { + let mut ship_foot = ShipmentFootprint::arbitrary(&mut og); + + let mut tces: Vec = vec![]; + let mut prev_tces: Vec = vec![]; + + let mut i = 0; + let limit = u8::arbitrary(&mut og) % 5 + 1; + loop { + let mut tce = Tce::arbitrary(&mut og); + + if (tce.toc_id.is_none() && tce.hoc_id.is_none()) + || tce.toc_id.is_some() && tce.hoc_id.is_some() + { + panic!("Either Toc or Hoc, but not both, must be provided"); + } + + if let Some(prev_tce) = tces.last() { + // Updates prevTceIds for the current TCE + prev_tces.push(prev_tce.tce_id.clone()); + tce.prev_tce_ids = Some(prev_tces.clone()); + + // Avoids having two HOCs follow one another + if prev_tce.hoc_id.is_some() && tce.hoc_id.is_some() { + continue; + } + }; + + if tce.hoc_id.is_some() { + // Avoids having an HOC as the first or the last TCE + if i == 0 || i == limit - 1 { + continue; + } + + let mut hoc = Hoc::arbitrary(&mut og); + hoc.hoc_id = tce.hoc_id.clone().unwrap(); + + tce.hoc_id = Some(hoc.hoc_id.clone()); + + tce.distance = GlecDistance::Actual(Decimal::from(0).into()); + tce.transport_activity = Decimal::from(0).into(); + + // TODO: Double-check divisor + tce.co2e_wtw = WrappedDecimal::from( + ((hoc.co2e_intensity_wtw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), + ); + tce.co2e_ttw = WrappedDecimal::from( + ((hoc.co2e_intensity_ttw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), + ); + + let hoc = to_pcf( + ILeapType::Hoc(hoc), + "SINE Foundation", + "urn:sine:example", + Some(HocTeuContainerSize::Normal), + Some(vec![CharFactors::Ar6]), + ); + + hocs.push(hoc); + } + + if tce.toc_id.is_some() { + let mut toc = Toc::arbitrary(&mut og); + toc.toc_id = tce.toc_id.clone().unwrap(); + + tce.transport_activity = (tce.mass.0 * tce.distance.get_distance()) + .round_dp(2) + .into(); + + tce.toc_id = Some(toc.toc_id.clone()); + tce.co2e_wtw = WrappedDecimal::from( + (toc.co2e_intensity_wtw.0 * tce.transport_activity.0).round_dp(2), + ); + tce.co2e_ttw = WrappedDecimal::from( + (toc.co2e_intensity_ttw.0 * tce.transport_activity.0).round_dp(2), + ); + + let toc = to_pcf( + ILeapType::Toc(toc), + "SINE Foundation", + "urn:sine:example", + None, + Some(vec![CharFactors::Ar6]), + ); + + tocs.push(toc.clone()); + } + + tces.push(tce); + i += 1; + if i == limit { + break; + } + } + + ship_foot.tces = NonEmptyVec::from(tces); + + let ship_foot = to_pcf( + ILeapType::ShipmentFootprint(ship_foot), + "SINE Foundation", + "urn:sine:example", + Some(HocTeuContainerSize::Normal), + Some(vec![CharFactors::Ar6]), + ); + + shipment_footprints.push(ship_foot); + } + + println!("{shipment_footprints:#?}"); + println!("{tocs:#?}"); + println!("{hocs:#?}"); + + let footprints: Vec = vec![shipment_footprints, tocs, hocs] + .into_iter() + .flatten() + .collect(); + + Ok(footprints) +} diff --git a/gen/src/main.rs b/gen/src/main.rs index 06676ad..2274a90 100644 --- a/gen/src/main.rs +++ b/gen/src/main.rs @@ -1,8 +1,5 @@ use ileap_extension::*; -use pact_data_model::WrappedDecimal; -use quickcheck::{Arbitrary, Gen}; use regex::Regex; -use rust_decimal::Decimal; use schemars::schema_for; use serde_json::to_string_pretty; use std::fs::File; @@ -14,8 +11,6 @@ fn main() -> Result<(), Error> { generate_schema::()?; generate_schema::()?; - generate_demo_data()?; - Ok(()) } @@ -44,124 +39,3 @@ fn generate_schema() -> Result<(), Error> { Ok(()) } - -fn generate_demo_data() -> Result<(), Error> { - let mut og = Gen::new(10); - - let mut shipment_footprints = vec![]; - let mut tocs = vec![]; - let mut hocs = vec![]; - for _ in 0..1 { - let mut ship_foot = ShipmentFootprint::arbitrary(&mut og); - - let mut tces: Vec = vec![]; - let mut prev_tces: Vec = vec![]; - - let mut i = 0; - let limit = u8::arbitrary(&mut og) % 5 + 1; - loop { - let mut tce = Tce::arbitrary(&mut og); - - if (tce.toc_id.is_none() && tce.hoc_id.is_none()) - || tce.toc_id.is_some() && tce.hoc_id.is_some() - { - panic!("Either Toc or Hoc, but not both, must be provided"); - } - - if let Some(prev_tce) = tces.last() { - // Updates prevTceIds for the current TCE - prev_tces.push(prev_tce.tce_id.clone()); - tce.prev_tce_ids = Some(prev_tces.clone()); - - // Avoids having two HOCs follow one another - if prev_tce.hoc_id.is_some() && tce.hoc_id.is_some() { - continue; - } - }; - - if tce.hoc_id.is_some() { - // Avoids having an HOC as the first or the last TCE - if i == 0 || i == limit - 1 { - continue; - } - - let mut hoc = Hoc::arbitrary(&mut og); - hoc.hoc_id = tce.hoc_id.clone().unwrap(); - - tce.hoc_id = Some(hoc.hoc_id.clone()); - - tce.distance = GlecDistance::Actual(Decimal::from(0).into()); - tce.transport_activity = Decimal::from(0).into(); - - // TODO: Double-check divisor - tce.co2e_wtw = WrappedDecimal::from( - ((hoc.co2e_intensity_wtw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), - ); - tce.co2e_ttw = WrappedDecimal::from( - ((hoc.co2e_intensity_ttw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), - ); - - let hoc = to_pcf( - ILeapType::Hoc(hoc), - "SINE Foundation", - "urn:sine:example", - Some(HocTeuContainerSize::Normal), - Some(vec![CharFactors::Ar6]), - ); - - hocs.push(hoc); - } - - if tce.toc_id.is_some() { - let mut toc = Toc::arbitrary(&mut og); - toc.toc_id = tce.toc_id.clone().unwrap(); - - tce.transport_activity = (tce.mass.0 * tce.distance.get_distance()) - .round_dp(2) - .into(); - - tce.toc_id = Some(toc.toc_id.clone()); - tce.co2e_wtw = WrappedDecimal::from( - (toc.co2e_intensity_wtw.0 * tce.transport_activity.0).round_dp(2), - ); - tce.co2e_ttw = WrappedDecimal::from( - (toc.co2e_intensity_ttw.0 * tce.transport_activity.0).round_dp(2), - ); - - let toc = to_pcf( - ILeapType::Toc(toc), - "SINE Foundation", - "urn:sine:example", - None, - Some(vec![CharFactors::Ar6]), - ); - - tocs.push(toc.clone()); - } - - tces.push(tce); - i += 1; - if i == limit { - break; - } - } - - ship_foot.tces = NonEmptyVec::from(tces); - - let ship_foot = to_pcf( - ILeapType::ShipmentFootprint(ship_foot), - "SINE Foundation", - "urn:sine:example", - Some(HocTeuContainerSize::Normal), - Some(vec![CharFactors::Ar6]), - ); - - shipment_footprints.push(ship_foot); - } - - println!("{shipment_footprints:#?}"); - println!("{tocs:#?}"); - println!("{hocs:#?}"); - - Ok(()) -} From 85ffc32c17521181536732ef0609dbaa315061e2 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Mon, 2 Sep 2024 16:35:59 +0100 Subject: [PATCH 20/28] fix: hoc tce emissions calc --- gen/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 00b9487..41b8efa 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,5 +1,4 @@ //! iLEAP Data Model Extension data model -use core::num; use std::error::Error; use chrono::{DateTime, Utc}; @@ -709,12 +708,11 @@ pub fn gen_rnd_demo_data() -> Result, Box> { tce.distance = GlecDistance::Actual(Decimal::from(0).into()); tce.transport_activity = Decimal::from(0).into(); - // TODO: Double-check divisor tce.co2e_wtw = WrappedDecimal::from( - ((hoc.co2e_intensity_wtw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), + ((hoc.co2e_intensity_wtw.0 * tce.mass.0)).round_dp(2), ); tce.co2e_ttw = WrappedDecimal::from( - ((hoc.co2e_intensity_ttw.0 * tce.mass.0) / Decimal::from(1000000)).round_dp(2), + ((hoc.co2e_intensity_ttw.0 * tce.mass.0)).round_dp(2), ); let hoc = to_pcf( From 3dab116703c3842ea14e9b3903d22b6569c07f8f Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 10 Sep 2024 14:40:18 +0100 Subject: [PATCH 21/28] refactor: LowerAToZNumDash --- gen/src/arbitrary_impls.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 3ea6b1d..2eff689 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -1,3 +1,5 @@ +use core::str; + use crate::*; use chrono::Duration; use quickcheck::Arbitrary; @@ -15,20 +17,20 @@ impl LowerAToZNumDash { impl Arbitrary for LowerAToZNumDash { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let s: Vec<()> = Vec::arbitrary(g); - let s: String = s + let maybe_string: Vec = Vec::arbitrary(g) .into_iter() - .map(|_| { - // ASCII characters -, 0..9, a..z - let i = u8::arbitrary(g) % 37; + .map(|v: u8| { + let i = v % 37; + match i { - 0 => '-', - 1..=10 => (i + 47) as char, - _ => (i + 86) as char, + 0 => '-' as u8, + 1..=10 => i + 47, + _ => i + 86, } }) .collect(); - Self(s) + + Self(str::from_utf8(&maybe_string).unwrap().to_string()) } fn shrink(&self) -> Box> { From c08d675708d6b8acabdb854ee2491ff0042c0a23 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 10 Sep 2024 15:06:42 +0100 Subject: [PATCH 22/28] refactor: improvements based on @zeitgeist's review --- gen/src/arbitrary_impls.rs | 2 +- gen/src/lib.rs | 39 ++++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 2eff689..359da68 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -51,7 +51,7 @@ fn formatted_arbitrary_string(fixed: &str, g: &mut quickcheck::Gen) -> String { impl Arbitrary for ShipmentFootprint { fn arbitrary(g: &mut quickcheck::Gen) -> Self { ShipmentFootprint { - // Using u16 to avoid unreadably large + // Using u16 to avoid unreadably large numbers. mass: format!("{}", u16::arbitrary(g)), shipment_id: formatted_arbitrary_string("shipment-", g), tces: NonEmptyVec::::arbitrary(g), diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 41b8efa..0d517ba 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -1,6 +1,4 @@ //! iLEAP Data Model Extension data model -use std::error::Error; - use chrono::{DateTime, Utc}; use pact_data_model::{ CarbonFootprint, CharacterizationFactors, CompanyIdSet, CrossSectoralStandard, @@ -631,7 +629,7 @@ pub fn to_pcf( product_or_sector_specific_rules: None, // TODO: get clarity on whether GLEC should be specified biogenic_accounting_methodology: None, boundary_processes_description: "".to_string(), - reference_period_start: Utc::now(), + reference_period_start: Utc::now(), // TODO: turn into parameter. reference_period_end: (Utc::now() + chrono::Duration::days(364)), geographic_scope: None, secondary_emission_factor_sources: None, @@ -658,8 +656,8 @@ pub fn to_pcf( } } -pub fn gen_rnd_demo_data() -> Result, Box> { - let mut og = Gen::new(10); +pub fn gen_rnd_demo_data(size: usize) -> Vec { + let mut og = Gen::new(size); let mut shipment_footprints = vec![]; let mut tocs = vec![]; @@ -674,6 +672,7 @@ pub fn gen_rnd_demo_data() -> Result, Box> { let mut i = 0; let limit = u8::arbitrary(&mut og) % 5 + 1; + // TODO: improve code through pair programming with Martin. loop { let mut tce = Tce::arbitrary(&mut og); @@ -708,12 +707,10 @@ pub fn gen_rnd_demo_data() -> Result, Box> { tce.distance = GlecDistance::Actual(Decimal::from(0).into()); tce.transport_activity = Decimal::from(0).into(); - tce.co2e_wtw = WrappedDecimal::from( - ((hoc.co2e_intensity_wtw.0 * tce.mass.0)).round_dp(2), - ); - tce.co2e_ttw = WrappedDecimal::from( - ((hoc.co2e_intensity_ttw.0 * tce.mass.0)).round_dp(2), - ); + tce.co2e_wtw = + WrappedDecimal::from((hoc.co2e_intensity_wtw.0 * tce.mass.0).round_dp(2)); + tce.co2e_ttw = + WrappedDecimal::from((hoc.co2e_intensity_ttw.0 * tce.mass.0).round_dp(2)); let hoc = to_pcf( ILeapType::Hoc(hoc), @@ -773,14 +770,20 @@ pub fn gen_rnd_demo_data() -> Result, Box> { shipment_footprints.push(ship_foot); } - println!("{shipment_footprints:#?}"); - println!("{tocs:#?}"); - println!("{hocs:#?}"); - - let footprints: Vec = vec![shipment_footprints, tocs, hocs] + vec![shipment_footprints, tocs, hocs] .into_iter() .flatten() - .collect(); + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; - Ok(footprints) + #[test] + fn test_gen_rnd_demo_data() { + let footprints = gen_rnd_demo_data(10); + + println!("{footprints:#?}"); + } } From c693ca660afad8e49d0707ed8f81faf39cff55db Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 10 Sep 2024 16:31:58 +0100 Subject: [PATCH 23/28] refactor: check hocId and tocId in test and improve loop --- gen/src/lib.rs | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 0d517ba..e458bb5 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -244,9 +244,8 @@ pub struct Tad { pub packaging_or_tr_eq_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub packaging_or_tr_eq_amount: Option, - // TODO: verify whether the absence of this property is intended. - // #[serde(skip_serializing_if = "Option::is_none")] - // pub energy_carrier: EnergyCarrier, + // TODO: verify whether the absence of this property is intended. #[serde(skip_serializing_if = + // "Option::is_none")] pub energy_carrier: EnergyCarrier, #[serde(skip_serializing_if = "Option::is_none")] pub feedstocks: Option>, } @@ -656,14 +655,16 @@ pub fn to_pcf( } } -pub fn gen_rnd_demo_data(size: usize) -> Vec { - let mut og = Gen::new(size); +// TODO: invert logic to generate a list of HOCs and TOCs and only then generate TCEs, improving +// readability and demo data quality, as suggested by Martin. +pub fn gen_rnd_demo_data(size: u8) -> Vec { + let mut og = Gen::new(size as usize); let mut shipment_footprints = vec![]; let mut tocs = vec![]; let mut hocs = vec![]; - let num_of_shipments = u8::arbitrary(&mut og) % 5 + 1; + let num_of_shipments = u8::arbitrary(&mut og) % size + 1; for _ in 0..num_of_shipments { let mut ship_foot = ShipmentFootprint::arbitrary(&mut og); @@ -671,17 +672,11 @@ pub fn gen_rnd_demo_data(size: usize) -> Vec { let mut prev_tces: Vec = vec![]; let mut i = 0; - let limit = u8::arbitrary(&mut og) % 5 + 1; + let limit = u8::arbitrary(&mut og) % size + 1; // TODO: improve code through pair programming with Martin. loop { let mut tce = Tce::arbitrary(&mut og); - if (tce.toc_id.is_none() && tce.hoc_id.is_none()) - || tce.toc_id.is_some() && tce.hoc_id.is_some() - { - panic!("Either Toc or Hoc, but not both, must be provided"); - } - if let Some(prev_tce) = tces.last() { // Updates prevTceIds for the current TCE prev_tces.push(prev_tce.tce_id.clone()); @@ -689,17 +684,19 @@ pub fn gen_rnd_demo_data(size: usize) -> Vec { // Avoids having two HOCs follow one another if prev_tce.hoc_id.is_some() && tce.hoc_id.is_some() { - continue; + tce = Tce::arbitrary(&mut og); } }; + if i == 0 || i == limit - 1 && tce.hoc_id.is_some() { + tce = Tce::arbitrary(&mut og); + } + if tce.hoc_id.is_some() { // Avoids having an HOC as the first or the last TCE - if i == 0 || i == limit - 1 { - continue; - } let mut hoc = Hoc::arbitrary(&mut og); + hoc.hoc_id = tce.hoc_id.clone().unwrap(); tce.hoc_id = Some(hoc.hoc_id.clone()); @@ -732,6 +729,7 @@ pub fn gen_rnd_demo_data(size: usize) -> Vec { .into(); tce.toc_id = Some(toc.toc_id.clone()); + tce.co2e_wtw = WrappedDecimal::from( (toc.co2e_intensity_wtw.0 * tce.transport_activity.0).round_dp(2), ); @@ -784,6 +782,24 @@ mod tests { fn test_gen_rnd_demo_data() { let footprints = gen_rnd_demo_data(10); + for footprint in footprints.iter() { + if let Some(extensions) = &footprint.extensions { + for extension in extensions.iter() { + if let Some(ship_foot) = extension.data.get("ShipmentFootprint") { + let ship_foot = + serde_json::from_value::(ship_foot.to_owned()) + .unwrap(); + for tce in ship_foot.tces.0.iter() { + assert!( + tce.toc_id.is_some() ^ tce.hoc_id.is_some(), + "Either tocId or hocId, but not both, must be provided." + ); + } + } + } + } + } + println!("{footprints:#?}"); } } From f6a23278ca069e288096a6a4330bf123898ce5fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Pomp=C3=A9ry?= Date: Mon, 16 Sep 2024 21:51:19 +0200 Subject: [PATCH 24/28] fix: differing inbount/outbound modalities for Hocs --- gen/src/arbitrary_impls.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index 359da68..ae544b1 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -69,13 +69,13 @@ impl Arbitrary for Hoc { g: &mut quickcheck::Gen, ) -> (Option, Option) { let inbound = Some(TransportMode::arbitrary(g)); - let outbound = Some(TransportMode::arbitrary(g)); + let mut outbound = Some(TransportMode::arbitrary(g)); - if inbound == outbound { - (inbound, Some(TransportMode::arbitrary(g))) - } else { - (inbound, outbound) + while inbound == outbound { + outbound = Some(TransportMode::arbitrary(g)); } + + (inbound, outbound) } let hub_type = HubType::arbitrary(g); From 8bb6878f34d5c130433a8191fe417175c70ca15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Pomp=C3=A9ry?= Date: Tue, 17 Sep 2024 17:57:02 +0200 Subject: [PATCH 25/28] fix: EnergyConsumptionUnit JSON serialization --- gen/schemas/hoc.json | 2 +- gen/schemas/toc.json | 2 +- gen/src/lib.rs | 1 + gen/tests/tests.rs | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/gen/schemas/hoc.json b/gen/schemas/hoc.json index 96afec8..7645cc0 100644 --- a/gen/schemas/hoc.json +++ b/gen/schemas/hoc.json @@ -178,7 +178,7 @@ "l", "kg", "kWh", - "mJ" + "MJ" ] }, "Feedstock": { diff --git a/gen/schemas/toc.json b/gen/schemas/toc.json index 6e54090..78cc4a3 100644 --- a/gen/schemas/toc.json +++ b/gen/schemas/toc.json @@ -189,7 +189,7 @@ "l", "kg", "kWh", - "mJ" + "MJ" ] }, "Feedstock": { diff --git a/gen/src/lib.rs b/gen/src/lib.rs index e458bb5..36d3de1 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -355,6 +355,7 @@ pub enum EnergyConsumptionUnit { L, Kg, KWh, + #[serde(rename = "MJ")] MJ, } diff --git a/gen/tests/tests.rs b/gen/tests/tests.rs index c2eadd6..1487a7b 100644 --- a/gen/tests/tests.rs +++ b/gen/tests/tests.rs @@ -127,3 +127,18 @@ fn test_ship_foot_deser() { let ship_foot: ShipmentFootprint = serde_json::from_str(json).unwrap(); assert_eq!(ship_foot, expected) } + +#[test] +fn test_energyconsumptionunit_deser() { + use EnergyConsumptionUnit::*; + let test_vectors = vec![ + ("\"l\"", L), + ("\"kg\"", Kg), + ("\"kWh\"", KWh), + ("\"MJ\"", MJ), + ]; + + for (expect, input) in &test_vectors { + assert_eq!(expect, &serde_json::to_string(input).unwrap()); + } +} From d1bd6b3431ad9bbecc8c039689384ce3591a5fb5 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 17 Sep 2024 17:29:57 +0100 Subject: [PATCH 26/28] fix: import UUID and udpdate energyConsumptionUnit type --- gen/src/lib.rs | 3 ++- gen/src/pact_integration.rs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index a549bc2..691c16e 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize}; mod pact_integration; pub use pact_integration::*; +use uuid::Uuid; mod arbitrary_impls; @@ -807,4 +808,4 @@ mod tests { println!("{footprints:#?}"); } -} \ No newline at end of file +} diff --git a/gen/src/pact_integration.rs b/gen/src/pact_integration.rs index 0d7cc27..5bdf0f8 100644 --- a/gen/src/pact_integration.rs +++ b/gen/src/pact_integration.rs @@ -381,7 +381,7 @@ fn toc_to_pcf() { region_provenance: Some("Europe".to_string()), }]), energy_consumption: None, - energy_consumption_unit: Some("MJ".to_string()), + energy_consumption_unit: Some(crate::EnergyConsumptionUnit::MJ), emission_factor_wtw: dec!(97).into(), emission_factor_ttw: dec!(0).into(), }] @@ -429,7 +429,7 @@ fn hoc_to_pfc() { energy_carrier: EnergyCarrierType::Diesel, feedstocks: None, energy_consumption: None, - energy_consumption_unit: Some("kg".to_string()), + energy_consumption_unit: Some(crate::EnergyConsumptionUnit::Kg), emission_factor_wtw: dec!(4.13).into(), emission_factor_ttw: dec!(3.17).into(), }, @@ -437,7 +437,7 @@ fn hoc_to_pfc() { energy_carrier: EnergyCarrierType::Electric, feedstocks: None, energy_consumption: None, - energy_consumption_unit: Some("MJ".to_string()), + energy_consumption_unit: Some(crate::EnergyConsumptionUnit::MJ), emission_factor_wtw: dec!(97).into(), emission_factor_ttw: dec!(0).into(), }, From e956015bb0d4ccaec04a5b754058f8d52d645fd0 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 17 Sep 2024 17:48:15 +0100 Subject: [PATCH 27/28] fix: add shipmentId to random TCE --- gen/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 691c16e..3426c86 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -683,6 +683,8 @@ pub fn gen_rnd_demo_data(size: u8) -> Vec { loop { let mut tce = Tce::arbitrary(&mut og); + tce.shipment_id = ship_foot.shipment_id.clone(); + if let Some(prev_tce) = tces.last() { // Updates prevTceIds for the current TCE prev_tces.push(prev_tce.tce_id.clone()); From a1b92ec8377ffcb1629c4c6f9268b92403c4ca45 Mon Sep 17 00:00:00 2001 From: Raimundo Henriques Date: Tue, 17 Sep 2024 17:52:17 +0100 Subject: [PATCH 28/28] fix: clippy warnings --- gen/src/arbitrary_impls.rs | 2 +- gen/src/lib.rs | 5 +++-- gen/src/pact_integration.rs | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/gen/src/arbitrary_impls.rs b/gen/src/arbitrary_impls.rs index ae544b1..55f1bd5 100644 --- a/gen/src/arbitrary_impls.rs +++ b/gen/src/arbitrary_impls.rs @@ -23,7 +23,7 @@ impl Arbitrary for LowerAToZNumDash { let i = v % 37; match i { - 0 => '-' as u8, + 0 => b'-', 1..=10 => i + 47, _ => i + 86, } diff --git a/gen/src/lib.rs b/gen/src/lib.rs index 3426c86..e85283e 100644 --- a/gen/src/lib.rs +++ b/gen/src/lib.rs @@ -683,8 +683,6 @@ pub fn gen_rnd_demo_data(size: u8) -> Vec { loop { let mut tce = Tce::arbitrary(&mut og); - tce.shipment_id = ship_foot.shipment_id.clone(); - if let Some(prev_tce) = tces.last() { // Updates prevTceIds for the current TCE prev_tces.push(prev_tce.tce_id.clone()); @@ -756,7 +754,10 @@ pub fn gen_rnd_demo_data(size: u8) -> Vec { tocs.push(toc.clone()); } + tce.shipment_id.clone_from(&ship_foot.shipment_id); + tces.push(tce); + i += 1; if i == limit { break; diff --git a/gen/src/pact_integration.rs b/gen/src/pact_integration.rs index 5bdf0f8..08a864a 100644 --- a/gen/src/pact_integration.rs +++ b/gen/src/pact_integration.rs @@ -354,8 +354,8 @@ fn ship_foot_to_pfc() { "ShipmentFootprint with id shipment-test" ); assert_eq!(pfc.pcf.declared_unit, DeclaredUnit::TonKilometer); - assert_eq!(pfc.pcf.unitary_product_amount.0, dec!(33840).into()); - assert_eq!(pfc.pcf.p_cf_excluding_biogenic.0, dec!(3131.06).into()); + assert_eq!(pfc.pcf.unitary_product_amount.0, dec!(33840)); + assert_eq!(pfc.pcf.p_cf_excluding_biogenic.0, dec!(3131.06)); } #[test] @@ -401,8 +401,8 @@ fn toc_to_pcf() { assert_eq!(pfc.product_name_company.0, "TOC with ID toc-test"); assert_eq!(pfc.pcf.declared_unit, DeclaredUnit::TonKilometer); - assert_eq!(pfc.pcf.unitary_product_amount.0, dec!(1).into()); - assert_eq!(pfc.pcf.p_cf_excluding_biogenic.0, dec!(0.007).into()); + assert_eq!(pfc.pcf.unitary_product_amount.0, dec!(1)); + assert_eq!(pfc.pcf.p_cf_excluding_biogenic.0, dec!(0.007)); } #[test] @@ -452,6 +452,6 @@ fn hoc_to_pfc() { assert_eq!(pfc.product_name_company.0, "HOC with ID hoc-test"); assert_eq!(pfc.pcf.declared_unit, DeclaredUnit::Kilogram); - assert_eq!(pfc.pcf.unitary_product_amount.0, dec!(1000).into()); - assert_eq!(pfc.pcf.p_cf_excluding_biogenic.0, dec!(33).into()); + assert_eq!(pfc.pcf.unitary_product_amount.0, dec!(1000)); + assert_eq!(pfc.pcf.p_cf_excluding_biogenic.0, dec!(33)); }