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
66/// Serialize `data` to a JSON object and strip any top-level field whose
67/// required access tier exceeds `viewer_tier`.
68///
69/// `descriptor.access_tiers` maps camelCase JSON field names to the minimum
70/// [`crate::domain::identity::AccessTier`] a viewer must hold to see that field.
71/// Fields not listed in the map are always retained (default: Public).
72///
73/// Returns a `serde_json::Value::Object` with redacted fields removed.
74/// Returns `serde_json::Value::Null` if serialization fails.
75pub fn redact_sector_data(
76    data: &SectorData,
77    viewer_tier: crate::domain::identity::AccessTier,
78    descriptor: &crate::catalog::SectorDescriptor,
79) -> serde_json::Value {
80    let mut value = match serde_json::to_value(data) {
81        Ok(v) => v,
82        Err(_) => return serde_json::Value::Null,
83    };
84    if let Some(obj) = value.as_object_mut() {
85        obj.retain(|key, _| match descriptor.access_tiers.get(key) {
86            Some(&required) => viewer_tier >= required,
87            None => true,
88        });
89    }
90    value
91}