#[cfg(test)]
pub(crate) mod test;
#[cfg(test)]
mod test_real_world;
pub(crate) mod v211;
pub(crate) mod v221;
pub(crate) mod v2x;
use std::{borrow::Cow, fmt};
use crate::{
country, currency, datetime, duration, enumeration, from_warning_all, guess, json, lint, money,
number, string, warning, ObjectType, ParseError, ReasonableStr, Version,
};
#[derive(Debug)]
pub enum Warning {
Country(country::Warning),
Currency(currency::Warning),
DateTime(datetime::Warning),
Decode(json::decode::Warning),
Duration(duration::Warning),
Enum(enumeration::Warning),
FieldInvalidType {
expected_type: json::ValueKind,
},
FieldInvalidValue {
value: String,
message: Cow<'static, str>,
},
FieldRequired {
field_name: Cow<'static, str>,
},
Money(money::Warning),
TotalCostClampedToMin,
TotalCostClampedToMax,
NoElements,
NotActive,
Number(number::Warning),
String(string::Warning),
}
impl Warning {
fn field_invalid_value(
value: impl Into<String>,
message: impl Into<Cow<'static, str>>,
) -> Self {
Warning::FieldInvalidValue {
value: value.into(),
message: message.into(),
}
}
}
impl fmt::Display for Warning {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::String(warning_kind) => write!(f, "{warning_kind}"),
Self::Country(warning_kind) => write!(f, "{warning_kind}"),
Self::Currency(warning_kind) => write!(f, "{warning_kind}"),
Self::DateTime(warning_kind) => write!(f, "{warning_kind}"),
Self::Decode(warning_kind) => write!(f, "{warning_kind}"),
Self::Duration(warning_kind) => write!(f, "{warning_kind}"),
Self::Enum(warning_kind) => write!(f, "{warning_kind}"),
Self::FieldInvalidType { expected_type } => {
write!(f, "Field has invalid type. Expected type `{expected_type}`")
}
Self::FieldInvalidValue { value, message } => {
write!(f, "Field has invalid value `{value}`: {message}")
}
Self::FieldRequired { field_name } => {
write!(f, "Field is required: `{field_name}`")
}
Self::Money(warning_kind) => write!(f, "{warning_kind}"),
Self::NoElements => f.write_str("The tariff has no `elements`"),
Self::NotActive => f.write_str("The tariff is not active for `Cdr::start_date_time`"),
Self::Number(warning_kind) => write!(f, "{warning_kind}"),
Self::TotalCostClampedToMin => write!(
f,
"The given tariff has a `min_price` set and the `total_cost` fell below it."
),
Self::TotalCostClampedToMax => write!(
f,
"The given tariff has a `max_price` set and the `total_cost` exceeded it."
),
}
}
}
impl crate::Warning for Warning {
fn id(&self) -> warning::Id {
match self {
Self::String(warning) => warning.id(),
Self::Country(warning) => warning.id(),
Self::Currency(warning) => warning.id(),
Self::DateTime(warning) => warning.id(),
Self::Decode(warning) => warning.id(),
Self::Duration(warning) => warning.id(),
Self::Enum(warning) => warning.id(),
Self::FieldInvalidType { expected_type } => {
warning::Id::from_string(format!("field_invalid_type({expected_type})"))
}
Self::FieldInvalidValue { value, .. } => {
warning::Id::from_string(format!("field_invalid_value({value})"))
}
Self::FieldRequired { field_name } => {
warning::Id::from_string(format!("field_required({field_name})"))
}
Self::Money(warning) => warning.id(),
Self::NoElements => warning::Id::from_static("no_elements"),
Self::NotActive => warning::Id::from_static("not_active"),
Self::Number(warning) => warning.id(),
Self::TotalCostClampedToMin => warning::Id::from_static("total_cost_clamped_to_min"),
Self::TotalCostClampedToMax => warning::Id::from_static("total_cost_clamped_to_max"),
}
}
}
from_warning_all!(
country::Warning => Warning::Country,
currency::Warning => Warning::Currency,
datetime::Warning => Warning::DateTime,
duration::Warning => Warning::Duration,
enumeration::Warning => Warning::Enum,
json::decode::Warning => Warning::Decode,
money::Warning => Warning::Money,
number::Warning => Warning::Number,
string::Warning => Warning::String
);
#[derive(Clone, Debug)]
pub(crate) struct CpoId<'buf> {
pub country_code: country::Code,
pub id: string::CiExactLen<'buf, 3>,
}
pub fn parse_with_version(
tariff_json: &str,
version: Version,
) -> Result<ParseReport<'_>, ParseError> {
let tariff_json =
ReasonableStr::new(tariff_json).map_err(ParseError::from_kind(ObjectType::Tariff))?;
match version {
Version::V221 => {
let schema = &*crate::v221::TARIFF_SCHEMA;
let report =
json::parse_with_schema(tariff_json, schema).map_err(ParseError::from_cdr_err)?;
let json::ParseReport {
element,
unexpected_fields,
} = report;
Ok(ParseReport {
tariff: Versioned::new(tariff_json.into_inner(), element, Version::V221),
unexpected_fields,
})
}
Version::V211 => {
let schema = &*crate::v211::TARIFF_SCHEMA;
let report =
json::parse_with_schema(tariff_json, schema).map_err(ParseError::from_cdr_err)?;
let json::ParseReport {
element,
unexpected_fields,
} = report;
Ok(ParseReport {
tariff: Versioned::new(tariff_json.into_inner(), element, Version::V211),
unexpected_fields,
})
}
}
}
pub fn parse(tariff_json: &str) -> Result<guess::TariffVersion<'_>, ParseError> {
let tariff_json =
ReasonableStr::new(tariff_json).map_err(ParseError::from_kind(ObjectType::Tariff))?;
guess::tariff_version(tariff_json)
}
pub fn parse_and_report(tariff_json: &str) -> Result<guess::TariffReport<'_>, ParseError> {
let tariff_json =
ReasonableStr::new(tariff_json).map_err(ParseError::from_kind(ObjectType::Tariff))?;
guess::tariff_version_with_report(tariff_json)
}
#[derive(Debug)]
pub struct ParseReport<'buf> {
pub tariff: Versioned<'buf>,
pub unexpected_fields: json::UnexpectedFields<'buf>,
}
#[derive(Clone)]
pub struct Versioned<'buf> {
source: &'buf str,
element: json::Element<'buf>,
version: Version,
}
impl fmt::Debug for Versioned<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
fmt::Debug::fmt(&self.element, f)
} else {
match self.version {
Version::V211 => f.write_str("V211"),
Version::V221 => f.write_str("V221"),
}
}
}
}
impl crate::Versioned for Versioned<'_> {
fn version(&self) -> Version {
self.version
}
}
impl<'buf> Versioned<'buf> {
pub(crate) fn new(source: &'buf str, element: json::Element<'buf>, version: Version) -> Self {
Self {
source,
element,
version,
}
}
pub fn into_element(self) -> json::Element<'buf> {
self.element
}
pub fn as_element(&self) -> &json::Element<'buf> {
&self.element
}
pub fn as_json_str(&self) -> &'buf str {
self.source
}
}
#[derive(Debug)]
pub struct Unversioned<'buf> {
source: &'buf str,
element: json::Element<'buf>,
}
impl<'buf> Unversioned<'buf> {
pub(crate) fn new(source: &'buf str, elem: json::Element<'buf>) -> Self {
Self {
source,
element: elem,
}
}
pub fn into_element(self) -> json::Element<'buf> {
self.element
}
pub fn as_element(&self) -> &json::Element<'buf> {
&self.element
}
pub fn as_json_str(&self) -> &'buf str {
self.source
}
}
impl<'buf> crate::Unversioned for Unversioned<'buf> {
type Versioned = Versioned<'buf>;
fn force_into_versioned(self, version: Version) -> Versioned<'buf> {
let Self { source, element } = self;
Versioned {
source,
element,
version,
}
}
}
pub fn lint(tariff: &Versioned<'_>) -> lint::tariff::Report {
lint::tariff(tariff)
}