phoxal_bundle/reader.rs
1//! The one bundle reader, used by the supervisor and by every participant.
2
3use std::path::Path;
4
5use phoxal_model::identity::RobotId;
6use phoxal_model::manifest::ManifestDocument;
7use phoxal_model::{AssetId, Robot};
8
9use crate::{BundleError, BundleRoot, ParticipantAssets, read_manifest_document};
10
11/// An opened bundle: its manifest, and access to its assets.
12///
13/// Opening one parses `manifest.json` and does nothing else. A participant not
14/// named in the manifest opens the bundle exactly as one that is - the manifest
15/// is the robot model plus, for those that have one, their own configuration -
16/// so there is no selection step and no way for a launched process to be refused
17/// by the bundle it was pointed at.
18#[derive(Clone, Debug)]
19pub struct RuntimeBundle {
20 root: BundleRoot,
21 manifest: ManifestDocument,
22 assets: ParticipantAssets,
23}
24
25impl RuntimeBundle {
26 /// Open one installed bundle.
27 ///
28 /// # Errors
29 ///
30 /// Returns [`BundleError::Root`] or [`BundleError::NotDirectory`] when
31 /// `root` is not a directory, [`BundleError::ReadManifest`] when
32 /// `manifest.json` cannot be read, and [`BundleError::ManifestJson`] when it
33 /// is not a document this train understands.
34 pub fn open(root: impl AsRef<Path>) -> Result<Self, BundleError> {
35 let root = BundleRoot::open(root.as_ref())?;
36 let manifest = read_manifest_document(&root)?;
37 Ok(Self {
38 assets: ParticipantAssets::new(root.clone()),
39 root,
40 manifest,
41 })
42 }
43
44 /// The bundle root path, retained for diagnostics and for launching.
45 #[must_use]
46 pub fn root(&self) -> &Path {
47 self.root.path()
48 }
49
50 /// The persisted document, tag included.
51 #[must_use]
52 pub const fn manifest(&self) -> &ManifestDocument {
53 &self.manifest
54 }
55
56 /// The compiled robot the manifest carries.
57 #[must_use]
58 pub const fn robot(&self) -> &Robot {
59 self.manifest.robot()
60 }
61
62 /// The sole persisted robot identity.
63 #[must_use]
64 pub const fn robot_id(&self) -> &RobotId {
65 self.robot().id()
66 }
67
68 /// Read one asset out of `<bundle>/assets`.
69 ///
70 /// # Errors
71 ///
72 /// Returns the same failures as [`ParticipantAssets::read`].
73 pub fn asset(&self, id: &AssetId) -> Result<Vec<u8>, BundleError> {
74 self.assets.read(id)
75 }
76
77 /// The asset reader, for a consumer that keeps it beyond this value.
78 #[must_use]
79 pub const fn assets(&self) -> &ParticipantAssets {
80 &self.assets
81 }
82
83 pub(crate) fn relocated(mut self, path: std::path::PathBuf) -> Self {
84 self.root.relocate(path.clone());
85 self.assets.relocate(path);
86 self
87 }
88}