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::mattress::MattressData;
use super::steel::SteelData;
use super::textile::TextileData;
use super::toy::ToyData;
use super::tyre::TyreData;
use super::unsold_goods::UnsoldGoodsReport;
use crate::product_group::ProductGroup;
use crate::product_group::payload::ProductGroupPayload;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum ProductGroupData {
Battery(Box<BatteryData>),
Textile(Box<TextileData>),
UnsoldGoods(UnsoldGoodsReport),
Steel(SteelData),
Electronics(ElectronicsData),
Construction(ConstructionData),
Tyre(TyreData),
Toy(ToyData),
Aluminium(AluminiumData),
Furniture(FurnitureData),
Mattress(MattressData),
Detergent(DetergentData),
Other {
product_group: String,
data: serde_json::Value,
},
}
impl Serialize for ProductGroupData {
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::Mattress(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.product_group();
if let serde_json::Value::Object(ref mut map) = value {
map.insert(
"productGroup".to_owned(),
serde_json::Value::String(tag.wire_str().to_owned()),
);
}
value.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ProductGroupData {
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("productGroup")
.and_then(serde_json::Value::as_str)
.ok_or_else(|| D::Error::missing_field("productGroup"))?
.to_owned();
macro_rules! typed {
($variant:ident) => {
serde_json::from_value(value.clone())
.map(Self::$variant)
.map_err(D::Error::custom)
};
}
match ProductGroup::from_wire_tag(&tag) {
ProductGroup::Battery => typed!(Battery),
ProductGroup::Textile => typed!(Textile),
ProductGroup::UnsoldGoods => typed!(UnsoldGoods),
ProductGroup::Steel => typed!(Steel),
ProductGroup::Electronics => typed!(Electronics),
ProductGroup::Construction => typed!(Construction),
ProductGroup::Tyre => typed!(Tyre),
ProductGroup::Toy => typed!(Toy),
ProductGroup::Aluminium => typed!(Aluminium),
ProductGroup::Furniture => typed!(Furniture),
ProductGroup::Mattress => typed!(Mattress),
ProductGroup::Detergent => typed!(Detergent),
ProductGroup::Other(product_group) => Ok(Self::Other {
product_group,
data: value,
}),
}
}
}
impl ProductGroupData {
#[must_use]
pub fn other(mut data: serde_json::Value) -> Option<Self> {
let map = data.as_object_mut()?;
let product_group = map
.get("productGroup")
.and_then(serde_json::Value::as_str)
.unwrap_or("other")
.to_owned();
map.entry("productGroup")
.or_insert_with(|| serde_json::Value::String(product_group.clone()));
match ProductGroup::from_wire_tag(&product_group) {
ProductGroup::Other(_) => Some(Self::Other {
product_group,
data,
}),
_ => None,
}
}
pub fn product_group(&self) -> ProductGroup {
match self {
ProductGroupData::Battery(_) => ProductGroup::Battery,
ProductGroupData::Textile(_) => ProductGroup::Textile,
ProductGroupData::UnsoldGoods(_) => ProductGroup::UnsoldGoods,
ProductGroupData::Steel(_) => ProductGroup::Steel,
ProductGroupData::Electronics(_) => ProductGroup::Electronics,
ProductGroupData::Construction(_) => ProductGroup::Construction,
ProductGroupData::Tyre(_) => ProductGroup::Tyre,
ProductGroupData::Toy(_) => ProductGroup::Toy,
ProductGroupData::Aluminium(_) => ProductGroup::Aluminium,
ProductGroupData::Furniture(_) => ProductGroup::Furniture,
ProductGroupData::Mattress(_) => ProductGroup::Mattress,
ProductGroupData::Detergent(_) => ProductGroup::Detergent,
ProductGroupData::Other { product_group, .. } => {
ProductGroup::Other(product_group.clone())
}
}
}
#[must_use]
pub fn model_identifier(&self) -> Option<&str> {
self.payload()?.model_identifier()
}
fn payload(&self) -> Option<&dyn ProductGroupPayload> {
match self {
Self::Battery(d) => Some(&**d),
Self::Textile(d) => Some(&**d),
Self::UnsoldGoods(d) => Some(d),
Self::Steel(d) => Some(d),
Self::Electronics(d) => Some(d),
Self::Construction(d) => Some(d),
Self::Tyre(d) => Some(d),
Self::Toy(d) => Some(d),
Self::Aluminium(d) => Some(d),
Self::Furniture(d) => Some(d),
Self::Mattress(d) => Some(d),
Self::Detergent(d) => Some(d),
Self::Other { .. } => None,
}
}
pub fn gtin(&self) -> Option<&str> {
self.payload()?.gtin()
}
pub fn svhc_substances(&self) -> Option<&[super::common::SvhcSubstance]> {
self.payload()?.svhc_substances()
}
#[must_use]
pub fn product_category(&self) -> Option<&str> {
self.payload()?.product_category()
}
}