Skip to main content

boxferry_podman/
source.rs

1//! Explicit already-acquired Podman source boundary.
2
3use boxferry_model::Identifier;
4use podman_lens::{CapabilityCatalogueEntry, ResourceGraph, ResourceInventory};
5
6/// Explicit authorization for promoting effective runtime observations into desired intent.
7///
8/// The conservative default promotes nothing. Runtime-assigned and local-resolution values are
9/// never promotable through this policy.
10#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
11pub struct PodmanPromotionPolicy {
12    enabled: u8,
13}
14
15const EFFECTIVE_BIND_MOUNTS: u8 = 1 << 0;
16const EFFECTIVE_NAMED_VOLUME_MOUNTS: u8 = 1 << 1;
17const EFFECTIVE_NAMED_NETWORKS: u8 = 1 << 2;
18const PORTABLE_EFFECTIVE_SETTINGS: u8 = 1 << 3;
19
20impl PodmanPromotionPolicy {
21    /// Creates the conservative policy that retains effective values as observations only.
22    #[must_use]
23    pub const fn conservative() -> Self {
24        Self { enabled: 0 }
25    }
26
27    /// Reconstructs the reviewed portable application subset from effective observations.
28    ///
29    /// This does not authorize host bind paths, runtime-assigned values, sensitive environment
30    /// values, unsupported native fields, or a non-exact target conversion. The CLI uses this
31    /// policy for ordinary local application imports; the library default stays conservative.
32    #[must_use]
33    pub const fn portable_application() -> Self {
34        Self {
35            enabled: EFFECTIVE_NAMED_VOLUME_MOUNTS | EFFECTIVE_NAMED_NETWORKS | PORTABLE_EFFECTIVE_SETTINGS,
36        }
37    }
38
39    /// Authorizes or rejects host-local bind-mount promotion.
40    #[must_use]
41    pub const fn with_effective_bind_mounts(mut self, enabled: bool) -> Self {
42        self.set(EFFECTIVE_BIND_MOUNTS, enabled);
43        self
44    }
45
46    /// Authorizes or rejects portable named-volume mount promotion.
47    #[must_use]
48    pub const fn with_effective_named_volume_mounts(mut self, enabled: bool) -> Self {
49        self.set(EFFECTIVE_NAMED_VOLUME_MOUNTS, enabled);
50        self
51    }
52
53    /// Authorizes or rejects named-network attachment promotion.
54    #[must_use]
55    pub const fn with_effective_named_networks(mut self, enabled: bool) -> Self {
56        self.set(EFFECTIVE_NAMED_NETWORKS, enabled);
57        self
58    }
59
60    /// Authorizes or rejects the reviewed portable effective-settings subset.
61    ///
62    /// This does not authorize environment-value acquisition. An embedding caller must select
63    /// `PodmanLens` acquisition options separately; redacted entries remain named prerequisites.
64    #[must_use]
65    pub const fn with_portable_effective_settings(mut self, enabled: bool) -> Self {
66        self.set(PORTABLE_EFFECTIVE_SETTINGS, enabled);
67        self
68    }
69
70    /// Returns whether portable effective named-volume mounts may be promoted.
71    #[must_use]
72    pub const fn promotes_effective_named_volume_mounts(self) -> bool {
73        self.contains(EFFECTIVE_NAMED_VOLUME_MOUNTS)
74    }
75
76    /// Returns whether effective host-local bind mounts may be promoted.
77    #[must_use]
78    pub const fn promotes_effective_bind_mounts(self) -> bool {
79        self.contains(EFFECTIVE_BIND_MOUNTS)
80    }
81
82    /// Returns whether effective named-network attachments may be promoted.
83    #[must_use]
84    pub const fn promotes_effective_named_networks(self) -> bool {
85        self.contains(EFFECTIVE_NAMED_NETWORKS)
86    }
87
88    /// Returns whether reviewed portable effective settings may be promoted.
89    #[must_use]
90    pub const fn promotes_portable_effective_settings(self) -> bool {
91        self.contains(PORTABLE_EFFECTIVE_SETTINGS)
92    }
93
94    const fn set(&mut self, flag: u8, value: bool) {
95        if value {
96            self.enabled |= flag;
97        } else {
98            self.enabled &= !flag;
99        }
100    }
101
102    const fn contains(self, flag: u8) -> bool {
103        self.enabled & flag != 0
104    }
105}
106/// One explicit Podman import source after caller-owned acquisition and discovery.
107#[derive(Clone, Debug, Eq, PartialEq)]
108pub struct PodmanSource {
109    application_name: Identifier,
110    inventory: ResourceInventory,
111    graph: ResourceGraph,
112    promotion_policy: PodmanPromotionPolicy,
113}
114
115impl PodmanSource {
116    /// Creates a source without reading files, environment variables, connections, or runtimes.
117    #[must_use]
118    pub const fn new(application_name: Identifier, inventory: ResourceInventory, graph: ResourceGraph) -> Self {
119        Self {
120            application_name,
121            inventory,
122            graph,
123            promotion_policy: PodmanPromotionPolicy::conservative(),
124        }
125    }
126
127    /// Returns the caller-selected neutral application name.
128    #[must_use]
129    pub const fn application_name(&self) -> &Identifier {
130        &self.application_name
131    }
132
133    /// Returns the acquired Podman inventory.
134    #[must_use]
135    pub const fn inventory(&self) -> &ResourceInventory {
136        &self.inventory
137    }
138
139    /// Returns the caller-selected discovery graph.
140    #[must_use]
141    pub const fn graph(&self) -> &ResourceGraph {
142        &self.graph
143    }
144
145    /// Returns the exact Podman Engine version observed before acquisition started.
146    ///
147    /// This is source evidence only. In particular, an input-only legacy runtime is never
148    /// treated as a Podman deployment target; exporters still require a separate explicit
149    /// [`crate::ResolvedPodmanTarget`].
150    #[must_use]
151    pub fn observed_engine_version(&self) -> &str {
152        self.inventory.service().engine_version().original()
153    }
154
155    /// Returns the exact Libpod API version used for acquisition.
156    ///
157    /// This is source evidence only and must not be inferred as an output target.
158    #[must_use]
159    pub fn observed_api_version(&self) -> &str {
160        self.inventory.service().api_version().original()
161    }
162
163    /// Returns the immutable input capability evidence for the observed service.
164    ///
165    /// The returned entry may be input-only. Callers must select a separate explicit target
166    /// profile before planning Podman output.
167    #[must_use]
168    pub fn input_capability(&self) -> &CapabilityCatalogueEntry {
169        self.inventory.service().input_capability()
170    }
171
172    /// Returns an always-redacted, serialization-only inventory snapshot.
173    ///
174    /// The snapshot omits environment values, secrets, connection details, label values and raw
175    /// unknown JSON. It is diagnostic evidence, not a supported Podman input format.
176    #[must_use]
177    pub fn redacted_inventory_snapshot(&self) -> podman_lens::snapshot::v1::InventorySnapshot {
178        podman_lens::snapshot::v1::inventory(&self.inventory)
179    }
180
181    /// Returns an always-redacted, serialization-only discovery-graph snapshot.
182    ///
183    /// The snapshot is intended for an opt-in support bundle and cannot be used to recreate a
184    /// live inventory or select an output target.
185    #[must_use]
186    pub fn redacted_graph_snapshot(&self) -> podman_lens::snapshot::v1::GraphSnapshot {
187        podman_lens::snapshot::v1::graph(&self.graph)
188    }
189
190    /// Replaces the conservative effective-observation promotion policy.
191    #[must_use]
192    pub const fn with_promotion_policy(mut self, policy: PodmanPromotionPolicy) -> Self {
193        self.promotion_policy = policy;
194        self
195    }
196
197    /// Returns the explicit effective-observation promotion policy.
198    #[must_use]
199    pub const fn promotion_policy(&self) -> PodmanPromotionPolicy {
200        self.promotion_policy
201    }
202}