Skip to main content

platform_module/
module.rs

1//! A loaded module: serializable manifest + behavior binding + internal config.
2
3use crate::admin_data::{AdminActionSource, AdminDataSource};
4use crate::binding::ModuleBinding;
5use crate::linked::{LinkedBinding, LinkedHttpContribution};
6use lenso_contracts::{ModuleManifest, ModuleSource};
7use platform_core::{RuntimeConfigDescriptor, RuntimeConfigGroupDescriptor};
8use std::sync::Arc;
9
10/// One loaded module. `manifest` is serializable data; `binding` is behavior;
11/// `runtime_config` is internal `&'static` config NOT in the manifest (the
12/// registry needs the real `RuntimeConfigType` enum to validate). Cross-source
13/// config wire form is deferred to a later spec.
14#[derive(Debug)]
15pub struct Module {
16    pub manifest: ModuleManifest,
17    pub binding: Arc<dyn ModuleBinding>,
18    pub source: ModuleSource,
19    pub load_status: ModuleLoadStatus,
20    pub linked_http: Option<LinkedHttpContribution>,
21    pub runtime_config: &'static [RuntimeConfigDescriptor],
22    pub runtime_config_groups: &'static [RuntimeConfigGroupDescriptor],
23    /// Optional schema-admin data source. `None` for modules without an admin
24    /// surface (e.g. notifications). Set via [`Module::with_admin_data`].
25    pub admin_data: Option<Arc<dyn AdminDataSource>>,
26    /// Optional executable admin actions. Set via [`Module::with_admin_actions`].
27    pub admin_actions: Option<Arc<dyn AdminActionSource>>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum ModuleLoadStatus {
32    Loaded,
33    Error { message: String },
34}
35
36impl Module {
37    /// Build a compile-time (Linked) module from a manifest + linked behavior.
38    /// Config defaults to empty; attach it with [`Module::with_runtime_config`].
39    #[must_use]
40    pub fn linked(manifest: ModuleManifest, binding: LinkedBinding) -> Self {
41        let linked_http = binding.http;
42        Self {
43            manifest,
44            binding: Arc::new(binding),
45            source: ModuleSource::Linked,
46            load_status: ModuleLoadStatus::Loaded,
47            linked_http,
48            runtime_config: &[],
49            runtime_config_groups: &[],
50            admin_data: None,
51            admin_actions: None,
52        }
53    }
54
55    /// Build a remote module from a manifest + transport-backed behavior.
56    /// Remote behavior is intentionally narrow in the first slice.
57    #[must_use]
58    pub fn remote(manifest: ModuleManifest, binding: Arc<dyn ModuleBinding>) -> Self {
59        Self {
60            manifest,
61            binding,
62            source: ModuleSource::Remote,
63            load_status: ModuleLoadStatus::Loaded,
64            linked_http: None,
65            runtime_config: &[],
66            runtime_config_groups: &[],
67            admin_data: None,
68            admin_actions: None,
69        }
70    }
71
72    /// Attach the module's editable configuration descriptors.
73    #[must_use]
74    pub fn with_runtime_config(
75        mut self,
76        runtime_config: &'static [RuntimeConfigDescriptor],
77    ) -> Self {
78        self.runtime_config = runtime_config;
79        self
80    }
81
82    /// Attach presentation groups for the module's editable configuration.
83    #[must_use]
84    pub fn with_runtime_config_groups(
85        mut self,
86        runtime_config_groups: &'static [RuntimeConfigGroupDescriptor],
87    ) -> Self {
88        self.runtime_config_groups = runtime_config_groups;
89        self
90    }
91
92    /// Attach a schema-admin data source (read access to admin entities).
93    #[must_use]
94    pub fn with_admin_data(mut self, data: Arc<dyn AdminDataSource>) -> Self {
95        self.admin_data = Some(data);
96        self
97    }
98
99    /// Attach admin action behavior for manifest-declared actions.
100    #[must_use]
101    pub fn with_admin_actions(mut self, actions: Arc<dyn AdminActionSource>) -> Self {
102        self.admin_actions = Some(actions);
103        self
104    }
105}