Skip to main content

minco_core/
provenance.rs

1use crate::PluginId;
2use serde::{Serialize, Serializer, ser::SerializeStruct};
3use std::fmt;
4
5/// Authoritative owner of a composition-time service or contribution registration.
6///
7/// Owners are created only by Minco's composition boundary. Application code can inspect an
8/// owner, but neither an application nor a plugin can construct a plugin-owned value and spoof a
9/// different plugin's identity.
10///
11/// ```compile_fail
12/// use minco_core::{PluginId, RegistrationOwner};
13///
14/// let forged = RegistrationOwner::plugin(PluginId::new("another-plugin")?);
15/// # Ok::<(), minco_core::IdentifierError>(())
16/// ```
17#[derive(Clone, PartialEq, Eq)]
18pub struct RegistrationOwner(RegistrationOwnerKind);
19
20#[derive(Clone, PartialEq, Eq)]
21enum RegistrationOwnerKind {
22    Application,
23    Plugin(PluginId),
24}
25
26impl RegistrationOwner {
27    pub(crate) const fn application() -> Self {
28        Self(RegistrationOwnerKind::Application)
29    }
30
31    pub(crate) const fn plugin(plugin_id: PluginId) -> Self {
32        Self(RegistrationOwnerKind::Plugin(plugin_id))
33    }
34
35    /// Returns `true` for services and contributions seeded by `compose_with`.
36    pub const fn is_application(&self) -> bool {
37        matches!(self.0, RegistrationOwnerKind::Application)
38    }
39
40    /// Returns the authoritative plugin ID, or `None` for application-seeded registrations.
41    pub const fn plugin_id(&self) -> Option<&PluginId> {
42        match &self.0 {
43            RegistrationOwnerKind::Application => None,
44            RegistrationOwnerKind::Plugin(plugin_id) => Some(plugin_id),
45        }
46    }
47}
48
49impl fmt::Display for RegistrationOwner {
50    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match &self.0 {
52            RegistrationOwnerKind::Application => formatter.write_str("application"),
53            RegistrationOwnerKind::Plugin(plugin_id) => write!(formatter, "plugin:{plugin_id}"),
54        }
55    }
56}
57
58impl fmt::Debug for RegistrationOwner {
59    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60        formatter
61            .debug_tuple("RegistrationOwner")
62            .field(&self.to_string())
63            .finish()
64    }
65}
66
67impl Serialize for RegistrationOwner {
68    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
69    where
70        S: Serializer,
71    {
72        match &self.0 {
73            RegistrationOwnerKind::Application => {
74                let mut state = serializer.serialize_struct("RegistrationOwner", 1)?;
75                state.serialize_field("kind", "application")?;
76                state.end()
77            }
78            RegistrationOwnerKind::Plugin(plugin_id) => {
79                let mut state = serializer.serialize_struct("RegistrationOwner", 2)?;
80                state.serialize_field("kind", "plugin")?;
81                state.serialize_field("plugin_id", plugin_id)?;
82                state.end()
83            }
84        }
85    }
86}
87
88/// Bounded singleton-service registration metadata retained after composition.
89#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
90pub struct ServiceRegistration {
91    pub rust_type: &'static str,
92    pub owner: RegistrationOwner,
93}
94
95/// One ordered registration within a contribution type.
96#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
97pub struct ContributionRegistration {
98    pub owner: RegistrationOwner,
99    pub installation_index: usize,
100}
101
102/// Bounded contribution metadata grouped by Rust type.
103#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
104pub struct ContributionTypeRegistration {
105    pub rust_type: &'static str,
106    pub registrations: Vec<ContributionRegistration>,
107}
108
109/// Complete metadata-only registration provenance for a composed application.
110#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
111pub struct RegistrationProvenance {
112    pub services: Vec<ServiceRegistration>,
113    pub contributions: Vec<ContributionTypeRegistration>,
114}