Skip to main content

dpp_plugin_traits/
plugin.rs

1//! [`DppSectorPlugin`] — the trait every sector plugin implements.
2
3use crate::error::PluginError;
4use crate::meta::{PluginCapabilities, PluginCapability, PluginMeta};
5use crate::result::PluginResult;
6use crate::version::{AbiVersion, SchemaVersionRange};
7
8/// Raw JSON input passed from the host to a plugin entry point.
9pub type PluginInput = serde_json::Value;
10
11/// The identity fields that genuinely vary per plugin — everything else in
12/// [`PluginMeta`] (`license`, `author`, `homepage`) is a fixed Odal Node
13/// convention supplied by the default [`DppSectorPlugin::meta`].
14pub struct PluginIdentity {
15    /// Sector key this plugin handles, e.g. `"textile"`, `"steel"`, `"battery"`.
16    pub sector: &'static str,
17    /// Human-readable plugin name.
18    pub name: &'static str,
19    /// SemVer version string of the plugin itself, typically
20    /// `env!("CARGO_PKG_VERSION")`.
21    pub version: &'static str,
22    /// Brief description of what this plugin does.
23    pub description: &'static str,
24}
25
26/// The entry points every sector plugin must export.
27///
28/// The Wasm host calls these after deserialising JSON sector data from the
29/// passport payload. Implementations must be deterministic and free of I/O.
30pub trait DppSectorPlugin: Send + Sync {
31    /// The fields that distinguish this plugin's identity (see [`PluginIdentity`]).
32    fn plugin_identity(&self) -> PluginIdentity;
33
34    /// The sector schema version range this plugin supports.
35    fn schema_version_range(&self) -> SchemaVersionRange;
36
37    /// Returns static metadata about this plugin.
38    ///
39    /// Built from [`Self::plugin_identity`] plus the fixed Odal Node
40    /// `license`/`author`/`homepage` convention. Override directly if a
41    /// plugin genuinely needs different values.
42    fn meta(&self) -> PluginMeta {
43        let id = self.plugin_identity();
44        PluginMeta {
45            sector: id.sector.to_owned(),
46            name: id.name.to_owned(),
47            version: id.version.to_owned(),
48            license: "Apache-2.0".to_owned(),
49            description: Some(id.description.to_owned()),
50            author: Some("Odal Node".to_owned()),
51            homepage: Some("https://github.com/odal-node/dpp-core".to_owned()),
52        }
53    }
54
55    /// Returns the plugin's capability declaration for version negotiation.
56    ///
57    /// Built from [`Self::schema_version_range`] plus the standard
58    /// `Validate`/`ComputeMetrics`/`GeneratePassport` capability set every
59    /// plugin declares today. Override directly if a plugin needs a
60    /// different capability set or negotiation limits.
61    fn capabilities(&self) -> PluginCapabilities {
62        PluginCapabilities {
63            abi_version: AbiVersion::current(),
64            supported_schemas: vec![self.schema_version_range()],
65            capabilities: vec![
66                PluginCapability::Validate,
67                PluginCapability::ComputeMetrics,
68                PluginCapability::GeneratePassport,
69            ],
70            min_host_version: None,
71            max_fuel: None,
72            max_memory_bytes: None,
73        }
74    }
75
76    /// Validate the structure and field constraints of the sector input.
77    ///
78    /// Returns `Ok(())` if the input is structurally valid, or a descriptive
79    /// error if a required field is missing or out of range. Prefer
80    /// `PluginError::ValidationErrors` with per-field detail over
81    /// `PluginError::InvalidInput` for better error reporting.
82    fn validate_input(&self, input: &PluginInput) -> Result<(), PluginError>;
83
84    /// Compute compliance metrics from the sector input.
85    ///
86    /// May return `None` for fields that do not apply to this sector.
87    fn calculate_metrics(&self, input: &PluginInput) -> Result<PluginResult, PluginError>;
88
89    /// Generate a passport-ready sector data JSON payload.
90    ///
91    /// Applies any normalisation or enrichment required by the sector schema
92    /// (e.g. rounding, unit conversion). The output is stored verbatim in the DPP.
93    /// Takes `input` by value — a pass-through implementation returns it
94    /// directly instead of cloning.
95    fn generate_passport(&self, input: PluginInput) -> Result<serde_json::Value, PluginError>;
96}