Skip to main content

dpp_domain/domain/sector/data/
sector_data.rs

1//! The [`SectorData`] discriminated union and its access-tier redaction.
2
3use serde::{Deserialize, Serialize};
4
5use super::aluminium::AluminiumData;
6use super::battery::BatteryData;
7use super::construction::ConstructionData;
8use super::detergent::DetergentData;
9use super::electronics::ElectronicsData;
10use super::furniture::FurnitureData;
11use super::steel::SteelData;
12use super::textile::TextileData;
13use super::toy::ToyData;
14use super::tyre::TyreData;
15use super::unsold_goods::UnsoldGoodsReport;
16use crate::domain::sector::Sector;
17
18/// Typed, sector-specific DPP data — replaces the opaque `compliance_data: Value`.
19///
20/// Serialises as an internally-tagged object where `"sector"` is the
21/// discriminant field, e.g.:
22/// ```json
23/// { "sector": "battery", "gtin": "09506000134352", "nominalVoltageV": 3.2, ... }
24/// ```
25/// ```json
26/// { "sector": "textile", "fibreComposition": [...], "countryOfManufacturing": "BD" }
27/// ```
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
29#[serde(tag = "sector", rename_all = "camelCase")]
30#[non_exhaustive]
31pub enum SectorData {
32    Battery(BatteryData),
33    Textile(TextileData),
34    UnsoldGoods(UnsoldGoodsReport),
35    Steel(SteelData),
36    Electronics(ElectronicsData),
37    Construction(ConstructionData),
38    Tyre(TyreData),
39    Toy(ToyData),
40    Aluminium(AluminiumData),
41    Furniture(FurnitureData),
42    Detergent(DetergentData),
43    Other(serde_json::Value),
44}
45
46impl SectorData {
47    /// Returns the `Sector` discriminant for this data.
48    pub fn sector(&self) -> Sector {
49        match self {
50            SectorData::Battery(_) => Sector::Battery,
51            SectorData::Textile(_) => Sector::Textile,
52            SectorData::UnsoldGoods(_) => Sector::UnsoldGoods,
53            SectorData::Steel(_) => Sector::Steel,
54            SectorData::Electronics(_) => Sector::Electronics,
55            SectorData::Construction(_) => Sector::Construction,
56            SectorData::Tyre(_) => Sector::Tyre,
57            SectorData::Toy(_) => Sector::Toy,
58            SectorData::Aluminium(_) => Sector::Aluminium,
59            SectorData::Furniture(_) => Sector::Furniture,
60            SectorData::Detergent(_) => Sector::Detergent,
61            SectorData::Other(_) => Sector::Other,
62        }
63    }
64
65    /// The GTIN carried by this sector's typed data, if any.
66    ///
67    /// `UnsoldGoods` and `Other` carry no GTIN field — a discard-event report
68    /// and an untyped catch-all respectively, neither of which identifies a
69    /// trade item the way every other sector does.
70    pub fn gtin(&self) -> Option<&str> {
71        match self {
72            SectorData::Battery(d) => Some(d.gtin.as_str()),
73            SectorData::Textile(d) => Some(d.gtin.as_str()),
74            SectorData::Steel(d) => Some(d.gtin.as_str()),
75            SectorData::Electronics(d) => Some(d.gtin.as_str()),
76            SectorData::Construction(d) => Some(d.gtin.as_str()),
77            SectorData::Tyre(d) => Some(d.gtin.as_str()),
78            SectorData::Toy(d) => Some(d.gtin.as_str()),
79            SectorData::Aluminium(d) => Some(d.gtin.as_str()),
80            SectorData::Furniture(d) => Some(d.gtin.as_str()),
81            SectorData::Detergent(d) => Some(d.gtin.as_str()),
82            SectorData::UnsoldGoods(_) | SectorData::Other(_) => None,
83        }
84    }
85}
86
87/// Serialize `data` to a JSON object and strip any top-level field whose
88/// required access tier exceeds `viewer_tier`.
89///
90/// `descriptor.access_tiers` maps camelCase JSON field names to the minimum
91/// [`crate::domain::identity::AccessTier`] a viewer must hold to see that field.
92/// Fields not listed in the map are always retained (default: Public).
93///
94/// Returns a `serde_json::Value::Object` with redacted fields removed.
95/// Returns `serde_json::Value::Null` if serialization fails.
96pub fn redact_sector_data(
97    data: &SectorData,
98    viewer_tier: crate::domain::identity::AccessTier,
99    descriptor: &crate::catalog::SectorDescriptor,
100) -> serde_json::Value {
101    let mut value = match serde_json::to_value(data) {
102        Ok(v) => v,
103        Err(_) => return serde_json::Value::Null,
104    };
105    if let Some(obj) = value.as_object_mut() {
106        obj.retain(|key, _| match descriptor.access_tiers.get(key) {
107            Some(&required) => viewer_tier >= required,
108            None => true,
109        });
110    }
111    value
112}