Skip to main content

ralph/contracts/config/
plugin.rs

1//! Plugin configuration for enable/disable and per-plugin settings.
2//!
3//! Responsibilities:
4//! - Define plugin config structs and merge behavior.
5//!
6//! Not handled here:
7//! - Plugin loading and execution (see `crate::plugin` module).
8
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12
13/// Plugin configuration container.
14#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
15#[serde(default, deny_unknown_fields)]
16pub struct PluginsConfig {
17    /// Per-plugin configuration keyed by plugin id.
18    pub plugins: BTreeMap<String, PluginConfig>,
19}
20
21impl PluginsConfig {
22    pub fn merge_from(&mut self, other: Self) {
23        for (id, patch) in other.plugins {
24            self.plugins
25                .entry(id)
26                .and_modify(|existing| existing.merge_from(patch.clone()))
27                .or_insert(patch);
28        }
29    }
30}
31
32/// Per-plugin configuration.
33#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
34#[serde(default, deny_unknown_fields)]
35pub struct PluginConfig {
36    /// Enable/disable the plugin. If None, defaults to disabled.
37    pub enabled: Option<bool>,
38
39    /// Optional overrides for runner executable path/name for this plugin's runner.
40    /// If not set, manifest runner.bin is used.
41    pub runner: Option<PluginRunnerConfig>,
42
43    /// Optional overrides for processor executable path/name for this plugin's task processors.
44    pub processor: Option<PluginProcessorConfig>,
45
46    /// Opaque plugin configuration blob (passed through to the plugin).
47    pub config: Option<serde_json::Value>,
48}
49
50impl PluginConfig {
51    pub fn merge_from(&mut self, other: Self) {
52        if other.enabled.is_some() {
53            self.enabled = other.enabled;
54        }
55        if let Some(r) = other.runner {
56            match &mut self.runner {
57                Some(existing) => existing.merge_from(r),
58                None => self.runner = Some(r),
59            }
60        }
61        if let Some(p) = other.processor {
62            match &mut self.processor {
63                Some(existing) => existing.merge_from(p),
64                None => self.processor = Some(p),
65            }
66        }
67        if other.config.is_some() {
68            self.config = other.config;
69        }
70    }
71}
72
73/// Plugin runner executable configuration.
74#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
75#[serde(default, deny_unknown_fields)]
76pub struct PluginRunnerConfig {
77    pub bin: Option<String>,
78}
79
80impl PluginRunnerConfig {
81    pub fn merge_from(&mut self, other: Self) {
82        if other.bin.is_some() {
83            self.bin = other.bin;
84        }
85    }
86}
87
88/// Plugin processor executable configuration.
89#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
90#[serde(default, deny_unknown_fields)]
91pub struct PluginProcessorConfig {
92    pub bin: Option<String>,
93}
94
95impl PluginProcessorConfig {
96    pub fn merge_from(&mut self, other: Self) {
97        if other.bin.is_some() {
98            self.bin = other.bin;
99        }
100    }
101}