#[cfg(test)]
mod tests;
#[cfg(test)]
mod tests_ir;
#[cfg(test)]
mod tests_cdr_ir;
use super::{
build, ocpi_enum, BuilderKind, Enum, Field, Integrity, Number, Object, Scalar, Schema, Str,
Warning,
};
use crate::{json, Caveat};
const MAX_LEN_IDENTIFIER: usize = 36;
const MAX_LEN_LONG_IDENTIFIER: usize = 39;
const MAX_LEN_EVSE_ID: usize = 48;
const MAX_LEN_CURRENCY: usize = 3;
const MAX_LEN_PARTY_ID: usize = 3;
const MAX_LEN_COUNTRY: usize = 3;
const MAX_LEN_COUNTRY_CODE: usize = 2;
const MAX_LEN_LANGUAGE: usize = 2;
const MAX_LEN_LATITUDE: usize = 10;
const MAX_LEN_LONGITUDE: usize = 11;
const MAX_LEN_POSTAL_CODE: usize = 10;
const MAX_LEN_ADDRESS: usize = 45;
const MAX_LEN_CITY: usize = 45;
const MAX_LEN_STATE: usize = 20;
const MAX_LEN_NAME: usize = 255;
const MAX_LEN_URL: usize = 255;
const MAX_LEN_METER_ID: usize = 255;
const MAX_LEN_REMARK: usize = 255;
const MAX_LEN_ENERGY_MIX_NAME: usize = 64;
const MAX_LEN_DISPLAY_TEXT: usize = 512;
const MAX_LEN_NATURE: usize = 32;
const MAX_LEN_PLAIN_DATA: usize = 512;
const MAX_LEN_SIGNED_DATA: usize = 5000;
const MAX_LEN_PUBLIC_KEY: usize = 512;
const MAX_LEN_SIGNED_DATA_URL: usize = 512;
ocpi_enum!(V221TokenType, TokenType {
AdHocUser = "AD_HOC_USER",
AppUser = "APP_USER",
Other = "OTHER",
Rfid = "RFID",
});
ocpi_enum!(V221ConnectorFormat, ConnectorFormat {
Socket = "SOCKET",
Cable = "CABLE",
});
ocpi_enum!(V221PowerType, PowerType {
Ac1Phase = "AC_1_PHASE",
Ac2Phase = "AC_2_PHASE",
Ac2PhaseSplit = "AC_2_PHASE_SPLIT",
Ac3Phase = "AC_3_PHASE",
Dc = "DC",
});
ocpi_enum!(V221ConnectorType, ConnectorType {
Chademo = "CHADEMO",
Chaoji = "CHAOJI",
DomesticA = "DOMESTIC_A",
DomesticB = "DOMESTIC_B",
DomesticC = "DOMESTIC_C",
DomesticD = "DOMESTIC_D",
DomesticE = "DOMESTIC_E",
DomesticF = "DOMESTIC_F",
DomesticG = "DOMESTIC_G",
DomesticH = "DOMESTIC_H",
DomesticI = "DOMESTIC_I",
DomesticJ = "DOMESTIC_J",
DomesticK = "DOMESTIC_K",
DomesticL = "DOMESTIC_L",
DomesticM = "DOMESTIC_M",
DomesticN = "DOMESTIC_N",
DomesticO = "DOMESTIC_O",
GbtAc = "GBT_AC",
GbtDc = "GBT_DC",
Iec603092Single16 = "IEC_60309_2_single_16",
Iec603092Three16 = "IEC_60309_2_three_16",
Iec603092Three32 = "IEC_60309_2_three_32",
Iec603092Three64 = "IEC_60309_2_three_64",
Iec62196T1 = "IEC_62196_T1",
Iec62196T1Combo = "IEC_62196_T1_COMBO",
Iec62196T2 = "IEC_62196_T2",
Iec62196T2Combo = "IEC_62196_T2_COMBO",
Iec62196T3A = "IEC_62196_T3A",
Iec62196T3C = "IEC_62196_T3C",
Nema520 = "NEMA_5_20",
Nema630 = "NEMA_6_30",
Nema650 = "NEMA_6_50",
Nema1030 = "NEMA_10_30",
Nema1050 = "NEMA_10_50",
Nema1430 = "NEMA_14_30",
Nema1450 = "NEMA_14_50",
PantographBottomUp = "PANTOGRAPH_BOTTOM_UP",
PantographTopDown = "PANTOGRAPH_TOP_DOWN",
TeslaR = "TESLA_R",
TeslaS = "TESLA_S",
});
ocpi_enum!(V221AuthMethod, AuthMethod {
AuthRequest = "AUTH_REQUEST",
Command = "COMMAND",
Whitelist = "WHITELIST",
});
ocpi_enum!(V221CdrDimensionType, CdrDimensionType {
Current = "CURRENT",
Energy = "ENERGY",
EnergyExport = "ENERGY_EXPORT",
EnergyImport = "ENERGY_IMPORT",
MaxCurrent = "MAX_CURRENT",
MinCurrent = "MIN_CURRENT",
MaxPower = "MAX_POWER",
MinPower = "MIN_POWER",
ParkingTime = "PARKING_TIME",
Power = "POWER",
ReservationTime = "RESERVATION_TIME",
StateOfCharge = "STATE_OF_CHARGE",
Time = "TIME",
});
ocpi_enum!(V221TariffDimensionType, TariffDimensionType {
Energy = "ENERGY",
Flat = "FLAT",
ParkingTime = "PARKING_TIME",
Time = "TIME",
});
ocpi_enum!(V221DayOfWeek, DayOfWeek {
Monday = "MONDAY",
Tuesday = "TUESDAY",
Wednesday = "WEDNESDAY",
Thursday = "THURSDAY",
Friday = "FRIDAY",
Saturday = "SATURDAY",
Sunday = "SUNDAY",
});
ocpi_enum!(V221ReservationRestrictionType, ReservationRestrictionType {
Reservation = "RESERVATION",
ReservationExpires = "RESERVATION_EXPIRES",
});
ocpi_enum!(V221TariffType, TariffType {
AdHocPayment = "AD_HOC_PAYMENT",
ProfileCheap = "PROFILE_CHEAP",
ProfileFast = "PROFILE_FAST",
ProfileGreen = "PROFILE_GREEN",
Regular = "REGULAR",
});
ocpi_enum!(V221EnergySourceCategory, EnergySourceCategory {
Nuclear = "NUCLEAR",
GeneralFossil = "GENERAL_FOSSIL",
Coal = "COAL",
Gas = "GAS",
GeneralGreen = "GENERAL_GREEN",
Solar = "SOLAR",
Wind = "WIND",
Water = "WATER",
});
ocpi_enum!(V221EnvironmentalImpactCategory, EnvironmentalImpactCategory {
NuclearWaste = "NUCLEAR_WASTE",
CarbonDioxide = "CARBON_DIOXIDE",
});
static DAY_OF_WEEK: Schema = Schema::Scalar(Scalar::Enum(DayOfWeek::VARIANTS));
static PRICE_OBJ: Object = Object {
fields: &[
Field::required("excl_vat", Scalar::Number),
Field::optional("incl_vat", Scalar::Number),
],
kind: BuilderKind::V221Price,
};
static GEO_LOCATION_OBJ: Object = Object {
fields: &[
Field::required("latitude", Scalar::StringMax(MAX_LEN_LATITUDE)),
Field::required("longitude", Scalar::StringMax(MAX_LEN_LONGITUDE)),
],
kind: BuilderKind::Ignore,
};
static CDR_TOKEN_OBJ: Object = Object {
fields: &[
Field::required("contract_id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("country_code", Scalar::StringMax(MAX_LEN_COUNTRY_CODE)),
Field::required("party_id", Scalar::StringMax(MAX_LEN_PARTY_ID)),
Field::required("type", Scalar::Enum(TokenType::VARIANTS)),
Field::required("uid", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
],
kind: BuilderKind::Ignore,
};
static CDR_LOCATION_OBJ: Object = Object {
fields: &[
Field::required("address", Scalar::StringMax(MAX_LEN_ADDRESS)),
Field::required("city", Scalar::StringMax(MAX_LEN_CITY)),
Field::required("connector_format", Scalar::Enum(ConnectorFormat::VARIANTS)),
Field::required("connector_id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("connector_power_type", Scalar::Enum(PowerType::VARIANTS)),
Field::required("connector_standard", Scalar::Enum(ConnectorType::VARIANTS)),
Field::required_object("coordinates", &GEO_LOCATION_OBJ),
Field::required("country", Scalar::StringMax(MAX_LEN_COUNTRY)),
Field::required("evse_id", Scalar::StringMax(MAX_LEN_EVSE_ID)),
Field::required("evse_uid", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::optional("name", Scalar::StringMax(MAX_LEN_NAME)),
Field::optional("postal_code", Scalar::StringMax(MAX_LEN_POSTAL_CODE)),
Field::optional("state", Scalar::StringMax(MAX_LEN_STATE)),
],
kind: BuilderKind::Ignore,
};
static CDR_DIMENSION_OBJ: Object = Object {
fields: &[
Field::required("type", Scalar::Enum(CdrDimensionType::VARIANTS)),
Field::required("volume", Scalar::Number),
],
kind: BuilderKind::V221CdrDimension,
};
static CDR_DIMENSION: Schema = Schema::Object(&CDR_DIMENSION_OBJ);
static CHARGING_PERIOD_OBJ: Object = Object {
fields: &[
Field::required_array("dimensions", &CDR_DIMENSION),
Field::required("start_date_time", Scalar::String),
Field::optional("tariff_id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
],
kind: BuilderKind::V221ChargingPeriod,
};
static CHARGING_PERIOD: Schema = Schema::Object(&CHARGING_PERIOD_OBJ);
static SIGNED_VALUE_OBJ: Object = Object {
fields: &[
Field::required("nature", Scalar::StringMax(MAX_LEN_NATURE)),
Field::required("plain_data", Scalar::StringMax(MAX_LEN_PLAIN_DATA)),
Field::required("signed_data", Scalar::StringMax(MAX_LEN_SIGNED_DATA)),
],
kind: BuilderKind::Ignore,
};
static SIGNED_VALUE: Schema = Schema::Object(&SIGNED_VALUE_OBJ);
static SIGNED_DATA_OBJ: Object = Object {
fields: &[
Field::required("encoding_method", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::optional("encoding_method_version", Scalar::String),
Field::optional("public_key", Scalar::StringMax(MAX_LEN_PUBLIC_KEY)),
Field::required_array("signed_values", &SIGNED_VALUE),
Field::optional("url", Scalar::StringMax(MAX_LEN_SIGNED_DATA_URL)),
],
kind: BuilderKind::Ignore,
};
static DISPLAY_TEXT_OBJ: Object = Object {
fields: &[
Field::required("language", Scalar::StringMax(MAX_LEN_LANGUAGE)),
Field::required("text", Scalar::StringMax(MAX_LEN_DISPLAY_TEXT)),
],
kind: BuilderKind::Ignore,
};
static DISPLAY_TEXT: Schema = Schema::Object(&DISPLAY_TEXT_OBJ);
static ENERGY_SOURCE_OBJ: Object = Object {
fields: &[
Field::required("percentage", Scalar::Number),
Field::required("source", Scalar::Enum(EnergySourceCategory::VARIANTS)),
],
kind: BuilderKind::Ignore,
};
static ENERGY_SOURCE: Schema = Schema::Object(&ENERGY_SOURCE_OBJ);
static ENVIRONMENTAL_IMPACT_OBJ: Object = Object {
fields: &[
Field::required("amount", Scalar::Number),
Field::required(
"category",
Scalar::Enum(EnvironmentalImpactCategory::VARIANTS),
),
],
kind: BuilderKind::Ignore,
};
static ENVIRONMENTAL_IMPACT: Schema = Schema::Object(&ENVIRONMENTAL_IMPACT_OBJ);
static ENERGY_MIX_OBJ: Object = Object {
fields: &[
Field::optional(
"energy_product_name",
Scalar::StringMax(MAX_LEN_ENERGY_MIX_NAME),
),
Field::optional_array("energy_sources", &ENERGY_SOURCE),
Field::optional_array("environ_impact", &ENVIRONMENTAL_IMPACT),
Field::required("is_green_energy", Scalar::Boolean),
Field::optional("supplier_name", Scalar::StringMax(MAX_LEN_ENERGY_MIX_NAME)),
],
kind: BuilderKind::Ignore,
};
static PRICE_COMPONENT_OBJ: Object = Object {
fields: &[
Field::required("price", Scalar::Number),
Field::required("step_size", Scalar::Number),
Field::required("type", Scalar::Enum(TariffDimensionType::VARIANTS)),
Field::optional("vat", Scalar::Number),
],
kind: BuilderKind::V221PriceComponent,
};
static PRICE_COMPONENT: Schema = Schema::Object(&PRICE_COMPONENT_OBJ);
static TARIFF_RESTRICTIONS_OBJ: Object = Object {
fields: &[
Field::optional_array("day_of_week", &DAY_OF_WEEK),
Field::optional("end_date", Scalar::String),
Field::optional("end_time", Scalar::String),
Field::optional("max_current", Scalar::Number),
Field::optional("max_duration", Scalar::Number),
Field::optional("max_kwh", Scalar::Number),
Field::optional("max_power", Scalar::Number),
Field::optional("min_current", Scalar::Number),
Field::optional("min_duration", Scalar::Number),
Field::optional("min_kwh", Scalar::Number),
Field::optional("min_power", Scalar::Number),
Field::optional(
"reservation",
Scalar::Enum(ReservationRestrictionType::VARIANTS),
),
Field::optional("start_date", Scalar::String),
Field::optional("start_time", Scalar::String),
],
kind: BuilderKind::V221Restrictions,
};
static TARIFF_ELEMENT_OBJ: Object = Object {
fields: &[
Field::required_array("price_components", &PRICE_COMPONENT),
Field::optional_object("restrictions", &TARIFF_RESTRICTIONS_OBJ),
],
kind: BuilderKind::V221Element,
};
static TARIFF_ELEMENT: Schema = Schema::Object(&TARIFF_ELEMENT_OBJ);
static TARIFF_OBJ: Object = Object {
fields: &[
Field::required("country_code", Scalar::StringMax(MAX_LEN_COUNTRY_CODE)),
Field::required("currency", Scalar::StringMax(MAX_LEN_CURRENCY)),
Field::required_array("elements", &TARIFF_ELEMENT),
Field::optional("end_date_time", Scalar::String),
Field::optional_object("energy_mix", &ENERGY_MIX_OBJ),
Field::required("id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("last_updated", Scalar::String),
Field::optional_object("max_price", &PRICE_OBJ),
Field::optional_object("min_price", &PRICE_OBJ),
Field::required("party_id", Scalar::StringMax(MAX_LEN_PARTY_ID)),
Field::optional("start_date_time", Scalar::String),
Field::optional_array("tariff_alt_text", &DISPLAY_TEXT),
Field::optional("tariff_alt_url", Scalar::StringMax(MAX_LEN_URL)),
Field::optional("type", Scalar::Enum(TariffType::VARIANTS)),
],
kind: BuilderKind::V221Tariff,
};
static TARIFF: Schema = Schema::Object(&TARIFF_OBJ);
static CDR_OBJ: Object = Object {
fields: &[
Field::required("auth_method", Scalar::Enum(AuthMethod::VARIANTS)),
Field::optional(
"authorization_reference",
Scalar::StringMax(MAX_LEN_IDENTIFIER),
),
Field::required_object("cdr_location", &CDR_LOCATION_OBJ),
Field::required_object("cdr_token", &CDR_TOKEN_OBJ),
Field::required_array("charging_periods", &CHARGING_PERIOD),
Field::required("country_code", Scalar::StringMax(MAX_LEN_COUNTRY_CODE)),
Field::optional("credit", Scalar::Boolean),
Field::optional(
"credit_reference_id",
Scalar::StringMax(MAX_LEN_LONG_IDENTIFIER),
),
Field::required("currency", Scalar::StringMax(MAX_LEN_CURRENCY)),
Field::required("end_date_time", Scalar::String),
Field::optional("home_charging_compensation", Scalar::Boolean),
Field::required("id", Scalar::StringMax(MAX_LEN_LONG_IDENTIFIER)),
Field::optional(
"invoice_reference_id",
Scalar::StringMax(MAX_LEN_LONG_IDENTIFIER),
),
Field::required("last_updated", Scalar::String),
Field::optional("meter_id", Scalar::StringMax(MAX_LEN_METER_ID)),
Field::required("party_id", Scalar::StringMax(MAX_LEN_PARTY_ID)),
Field::optional("remark", Scalar::StringMax(MAX_LEN_REMARK)),
Field::optional("session_id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::optional_object("signed_data", &SIGNED_DATA_OBJ),
Field::required("start_date_time", Scalar::String),
Field::optional_array("tariffs", &TARIFF),
Field::required_object("total_cost", &PRICE_OBJ),
Field::required("total_energy", Scalar::Number),
Field::optional_object("total_energy_cost", &PRICE_OBJ),
Field::optional_object("total_fixed_cost", &PRICE_OBJ),
Field::optional_object("total_parking_cost", &PRICE_OBJ),
Field::optional("total_parking_time", Scalar::Number),
Field::optional_object("total_reservation_cost", &PRICE_OBJ),
Field::required("total_time", Scalar::Number),
Field::optional_object("total_time_cost", &PRICE_OBJ),
],
kind: BuilderKind::V221Cdr,
};
static CDR: Schema = Schema::Object(&CDR_OBJ);
#[derive(Default, Clone, Debug)]
pub(crate) struct Cdr<'buf> {
pub currency: Integrity<Str<'buf>>,
pub start_date_time: Integrity<Str<'buf>>,
pub end_date_time: Integrity<Str<'buf>>,
pub charging_periods: Integrity<Vec<Integrity<ChargingPeriod<'buf>>>>,
pub total_cost: Integrity<Price<'buf>>,
pub total_energy: Integrity<Number<'buf>>,
pub total_time: Integrity<Number<'buf>>,
pub total_parking_time: Integrity<Option<Number<'buf>>>,
pub total_energy_cost: Integrity<Option<Price<'buf>>>,
pub total_fixed_cost: Integrity<Option<Price<'buf>>>,
pub total_parking_cost: Integrity<Option<Price<'buf>>>,
pub total_reservation_cost: Integrity<Option<Price<'buf>>>,
pub total_time_cost: Integrity<Option<Price<'buf>>>,
pub tariffs: Integrity<Option<Vec<Integrity<Tariff<'buf>>>>>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct ChargingPeriod<'buf> {
pub start_date_time: Integrity<Str<'buf>>,
pub dimensions: Integrity<Vec<Integrity<Dimension<'buf>>>>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct Dimension<'buf> {
pub dimension_type: Integrity<Enum<'buf>>,
pub volume: Integrity<Number<'buf>>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct Tariff<'buf> {
pub country_code: Integrity<Str<'buf>>,
pub currency: Integrity<Str<'buf>>,
pub id: Integrity<Str<'buf>>,
pub party_id: Integrity<Str<'buf>>,
pub min_price: Integrity<Option<Price<'buf>>>,
pub max_price: Integrity<Option<Price<'buf>>>,
pub start_date_time: Integrity<Option<Str<'buf>>>,
pub end_date_time: Integrity<Option<Str<'buf>>>,
pub elements: Integrity<Vec<Integrity<Element<'buf>>>>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct Element<'buf> {
pub price_components: Integrity<Vec<Integrity<PriceComponent<'buf>>>>,
pub restrictions: Integrity<Option<Restrictions<'buf>>>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct PriceComponent<'buf> {
pub dimension_type: Integrity<Enum<'buf>>,
pub vat: Integrity<Option<Number<'buf>>>,
pub price: Integrity<Number<'buf>>,
pub step_size: Integrity<Number<'buf>>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct Price<'buf> {
pub excl_vat: Integrity<Number<'buf>>,
pub incl_vat: Integrity<Option<Number<'buf>>>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct Restrictions<'buf> {
pub start_time: Integrity<Option<Str<'buf>>>,
pub end_time: Integrity<Option<Str<'buf>>>,
pub start_date: Integrity<Option<Str<'buf>>>,
pub end_date: Integrity<Option<Str<'buf>>>,
pub min_kwh: Integrity<Option<Number<'buf>>>,
pub max_kwh: Integrity<Option<Number<'buf>>>,
pub min_current: Integrity<Option<Number<'buf>>>,
pub max_current: Integrity<Option<Number<'buf>>>,
pub min_power: Integrity<Option<Number<'buf>>>,
pub max_power: Integrity<Option<Number<'buf>>>,
pub min_duration: Integrity<Option<Number<'buf>>>,
pub max_duration: Integrity<Option<Number<'buf>>>,
pub day_of_week: Integrity<Option<Vec<Integrity<Enum<'buf>>>>>,
pub reservation: Integrity<Option<Enum<'buf>>>,
}
pub(crate) fn build_tariff<'buf>(doc: &json::Document<'buf>) -> Caveat<Tariff<'buf>, Warning> {
let (node, warnings) = super::walk(doc, &TARIFF).into_parts();
let tariff = if let build::Node::Tariff(tariff) = node {
tariff
} else {
Tariff::default()
};
Caveat::new(tariff, warnings)
}
pub(crate) fn build_cdr<'buf>(doc: &json::Document<'buf>) -> Caveat<Cdr<'buf>, Warning> {
let (node, warnings) = super::walk(doc, &CDR).into_parts();
let cdr = if let build::Node::Cdr(cdr) = node {
cdr
} else {
Cdr::default()
};
Caveat::new(cdr, warnings)
}