use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::aluminium::AluminiumData;
use super::battery::BatteryData;
use super::construction::ConstructionData;
use super::detergent::DetergentData;
use super::electronics::ElectronicsData;
use super::furniture::FurnitureData;
use super::steel::SteelData;
use super::textile::TextileData;
use super::toy::ToyData;
use super::tyre::TyreData;
use super::unsold_goods::UnsoldGoodsReport;
use crate::domain::sector::Sector;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum SectorData {
Battery(BatteryData),
Textile(TextileData),
UnsoldGoods(UnsoldGoodsReport),
Steel(SteelData),
Electronics(ElectronicsData),
Construction(ConstructionData),
Tyre(TyreData),
Toy(ToyData),
Aluminium(AluminiumData),
Furniture(FurnitureData),
Detergent(DetergentData),
Other {
sector: String,
data: serde_json::Value,
},
}
impl Serialize for SectorData {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut value = match self {
Self::Battery(d) => serde_json::to_value(d),
Self::Textile(d) => serde_json::to_value(d),
Self::UnsoldGoods(d) => serde_json::to_value(d),
Self::Steel(d) => serde_json::to_value(d),
Self::Electronics(d) => serde_json::to_value(d),
Self::Construction(d) => serde_json::to_value(d),
Self::Tyre(d) => serde_json::to_value(d),
Self::Toy(d) => serde_json::to_value(d),
Self::Aluminium(d) => serde_json::to_value(d),
Self::Furniture(d) => serde_json::to_value(d),
Self::Detergent(d) => serde_json::to_value(d),
Self::Other { data, .. } => Ok(data.clone()),
}
.map_err(serde::ser::Error::custom)?;
let tag = self.sector();
if let serde_json::Value::Object(ref mut map) = value {
map.insert(
"sector".to_owned(),
serde_json::Value::String(tag.wire_str().to_owned()),
);
}
value.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for SectorData {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error as _;
let value = serde_json::Value::deserialize(deserializer)?;
let tag = value
.get("sector")
.and_then(serde_json::Value::as_str)
.ok_or_else(|| D::Error::missing_field("sector"))?
.to_owned();
macro_rules! typed {
($variant:ident) => {
serde_json::from_value(value.clone())
.map(Self::$variant)
.map_err(D::Error::custom)
};
}
match Sector::from_wire_tag(&tag) {
Sector::Battery => typed!(Battery),
Sector::Textile => typed!(Textile),
Sector::UnsoldGoods => typed!(UnsoldGoods),
Sector::Steel => typed!(Steel),
Sector::Electronics => typed!(Electronics),
Sector::Construction => typed!(Construction),
Sector::Tyre => typed!(Tyre),
Sector::Toy => typed!(Toy),
Sector::Aluminium => typed!(Aluminium),
Sector::Furniture => typed!(Furniture),
Sector::Detergent => typed!(Detergent),
Sector::Other(sector) => Ok(Self::Other {
sector,
data: value,
}),
}
}
}
impl SectorData {
#[must_use]
pub fn other(data: serde_json::Value) -> Option<Self> {
let sector = data
.get("sector")
.and_then(serde_json::Value::as_str)
.unwrap_or("other")
.to_owned();
match Sector::from_wire_tag(§or) {
Sector::Other(_) => Some(Self::Other { sector, data }),
_ => None,
}
}
pub fn sector(&self) -> Sector {
match self {
SectorData::Battery(_) => Sector::Battery,
SectorData::Textile(_) => Sector::Textile,
SectorData::UnsoldGoods(_) => Sector::UnsoldGoods,
SectorData::Steel(_) => Sector::Steel,
SectorData::Electronics(_) => Sector::Electronics,
SectorData::Construction(_) => Sector::Construction,
SectorData::Tyre(_) => Sector::Tyre,
SectorData::Toy(_) => Sector::Toy,
SectorData::Aluminium(_) => Sector::Aluminium,
SectorData::Furniture(_) => Sector::Furniture,
SectorData::Detergent(_) => Sector::Detergent,
SectorData::Other { sector, .. } => Sector::Other(sector.clone()),
}
}
pub fn gtin(&self) -> Option<&str> {
match self {
SectorData::Battery(d) => Some(d.gtin.as_str()),
SectorData::Textile(d) => Some(d.gtin.as_str()),
SectorData::Steel(d) => Some(d.gtin.as_str()),
SectorData::Electronics(d) => Some(d.gtin.as_str()),
SectorData::Construction(d) => Some(d.gtin.as_str()),
SectorData::Tyre(d) => Some(d.gtin.as_str()),
SectorData::Toy(d) => Some(d.gtin.as_str()),
SectorData::Aluminium(d) => Some(d.gtin.as_str()),
SectorData::Furniture(d) => Some(d.gtin.as_str()),
SectorData::Detergent(d) => Some(d.gtin.as_str()),
SectorData::UnsoldGoods(_) | SectorData::Other { .. } => None,
}
}
}
pub fn redact_sector_data(
data: &SectorData,
audience: crate::domain::identity::Audience,
descriptor: &crate::catalog::SectorDescriptor,
) -> serde_json::Value {
let mut value = match serde_json::to_value(data) {
Ok(v) => v,
Err(_) => return serde_json::Value::Null,
};
if let Some(obj) = value.as_object_mut() {
obj.retain(|key, _| match descriptor.disclosure.get(key) {
Some(&class) => audience.may_see(class),
None => true,
});
}
value
}