Skip to content

Commit 535c349

Browse files
committed
refactor: to_pcf to use From<&T> -> MappedFields
1 parent 342b8eb commit 535c349

File tree

1 file changed

+146
-110
lines changed

1 file changed

+146
-110
lines changed

gen/src/pact_integration.rs

+146-110
Original file line numberDiff line numberDiff line change
@@ -2,93 +2,51 @@ use chrono::Utc;
22
use pact_data_model::{
33
CarbonFootprint, CharacterizationFactors, CompanyIdSet, CrossSectoralStandard,
44
CrossSectoralStandardSet, DataModelExtension, DeclaredUnit, ExemptedEmissionsPercent,
5-
IpccCharacterizationFactorsSource, PfId, PfStatus, ProductFootprint, ProductIdSet,
6-
SpecVersionString, Urn, VersionInteger,
5+
IpccCharacterizationFactorsSource, PfId, PfStatus, PositiveDecimal, ProductFootprint,
6+
ProductIdSet, SpecVersionString, Urn, VersionInteger,
77
};
88
use rust_decimal::Decimal;
9-
use schemars::JsonSchema;
10-
use serde::{Deserialize, Serialize};
9+
use serde::Serialize;
1110
use uuid::Uuid;
1211

1312
use crate::{Hoc, HocCo2eIntensityThroughput, ShipmentFootprint, Toc};
1413

