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}