#[cfg(test)]
mod tests;
#[cfg(test)]
mod tests_ir;
#[cfg(test)]
mod tests_cdr_ir;
use crate::{json, schema::Warning, Caveat};
use super::{build, BuilderKind, Enum, Field, Integrity, Number, Object, Scalar, Schema, Str};
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_COUNTRY: usize = 3;
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_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_TIME_ZONE: usize = 255;
const MAX_LEN_ENERGY_MIX_NAME: usize = 64;
const MAX_LEN_DISPLAY_TEXT: usize = 512;
const MAX_LEN_FLOOR_LEVEL: usize = 4;
const MAX_LEN_PHYSICAL_REFERENCE: usize = 16;
static ANY: Schema = Schema::Scalar(Scalar::Any);
const CONNECTOR_FORMAT_VALUES: &[&str] = &["SOCKET", "CABLE"];
const POWER_TYPE_VALUES: &[&str] = &["AC_1_PHASE", "AC_3_PHASE", "DC"];
const CONNECTOR_TYPE_VALUES: &[&str] = &[
"CHADEMO",
"DOMESTIC_A",
"DOMESTIC_B",
"DOMESTIC_C",
"DOMESTIC_D",
"DOMESTIC_E",
"DOMESTIC_F",
"DOMESTIC_G",
"DOMESTIC_H",
"DOMESTIC_I",
"DOMESTIC_J",
"DOMESTIC_K",
"DOMESTIC_L",
"IEC_60309_2_single_16",
"IEC_60309_2_three_16",
"IEC_60309_2_three_32",
"IEC_60309_2_three_64",
"IEC_62196_T1",
"IEC_62196_T1_COMBO",
"IEC_62196_T2",
"IEC_62196_T2_COMBO",
"IEC_62196_T3A",
"IEC_62196_T3C",
"TESLA_R",
"TESLA_S",
];
const CAPABILITY_VALUES: &[&str] = &[
"CHARGING_PROFILE_CAPABLE",
"CREDIT_CARD_PAYABLE",
"REMOTE_START_STOP_CAPABLE",
"RESERVABLE",
"RFID_READER",
"UNLOCK_CAPABLE",
];
const PARKING_RESTRICTION_VALUES: &[&str] =
&["EV_ONLY", "PLUGGED", "DISABLED", "CUSTOMERS", "MOTORCYCLES"];
const STATUS_VALUES: &[&str] = &[
"AVAILABLE",
"BLOCKED",
"CHARGING",
"INOPERATIVE",
"OUTOFORDER",
"PLANNED",
"REMOVED",
"RESERVED",
"UNKNOWN",
];
const FACILITY_VALUES: &[&str] = &[
"HOTEL",
"RESTAURANT",
"CAFE",
"MALL",
"SUPERMARKET",
"SPORT",
"RECREATION_AREA",
"NATURE",
"MUSEUM",
"BUS_STOP",
"TAXI_STAND",
"TRAIN_STATION",
"AIRPORT",
"CARPOOL_PARKING",
"FUEL_STATION",
"WIFI",
];
const LOCATION_TYPE_VALUES: &[&str] = &[
"ON_STREET",
"PARKING_GARAGE",
"UNDERGROUND_GARAGE",
"PARKING_LOT",
"OTHER",
"UNKNOWN",
];
const AUTH_METHOD_VALUES: &[&str] = &["AUTH_REQUEST", "WHITELIST"];
const CDR_DIMENSION_TYPE_VALUES: &[&str] = &[
"ENERGY",
"FLAT",
"MAX_CURRENT",
"MIN_CURRENT",
"PARKING_TIME",
"TIME",
];
const TARIFF_DIMENSION_TYPE_VALUES: &[&str] = &["ENERGY", "FLAT", "PARKING_TIME", "TIME"];
const DAY_OF_WEEK_VALUES: &[&str] = &[
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
"SUNDAY",
];
const ENERGY_SOURCE_CATEGORY_VALUES: &[&str] = &[
"NUCLEAR",
"GENERAL_FOSSIL",
"COAL",
"GAS",
"GENERAL_GREEN",
"SOLAR",
"WIND",
"WATER",
];
const ENVIRONMENTAL_IMPACT_CATEGORY_VALUES: &[&str] = &["NUCLEAR_WASTE", "CARBON_DIOXIDE"];
static CAPABILITY: Schema = Schema::Scalar(Scalar::Enum(CAPABILITY_VALUES));
static PARKING_RESTRICTION: Schema = Schema::Scalar(Scalar::Enum(PARKING_RESTRICTION_VALUES));
static FACILITY: Schema = Schema::Scalar(Scalar::Enum(FACILITY_VALUES));
static DAY_OF_WEEK: Schema = Schema::Scalar(Scalar::Enum(DAY_OF_WEEK_VALUES));
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 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(ENERGY_SOURCE_CATEGORY_VALUES)),
],
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("source", Scalar::Enum(ENVIRONMENTAL_IMPACT_CATEGORY_VALUES)),
],
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 CONNECTOR_OBJ: Object = Object {
fields: &[
Field::required("amperage", Scalar::Number),
Field::required("format", Scalar::Enum(CONNECTOR_FORMAT_VALUES)),
Field::required("id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("last_updated", Scalar::String),
Field::required("power_type", Scalar::Enum(POWER_TYPE_VALUES)),
Field::required("standard", Scalar::Enum(CONNECTOR_TYPE_VALUES)),
Field::optional("tariff_id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::optional("terms_and_conditions", Scalar::StringMax(MAX_LEN_URL)),
Field::required("voltage", Scalar::Number),
],
kind: BuilderKind::Ignore,
};
static CONNECTOR: Schema = Schema::Object(&CONNECTOR_OBJ);
static EVSE_OBJ: Object = Object {
fields: &[
Field::optional_array("capabilities", &CAPABILITY),
Field::required_array("connectors", &CONNECTOR),
Field::optional_object("coordinates", &GEO_LOCATION_OBJ),
Field::optional_array("directions", &DISPLAY_TEXT),
Field::optional("evse_id", Scalar::StringMax(MAX_LEN_EVSE_ID)),
Field::optional("floor_level", Scalar::StringMax(MAX_LEN_FLOOR_LEVEL)),
Field::optional_array("images", &ANY),
Field::required("last_updated", Scalar::String),
Field::optional_array("parking_restrictions", &PARKING_RESTRICTION),
Field::optional(
"physical_reference",
Scalar::StringMax(MAX_LEN_PHYSICAL_REFERENCE),
),
Field::required("status", Scalar::Enum(STATUS_VALUES)),
Field::optional_array("status_schedule", &ANY),
Field::required("uid", Scalar::StringMax(MAX_LEN_LONG_IDENTIFIER)),
],
kind: BuilderKind::Ignore,
};
static EVSE: Schema = Schema::Object(&EVSE_OBJ);
static LOCATION_OBJ: Object = Object {
fields: &[
Field::required("address", Scalar::StringMax(MAX_LEN_ADDRESS)),
Field::optional("charging_when_closed", Scalar::Boolean),
Field::required("city", Scalar::StringMax(MAX_LEN_CITY)),
Field::required_object("coordinates", &GEO_LOCATION_OBJ),
Field::required("country", Scalar::StringMax(MAX_LEN_COUNTRY)),
Field::optional_array("directions", &DISPLAY_TEXT),
Field::optional_object("energy_mix", &ENERGY_MIX_OBJ),
Field::optional_array("evses", &EVSE),
Field::optional_array("facilities", &FACILITY),
Field::required("id", Scalar::StringMax(MAX_LEN_LONG_IDENTIFIER)),
Field::optional_array("images", &ANY),
Field::required("last_updated", Scalar::String),
Field::optional("name", Scalar::StringMax(MAX_LEN_NAME)),
Field::optional("opening_times", Scalar::Any),
Field::optional("operator", Scalar::Any),
Field::optional("owner", Scalar::Any),
Field::required("postal_code", Scalar::StringMax(MAX_LEN_POSTAL_CODE)),
Field::optional_array("related_locations", &ANY),
Field::optional("suboperator", Scalar::Any),
Field::optional("time_zone", Scalar::StringMax(MAX_LEN_TIME_ZONE)),
Field::required("type", Scalar::Enum(LOCATION_TYPE_VALUES)),
],
kind: BuilderKind::Ignore,
};
static CDR_DIMENSION_OBJ: Object = Object {
fields: &[
Field::required("type", Scalar::Enum(CDR_DIMENSION_TYPE_VALUES)),
Field::required("volume", Scalar::Number),
],
kind: BuilderKind::V211CdrDimension,
};
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),
],
kind: BuilderKind::V211ChargingPeriod,
};
static CHARGING_PERIOD: Schema = Schema::Object(&CHARGING_PERIOD_OBJ);
static PRICE_COMPONENT_OBJ: Object = Object {
fields: &[
Field::required("price", Scalar::Number),
Field::required("step_size", Scalar::Number),
Field::required("type", Scalar::Enum(TARIFF_DIMENSION_TYPE_VALUES)),
],
kind: BuilderKind::V211PriceComponent,
};
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_duration", Scalar::Number),
Field::optional("max_kwh", Scalar::Number),
Field::optional("max_power", Scalar::Number),
Field::optional("min_duration", Scalar::Number),
Field::optional("min_kwh", Scalar::Number),
Field::optional("min_power", Scalar::Number),
Field::optional("start_date", Scalar::String),
Field::optional("start_time", Scalar::String),
],
kind: BuilderKind::V211Restrictions,
};
static TARIFF_ELEMENT_OBJ: Object = Object {
fields: &[
Field::required_array("price_components", &PRICE_COMPONENT),
Field::optional_object("restrictions", &TARIFF_RESTRICTIONS_OBJ),
],
kind: BuilderKind::V211Element,
};
static TARIFF_ELEMENT: Schema = Schema::Object(&TARIFF_ELEMENT_OBJ);
static TARIFF_OBJ: Object = Object {
fields: &[
Field::required("currency", Scalar::StringMax(MAX_LEN_CURRENCY)),
Field::required_array("elements", &TARIFF_ELEMENT),
Field::optional_object("energy_mix", &ENERGY_MIX_OBJ),
Field::required("id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("last_updated", Scalar::String),
Field::optional_array("tariff_alt_text", &DISPLAY_TEXT),
Field::optional("tariff_alt_url", Scalar::StringMax(MAX_LEN_URL)),
],
kind: BuilderKind::V211Tariff,
};
static TARIFF: Schema = Schema::Object(&TARIFF_OBJ);
static CDR_OBJ: Object = Object {
fields: &[
Field::required("auth_id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("auth_method", Scalar::Enum(AUTH_METHOD_VALUES)),
Field::required_array("charging_periods", &CHARGING_PERIOD),
Field::required("currency", Scalar::StringMax(MAX_LEN_CURRENCY)),
Field::required("id", Scalar::StringMax(MAX_LEN_IDENTIFIER)),
Field::required("last_updated", Scalar::String),
Field::required_object("location", &LOCATION_OBJ),
Field::optional("meter_id", Scalar::StringMax(MAX_LEN_METER_ID)),
Field::optional("remark", Scalar::StringMax(MAX_LEN_REMARK)),
Field::required("start_date_time", Scalar::String),
Field::required("stop_date_time", Scalar::String),
Field::optional_array("tariffs", &TARIFF),
Field::required("total_cost", Scalar::Number),
Field::required("total_energy", Scalar::Number),
Field::optional("total_parking_time", Scalar::Number),
Field::required("total_time", Scalar::Number),
],
kind: BuilderKind::V211Cdr,
};
static CDR: Schema = Schema::Object(&CDR_OBJ);
#[derive(Clone, Default, Debug)]
pub(crate) struct Cdr<'buf> {
pub currency: Integrity<Str<'buf>>,
pub start_date_time: Integrity<Str<'buf>>,
pub stop_date_time: Integrity<Str<'buf>>,
pub charging_periods: Integrity<Vec<Integrity<ChargingPeriod<'buf>>>>,
pub total_cost: Integrity<Number<'buf>>,
pub total_energy: Integrity<Number<'buf>>,
pub total_time: Integrity<Number<'buf>>,
pub total_parking_time: Integrity<Option<Number<'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 currency: Integrity<Str<'buf>>,
pub id: Integrity<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 price: Integrity<Number<'buf>>,
pub step_size: Integrity<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_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(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::V211Tariff(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::V211Cdr(cdr) = node {
cdr
} else {
Cdr::default()
};
Caveat::new(cdr, warnings)
}