15-
pub enum HocTeuContainerSize {
14+
/*pub enum HocTeuContainerSize {
1615
Normal,
1716
Light,
1817
Heavy,
19-
}
20-
21-
#[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq, Clone)]
22-
pub enum ILeapType {
23-
ShipmentFootprint(ShipmentFootprint),
24-
Toc(Toc),
25-
Hoc(Hoc),
26-
}
27-
28-
pub fn to_pcf(
29-
ileap_type: ILeapType,
30-
company_name: &str,
31-
company_urn: &str,
32-
hoc_container_size: Option<HocTeuContainerSize>,
33-
characterization_factors: Option<Vec<CharacterizationFactors>>,
34-
) -> ProductFootprint {
35-
let (characterization_factors, characterization_factors_sources) =
36-
match characterization_factors {
37-
None => (
38-
CharacterizationFactors::Ar5,
39-
vec![IpccCharacterizationFactorsSource::from("AR5".to_string())],
40-
),
41-
Some(cf) => {
42-
if cf.is_empty() {
43-
(
44-
CharacterizationFactors::Ar5,
45-
vec![IpccCharacterizationFactorsSource::from("AR5".to_string())],
46-
)
47-
} else {
48-
let cf: Vec<IpccCharacterizationFactorsSource> = cf
49-
.iter()
50-
.map(|cf| match cf {
51-
CharacterizationFactors::Ar5 => {
52-
IpccCharacterizationFactorsSource::from("AR5".to_string())
53-
}
54-
CharacterizationFactors::Ar6 => {
55-
IpccCharacterizationFactorsSource::from("AR6".to_string())
56-
}
57-
})
58-
.collect();
59-
60-
let characterization_factors = if cf
61-
.contains(&IpccCharacterizationFactorsSource::from("AR5".to_string()))
62-
{
63-
CharacterizationFactors::Ar5
64-
} else {
65-
CharacterizationFactors::Ar6
66-
};
67-
68-
(characterization_factors, cf)
69-
}
70-
}
71-
};
18+
}*/
7219

73-
struct MappedFields {
74-
product_id_type: String,
75-
id: String,
76-
product_name_company: String,
77-
declared_unit: DeclaredUnit,
78-
unitary_product_amount: Decimal,
79-
p_cf_excluding_biogenic: Decimal,
20+
/* fn get_teu_co2e_intensity_wtw(
21+
hoc_co2e_intensity_wtw: Decimal,
22+
hoc_container_size: &Option<HocTeuContainerSize>,
23+
) -> Decimal {
24+
match hoc_container_size {
25+
Some(HocTeuContainerSize::Normal) => hoc_co2e_intensity_wtw * Decimal::from(10000),
26+
Some(HocTeuContainerSize::Light) => hoc_co2e_intensity_wtw * Decimal::from(6000),
27+
Some(HocTeuContainerSize::Heavy) => hoc_co2e_intensity_wtw * Decimal::from(14050),
28+
None => {
29+
println!("Warning: HOC TEU container size not specified, using normal container");
30+
hoc_co2e_intensity_wtw * Decimal::from(10000)
31+
}
8032
}
33+
} */
8134

82-
let MappedFields {
83-
product_id_type,
84-
id,
85-
product_name_company,
86-
declared_unit,
87-
unitary_product_amount,
88-
p_cf_excluding_biogenic,
89-
} = match ileap_type {
90-
ILeapType::ShipmentFootprint(ref shipment) => MappedFields {
91-
product_id_type: "shipment".to_string(),
35+
pub struct MappedFields {
36+
product_id_type: &'static str,
37+
data_schema_id: &'static str,
38+
id: String,
39+
product_name_company: String,
40+
declared_unit: DeclaredUnit,
41+
unitary_product_amount: Decimal,
42+
p_cf_excluding_biogenic: Decimal,
43+
}
44+
45+
impl From<&ShipmentFootprint> for MappedFields {
46+
fn from(shipment: &ShipmentFootprint) -> Self {
47+
MappedFields {
48+
product_id_type: "shipment",
49+
data_schema_id: "shipment-footprint",
9250
id: shipment.shipment_id.clone(),
9351
product_name_company: format!("ShipmentFootprint with id {}", shipment.shipment_id),
9452
declared_unit: DeclaredUnit::TonKilometer,
@@ -102,51 +60,83 @@ pub fn to_pcf(
10260
.0
10361
.iter()
10462
.fold(Decimal::from(0), |acc, tce| acc + tce.co2e_wtw.0),
105-
},
106-
ILeapType::Toc(ref toc) => MappedFields {
107-
product_id_type: "toc".to_string(),
108-
id: toc.toc_id.clone(),
109-
product_name_company: format!("TOC with ID {}", toc.toc_id),
110-
declared_unit: DeclaredUnit::TonKilometer,
111-
unitary_product_amount: Decimal::from(1),
112-
p_cf_excluding_biogenic: toc.co2e_intensity_wtw.0,
113-
},
114-
ILeapType::Hoc(ref hoc) => MappedFields {
115-
product_id_type: "hoc".to_string(),
63+
}
64+
}
65+
}
66+
67+
impl From<&Hoc> for MappedFields {
68+
fn from(hoc: &Hoc) -> Self {
69+
MappedFields {
70+
product_id_type: "hoc",
71+
data_schema_id: "hoc",
11672
id: hoc.hoc_id.clone(),
11773
product_name_company: format!("HOC with ID {}", hoc.hoc_id),
11874
declared_unit: DeclaredUnit::Kilogram,
11975
unitary_product_amount: Decimal::from(1000),
12076
p_cf_excluding_biogenic: match hoc.co2e_intensity_throughput {
12177
HocCo2eIntensityThroughput::TEU => {
122-
get_teu_co2e_intensity_wtw(hoc.co2e_intensity_wtw.0, hoc_container_size)
78+
panic!("HOC with TEU throughput is not supported, yet")
12379
}
12480
HocCo2eIntensityThroughput::Tonnes => hoc.co2e_intensity_wtw.0,
12581
},
126-
},
127-
};
82+
}
83+
}
84+
}
12885

129-
fn get_teu_co2e_intensity_wtw(
130-
hoc_co2e_intensity_wtw: Decimal,
131-
hoc_container_size: Option<HocTeuContainerSize>,
132-
) -> Decimal {
133-
match hoc_container_size {
134-
Some(HocTeuContainerSize::Normal) => hoc_co2e_intensity_wtw * Decimal::from(10000),
135-
Some(HocTeuContainerSize::Light) => hoc_co2e_intensity_wtw * Decimal::from(6000),
136-
Some(HocTeuContainerSize::Heavy) => hoc_co2e_intensity_wtw * Decimal::from(14050),
137-
None => {
138-
println!("Warning: HOC TEU container size not specified, using normal container");
139-
hoc_co2e_intensity_wtw * Decimal::from(10000)
140-
}
86+
impl From<&Toc> for MappedFields {
87+
fn from(toc: &Toc) -> Self {
88+
MappedFields {
89+
product_id_type: "toc",
90+
data_schema_id: "toc",
91+
id: toc.toc_id.clone(),
92+
product_name_company: format!("TOC with ID {}", toc.toc_id),
93+
declared_unit: DeclaredUnit::TonKilometer,
94+
unitary_product_amount: Decimal::from(1),
95+
p_cf_excluding_biogenic: toc.co2e_intensity_wtw.0,
14196
}
14297
}
98+
}
99+
100+
/**
101+
* converts an iLEAP type into a PACT Data Model's ProductFootprint.
102+
*
103+
* To do so, additional propertiers are needed:
104+
* - company_name: the name of the company that is responsible for the product
105+
* - company_urn: the URN of the company that is responsible for the product
106+
* - characterization_factors: the optional IPCC characterization factors that were used in the calculation of the carbon footprint (TOC, HOC, ShipmentFootprint). If not defined `AR5` will be used.
107+
*/
108+
pub fn to_pcf<T>(
109+
ileap_type: &T,
110+
company_name: &str,
111+
company_urn: &str,
112+
// hoc_container_size: Option<HocTeuContainerSize>,
113+
characterization_factors: Option<Vec<CharacterizationFactors>>,
114+
) -> ProductFootprint
115+
where
116+
T: Serialize,
117+
MappedFields: for<'a> From<&'a T>,
118+
{
119+
// massage the optional IPCC characterization factors into a tuple of the actual factors and the IPCC Characterization Factor sources
120+
let (characterization_factors, characterization_factors_sources) =
121+
to_char_factors(characterization_factors);
143122

144-
let data_schema_id = if product_id_type == "shipment" {
145-
"shipment-footprint"
146-
} else {
147-
product_id_type.as_str()
148-
};
123+
// extract the properties necessary to turn the iLEAP type into a ProductFootprint
124+
// Note: this conversion at this point is "static" and does not require any additional data.
125+
// However it seems that the current HOC data type (when throughput is declared in `TEU`
126+
// the current implementation bails out drastically. We are investingating whether
127+
// this is indicates a lack in the iLEAP Data Model. This function will be updated
128+
// once we have more information.
129+
let MappedFields {
130+
product_id_type,
131+
data_schema_id,
132+
id,
133+
product_name_company,
134+
declared_unit,
135+
unitary_product_amount,
136+
p_cf_excluding_biogenic,
137+
} = ileap_type.into();
149138

139+
// fasten your seatbelts, we are about to create a ProductFootprint...
150140
ProductFootprint {
151141
id: PfId(Uuid::new_v4()),
152142
spec_version: SpecVersionString("2.2.0".to_string()),
@@ -171,10 +161,10 @@ pub fn to_pcf(
171161
declared_unit,
172162
unitary_product_amount: unitary_product_amount.into(),
173163
p_cf_excluding_biogenic: p_cf_excluding_biogenic.into(),
174-
p_cf_including_biogenic: Some(p_cf_excluding_biogenic.into()), // TODO: to be clarified in the Tech Specs
164+
p_cf_including_biogenic: None,
175165
fossil_ghg_emissions: p_cf_excluding_biogenic.into(),
176-
fossil_carbon_content: p_cf_excluding_biogenic.into(), // TODO: to be clarified in the Tech Specs
177-
biogenic_carbon_content: p_cf_excluding_biogenic.into(), // TODO: to be clarified in the Tech Specs
166+
fossil_carbon_content: PositiveDecimal::from(Decimal::from(0)),
167+
biogenic_carbon_content: PositiveDecimal::from(Decimal::from(0)),
178168
d_luc_ghg_emissions: None,
179169
land_management_ghg_emissions: None,
180170
other_biogenic_ghg_emissions: None,
@@ -207,11 +197,57 @@ pub fn to_pcf(
207197
spec_version: SpecVersionString::from("0.2.0".to_string()),
208198
data_schema: format!("https://api.ileap.sine.dev/{data_schema_id}.json"),
209199
documentation: Some("https://sine-fdn.github.io/ileap-extension/".to_string()),
210-
data: serde_json::to_value(&ileap_type)
200+
data: serde_json::to_value(ileap_type)
211201
.unwrap()
212202
.as_object()
213203
.unwrap()
214204
.to_owned(),
215205
}]),
216206
}
217207
}
208+
209+
fn to_char_factors(
210+
characterization_factors: Option<Vec<CharacterizationFactors>>,
211+
) -> (
212+
CharacterizationFactors,
213+
Vec<IpccCharacterizationFactorsSource>,
214+
) {
215+
let (characterization_factors, characterization_factors_sources) =
216+
match characterization_factors {
217+
None => (
218+
CharacterizationFactors::Ar5,
219+
vec![IpccCharacterizationFactorsSource::from("AR5".to_string())],
220+
),
221+
Some(cf) => {
222+
if cf.is_empty() {
223+
(
224+
CharacterizationFactors::Ar5,
225+
vec![IpccCharacterizationFactorsSource::from("AR5".to_string())],
226+
)
227+
} else {
228+
let cf: Vec<IpccCharacterizationFactorsSource> = cf
229+
.iter()
230+
.map(|cf| match cf {
231+
CharacterizationFactors::Ar5 => {
232+
IpccCharacterizationFactorsSource::from("AR5".to_string())
233+
}
234+
CharacterizationFactors::Ar6 => {
235+
IpccCharacterizationFactorsSource::from("AR6".to_string())
236+
}
237+
})
238+
.collect();
239+
240+
let characterization_factors = if cf
241+
.contains(&IpccCharacterizationFactorsSource::from("AR5".to_string()))
242+
{
243+
CharacterizationFactors::Ar5
244+
} else {
245+
CharacterizationFactors::Ar6
246+
};
247+
248+
(characterization_factors, cf)
249+
}
250+
}
251+
};
252+
(characterization_factors, characterization_factors_sources)
253+
}

0 commit comments

Comments
 (0)