Skip to main content

price_cdr_with_unknown_version/
price_cdr_with_unknown_version.rs

1#![expect(clippy::expect_used, reason = "examples can panic")]
2#![expect(clippy::print_stderr, reason = "examples can log to stderr")]
3
4use ocpi_tariffs::{cdr, price, timezone, warning, Version};
5
6fn main() {
7    const CDR_JSON: &str =
8        include_str!("../test_data/v211/real_world/time_and_parking_time/cdr.json");
9
10    // First the raw JSON should be parsed into a `cdr::Versioned` object.
11    // The `cdr::Report` returned from calling `cdr::parse` contains a `cdr::Versioned` object
12    // and a potential list of unexpected fields based on the OCPI v221 spec.
13    let cdr = cdr::parse(CDR_JSON).expect("Unable to parse CDR JSON");
14
15    // The guessed Version can be either certain or uncertain.
16    // In this case we discard the CDR object and try to convert it into a version.
17    // If the version is uncertain then fallback to presuming the CDR is v211.
18    let cdr = cdr.certain_or(Version::V211);
19
20    // The timezone can be inferred or found in the CDR, but a versioned CDR is required.
21    let (timezone_source, warnings) = timezone::find_or_infer(&cdr).into_parts();
22
23    let Some(timezone_source) = timezone_source else {
24        eprintln!("Unable to infer timezone");
25        print_timezone_warnings(&cdr, &warnings);
26        return;
27    };
28
29    if !warnings.is_empty() {
30        print_timezone_warnings(&cdr, &warnings);
31    }
32
33    // We don't care whether the timezone was found or inferred.
34    let timezone = timezone_source.into_timezone();
35    let report = cdr::parse_with_version(CDR_JSON, Version::V211).expect("unable to parse CDR");
36    let cdr::ParseReport {
37        cdr,
38        unexpected_fields,
39    } = report;
40
41    if !unexpected_fields.is_empty() {
42        eprintln!("Strange... there are fields in the CDR that are not defined in the spec.");
43
44        for path in &unexpected_fields {
45            eprintln!("{path}");
46        }
47    }
48
49    let report =
50        cdr::price(&cdr, price::TariffSource::UseCdr, timezone).expect("unable to price CDR JSON");
51    let (report, warnings) = report.into_parts();
52
53    print_pricing_warnings(&cdr, &warnings);
54
55    // The various fields of the `price::Report` can be examined or converted to JSON.
56    let price::Report {
57        periods: _,
58        tariff_used: _,
59        tariff_reports: _,
60        timezone: _,
61        billed_energy: _,
62        billed_parking_time: _,
63        total_charging_time: _,
64        billed_charging_time: _,
65        total_cost: _,
66        total_fixed_cost: _,
67        total_time: _,
68        total_time_cost: _,
69        total_energy: _,
70        total_energy_cost: _,
71        total_parking_time: _,
72        total_parking_cost: _,
73        total_reservation_cost: _,
74    } = report;
75}
76
77fn print_timezone_warnings(
78    cdr: &cdr::Versioned<'_>,
79    warnings: &warning::Set<timezone::WarningKind>,
80) {
81    if warnings.is_empty() {
82        return;
83    }
84
85    eprintln!("WARN: {} warnings from the timezone search", warnings.len());
86
87    eprintln!(
88        "WARN: {} warnings from the timezone search:\n {}",
89        warnings.len(),
90        warning::SetWriter::new(cdr.as_element(), warnings)
91    );
92}
93
94fn print_pricing_warnings(cdr: &cdr::Versioned<'_>, warnings: &warning::Set<price::WarningKind>) {
95    if warnings.is_empty() {
96        return;
97    }
98
99    eprintln!(
100        "WARN: {} warnings from the linting:\n {}",
101        warnings.len(),
102        warning::SetWriter::new(cdr.as_element(), warnings)
103    );
104}