#[cfg(test)]
mod test_real_world;
#[cfg(test)]
mod test_lints;
use tracing::{debug, instrument};
use crate::{
country,
json::{self, FieldsAsExt, FromJson as _},
required_field, string,
warning::{self, DeescalateError as _, GatherWarnings as _, IntoCaveat as _},
Price, Verdict,
};
use super::{v2x, Report, Warning};
#[instrument(skip_all)]
pub(crate) fn lint<'buf>(
tariff_elem: &json::Element<'buf>,
mut warnings: warning::Set<Warning>,
) -> Report {
let Some(fields) = tariff_elem.as_object_fields() else {
return Report { warnings };
};
let fields = fields.as_raw_map();
if let Some(elem) = required_field!(tariff_elem, fields, "country_code", warnings) {
let _code: Option<country::Code> = lint_country_code(elem)
.gather_warnings_into(&mut warnings)
.deescalate_error_into(&mut warnings);
}
if let Some(elem) = required_field!(tariff_elem, fields, "party_id", warnings) {
let _drop: Option<()> = lint_party_id(elem)
.gather_warnings_into(&mut warnings)
.deescalate_error_into(&mut warnings);
}
if let Some(elem) = required_field!(tariff_elem, fields, "currency", warnings) {
let _drop: Option<()> = v2x::currency::lint(elem)
.gather_warnings_into(&mut warnings)
.deescalate_error_into(&mut warnings);
}
{
let start_date_time = fields.get("start_date_time").map(|e| &**e);
let end_date_time = fields.get("end_date_time").map(|e| &**e);
let _drop: Option<()> = v2x::datetime::lint_start_end(start_date_time, end_date_time)
.gather_warnings_into(&mut warnings)
.deescalate_error_into(&mut warnings);
}
{
let min_price = fields.get("min_price").map(|e| &**e);
let max_price = fields.get("max_price").map(|e| &**e);
let _drop: Option<()> = lint_min_max_price(min_price, max_price)
.gather_warnings_into(&mut warnings)
.deescalate_error_into(&mut warnings);
}
{
if let Some(elem) = required_field!(tariff_elem, fields, "elements", warnings) {
let _drop: Option<()> = v2x::elements::lint(elem)
.gather_warnings_into(&mut warnings)
.deescalate_error_into(&mut warnings);
}
}
Report { warnings }
}
#[instrument(skip_all)]
fn lint_country_code(elem: &json::Element<'_>) -> Verdict<country::Code, Warning> {
let mut warnings = warning::Set::<Warning>::new();
let code_set = country::CodeSet::from_json(elem)?.gather_warnings_into(&mut warnings);
debug!("code_set: {code_set:?}");
let country_code = match code_set {
country::CodeSet::Alpha2(code) => code,
country::CodeSet::Alpha3(code) => {
warnings.insert(Warning::CpoCountryCodeShouldBeAlpha2, elem);
code
}
};
Ok(country_code.into_caveat(warnings))
}
type PartyId<'buf> = string::CiExactLen<'buf, 3>;
fn lint_party_id<'caller: 'buf, 'buf>(elem: &'caller json::Element<'buf>) -> Verdict<(), Warning> {
let party_id = PartyId::from_json(elem)?;
let (party_id, mut warnings) = party_id.into_parts();
if party_id.chars().any(char::is_lowercase) {
warnings.insert(string::Warning::PreferUppercase, elem);
}
Ok(().into_caveat(warnings.into_other()))
}
#[instrument(skip_all)]
fn lint_min_max_price(
min_price: Option<&json::Element<'_>>,
max_price: Option<&json::Element<'_>>,
) -> Verdict<(), Warning> {
let mut warnings = warning::Set::<Warning>::new();
if let Some((min_elem, max_elem)) = min_price.zip(max_price) {
let min = Price::from_json(min_elem)?.gather_warnings_into(&mut warnings);
let max = Price::from_json(max_elem)?.gather_warnings_into(&mut warnings);
if min > max {
warnings.insert(Warning::MinPriceIsGreaterThanMax, min_elem);
}
} else if let Some(elem) = min_price {
let _drop = Price::from_json(elem)?.gather_warnings_into(&mut warnings);
} else if let Some(elem) = max_price {
let _drop = Price::from_json(elem)?.gather_warnings_into(&mut warnings);
}
Ok(().into_caveat(warnings))
}