#[cfg(test)]
mod test_every_field_set;
#[cfg(test)]
mod test_from_schema;
use std::convert::Infallible;
use chrono::{DateTime, NaiveDate, NaiveTime, TimeDelta, Utc};
use rust_decimal::Decimal;
use super::{v2x, CpoId, Warning};
use crate::{
country, currency,
duration::Seconds,
energy::{Ampere, Kw, Kwh},
money::VatOrigin,
number::FromDecimal as _,
schema, string,
warning::{self, GatherWarnings as _, IntoCaveat as _, IntoInfallible as _},
FromSchema, Money, Price, Verdict, Weekday,
};
#[derive(Debug)]
pub(crate) struct Tariff<'buf> {
pub party_id: Option<CpoId<'buf>>,
pub id: string::CiMaxLen<'buf, 36>,
pub currency: currency::Code,
pub min_price: Option<Price>,
pub max_price: Option<Price>,
pub start_date_time: Option<DateTime<Utc>>,
pub end_date_time: Option<DateTime<Utc>>,
pub elements: Vec<Element>,
}
#[derive(Debug)]
pub(crate) struct Element {
pub price_components: Vec<PriceComponent>,
pub restrictions: Option<Restrictions>,
}
#[derive(Debug)]
pub(crate) struct PriceComponent {
pub dimension_type: v2x::DimensionType,
pub vat: VatOrigin,
pub price: Money,
pub step_size: u64,
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum ReservationRestrictionType {
Reservation,
ReservationExpires,
}
impl FromSchema<'_, schema::v221::ReservationRestrictionType> for ReservationRestrictionType {
type Warning = Infallible;
fn from_schema(
source: &schema::v221::ReservationRestrictionType,
) -> Verdict<Self, Self::Warning> {
use schema::v221::ReservationRestrictionType;
let v = match source {
ReservationRestrictionType::Reservation => Self::Reservation,
ReservationRestrictionType::ReservationExpires => Self::ReservationExpires,
};
Ok(v.into_infallible_caveat())
}
}
#[derive(Debug)]
pub(crate) struct Restrictions {
pub start_time: Option<NaiveTime>,
pub end_time: Option<NaiveTime>,
pub start_date: Option<NaiveDate>,
pub end_date: Option<NaiveDate>,
pub min_kwh: Option<Kwh>,
pub max_kwh: Option<Kwh>,
pub min_current: Option<Ampere>,
pub max_current: Option<Ampere>,
pub min_power: Option<Kw>,
pub max_power: Option<Kw>,
pub min_duration: Option<TimeDelta>,
pub max_duration: Option<TimeDelta>,
pub day_of_week: Option<Vec<Weekday>>,
pub reservation: Option<ReservationRestrictionType>,
}
impl<'buf> FromSchema<'buf, schema::v221::Tariff<'buf>> for Tariff<'buf> {
type Warning = Warning;
fn from_schema(source: &schema::v221::Tariff<'buf>) -> Verdict<Self, Self::Warning> {
let mut warnings = warning::Set::new();
let country_code = warnings.ok_or_bail(&source.country_code)?;
let country_code =
country::CodeSet::from_schema(country_code)?.gather_warnings_into(&mut warnings);
let country_code = match country_code {
country::CodeSet::Alpha2(code) | country::CodeSet::Alpha3(code) => code,
};
let currency = warnings.ok_or_bail(&source.currency)?;
let currency = currency::Code::from_schema(currency)?.gather_warnings_into(&mut warnings);
let id = warnings.ok_or_bail(&source.id)?;
let id = string::CiMaxLen::<'_, 36>::from_schema(id)?.gather_warnings_into(&mut warnings);
let party_id = warnings.ok_or_bail(&source.party_id)?;
let party_id =
string::CiExactLen::<'_, 3>::from_schema(party_id)?.gather_warnings_into(&mut warnings);
let min_price = warnings.ok_or_bail(&source.min_price)?;
let min_price = min_price
.as_ref()
.map(Price::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let max_price = warnings.ok_or_bail(&source.max_price)?;
let max_price = max_price
.as_ref()
.map(Price::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let start_date_time = warnings.ok_or_bail(&source.start_date_time)?;
let start_date_time = start_date_time
.as_ref()
.map(DateTime::<Utc>::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let end_date_time = warnings.ok_or_bail(&source.end_date_time)?;
let end_date_time = end_date_time
.as_ref()
.map(DateTime::<Utc>::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let elements = warnings.ok_or_bail(&source.elements)?;
let mut lowered = Vec::with_capacity(elements.len());
for element in elements {
let element = warnings.ok_or_bail(element)?;
lowered.push(Element::from_schema(element)?.gather_warnings_into(&mut warnings));
}
let tariff = Tariff {
currency,
party_id: Some(CpoId {
country_code,
id: party_id,
}),
id,
min_price,
max_price,
start_date_time,
end_date_time,
elements: lowered,
};
Ok(tariff.into_caveat(warnings))
}
}
impl<'buf> FromSchema<'buf, schema::v221::Element<'buf>> for Element {
type Warning = Warning;
fn from_schema(source: &schema::v221::Element<'buf>) -> Verdict<Self, Self::Warning> {
let mut warnings = warning::Set::new();
let components = warnings.ok_or_bail(&source.price_components)?;
let mut price_components = Vec::with_capacity(components.len());
for component in components {
let component = warnings.ok_or_bail(component)?;
let component = Option::<PriceComponent>::from_schema(component)?
.gather_warnings_into(&mut warnings);
if let Some(component) = component {
price_components.push(component);
}
}
let restrictions = warnings.ok_or_bail(&source.restrictions)?;
let restrictions = restrictions
.as_ref()
.map(Restrictions::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let elem = Element {
price_components,
restrictions,
};
Ok(elem.into_caveat(warnings))
}
}
impl<'buf> FromSchema<'buf, schema::v221::PriceComponent<'buf>> for Option<PriceComponent> {
type Warning = Warning;
fn from_schema(source: &schema::v221::PriceComponent<'buf>) -> Verdict<Self, Self::Warning> {
let mut warnings = warning::Set::new();
if let schema::Integrity::Err(_) = &source.dimension_type {
return Ok(None.into_caveat(warnings));
}
let dimension_type = warnings.ok_or_bail(&source.dimension_type)?;
let dimension_type =
v2x::DimensionType::from_schema(&dimension_type.value()).into_infallible();
let vat = source
.vat
.map_some(VatOrigin::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings)
.unwrap_or(VatOrigin::NotProvided);
let price = warnings.ok_or_bail(&source.price)?;
let price = Decimal::from_schema(price)?.gather_warnings_into(&mut warnings);
let step_size = warnings.ok_or_bail(&source.step_size)?;
let step_size = u64::from_schema(step_size)?.gather_warnings_into(&mut warnings);
let comp = PriceComponent {
dimension_type,
vat,
price: Money::from_decimal(price),
step_size,
};
Ok(Some(comp).into_caveat(warnings))
}
}
impl<'buf> FromSchema<'buf, schema::v221::Restrictions<'buf>> for Restrictions {
type Warning = Warning;
fn from_schema(source: &schema::v221::Restrictions<'buf>) -> Verdict<Self, Self::Warning> {
let mut warnings = warning::Set::new();
let start_time = warnings.ok_or_bail(&source.start_time)?;
let start_time = start_time
.as_ref()
.map(NaiveTime::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let end_time = warnings.ok_or_bail(&source.end_time)?;
let end_time = end_time
.as_ref()
.map(NaiveTime::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let start_date = warnings.ok_or_bail(&source.start_date)?;
let start_date = start_date
.as_ref()
.map(NaiveDate::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let end_date = warnings.ok_or_bail(&source.end_date)?;
let end_date = end_date
.as_ref()
.map(NaiveDate::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let min_kwh = warnings.ok_or_bail(&source.min_kwh)?;
let min_kwh = min_kwh
.as_ref()
.map(Kwh::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let max_kwh = warnings.ok_or_bail(&source.max_kwh)?;
let max_kwh = max_kwh
.as_ref()
.map(Kwh::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let min_current = warnings.ok_or_bail(&source.min_current)?;
let min_current = min_current
.as_ref()
.map(Ampere::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let max_current = warnings.ok_or_bail(&source.max_current)?;
let max_current = max_current
.as_ref()
.map(Ampere::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let min_power = warnings.ok_or_bail(&source.min_power)?;
let min_power = min_power
.as_ref()
.map(Kw::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let max_power = warnings.ok_or_bail(&source.max_power)?;
let max_power = max_power
.as_ref()
.map(Kw::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings);
let min_duration = warnings.ok_or_bail(&source.min_duration)?;
let min_duration = min_duration
.as_ref()
.map(Seconds::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings)
.map(TimeDelta::from);
let max_duration = warnings.ok_or_bail(&source.max_duration)?;
let max_duration = max_duration
.as_ref()
.map(Seconds::from_schema)
.transpose()?
.gather_warnings_into(&mut warnings)
.map(TimeDelta::from);
let day_of_week = warnings.ok_or_bail(&source.day_of_week)?;
let day_of_week = match day_of_week {
Some(days) => {
let mut weekdays = Vec::with_capacity(days.len());
for day in days {
let day = warnings.ok_or_bail(day)?;
weekdays.push(Weekday::from_schema(&day.value()).into_infallible());
}
Some(weekdays)
}
None => None,
};
let reservation = warnings.ok_or_bail(&source.reservation)?;
let reservation = reservation.as_ref().map(|reservation| {
ReservationRestrictionType::from_schema(&reservation.value()).into_infallible()
});
let res = Restrictions {
start_time,
end_time,
start_date,
end_date,
min_kwh,
max_kwh,
min_current,
max_current,
min_power,
max_power,
min_duration,
max_duration,
day_of_week,
reservation,
};
Ok(res.into_caveat(warnings))
}
}