Skip to main content

dpp_domain/catalog/
descriptor.rs

1//! [`SectorDescriptor`] — a single sector's catalog entry.
2
3use serde::{Deserialize, Serialize};
4
5use super::regime::Regime;
6use super::status::RegulatoryStatus;
7
8/// A single sector's catalog entry — the canonical record every component
9/// (schema registry, plugin host, passport model) resolves against.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct SectorDescriptor {
13    /// Canonical sector key, e.g. `"battery"`, `"unsold-goods"`. Matches the
14    /// schema-registry sector key and the plugin's `meta().sector`.
15    pub key: String,
16    /// Human-readable title.
17    pub title: String,
18    /// Regulatory status — gates whether determinations are binding.
19    pub status: RegulatoryStatus,
20    /// Which EU legal instrument family this sector derives from.
21    ///
22    /// Orthogonal to [`Self::status`]: the regime says *which law*, the status
23    /// says *whether it binds yet*. Determination gating must never branch on
24    /// this field.
25    pub regime: Regime,
26    /// EU legal instrument(s) this sector derives from.
27    pub legal_basis: Vec<String>,
28    /// ISO-8601 date the **passport obligation** applies from, when known.
29    ///
30    /// Scope note: this is when the DPP itself becomes mandatory. It is **not**
31    /// the determination gate and must not be used as one — a regulation may
32    /// bind long before its passport is required (see [`RegulatoryStatus`]).
33    /// Where a sector carries several obligations with different dates, this
34    /// records the earliest that binds an in-scope operator; the manifest
35    /// `notes` carry the rest.
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub dpp_applies_from: Option<String>,
38    /// Minimum data retention in years required by the applicable act.
39    pub retention_years: u32,
40    /// Schema versions available for this sector (semver strings).
41    pub schema_versions: Vec<String>,
42    /// The schema version applicable to *new* passports in this sector right
43    /// now. Decouples "current" from "latest embedded" so a future schema can
44    /// ship embedded without becoming current until its act is in force. Must
45    /// be one of `schema_versions`.
46    pub current_schema_version: String,
47    /// Product categories *within* this sector — sub-types a plugin may branch
48    /// on, never dispatch keys. See `DATA-MODEL.md` §3.5.
49    #[serde(default)]
50    pub product_categories: Vec<String>,
51    /// Per-field [`Disclosure`](crate::domain::identity::Disclosure) class for
52    /// this sector's data: field name → class; unlisted fields default to
53    /// public. Not an ordering — a class names which audiences may see the
54    /// field, and the audiences do not nest. Universal conformity fields
55    /// (signatures, audit trails) are folded in by the access-policy engine, so
56    /// they are not repeated per sector here.
57    #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
58    pub disclosure: std::collections::HashMap<String, crate::domain::identity::Disclosure>,
59    /// Plugin that handles this sector (crate / filename stem, e.g.
60    /// `"sector-battery"`). `None` if no plugin is bound yet.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub plugin: Option<String>,
63    /// Free-text regulatory note (effective dates, scope, caveats).
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub notes: Option<String>,
66}