phoxal_bundle/
artifact.rs1use std::fmt;
4use std::io::{Seek, SeekFrom};
5use std::path::{Path, PathBuf};
6
7use phoxal_runtime_contract::identity::ParticipantArtifactId;
8use phoxal_runtime_contract::metadata::ParticipantContract;
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 BIN_DIR, BundleError, BundlePath, DocumentError, Sha256Digest, open_executable_source,
13};
14
15pub struct BinarySource {
18 file: std::fs::File,
19 path: PathBuf,
20}
21
22impl fmt::Debug for BinarySource {
23 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
24 formatter
25 .debug_struct("BinarySource")
26 .field("path", &self.path)
27 .finish_non_exhaustive()
28 }
29}
30
31impl BinarySource {
32 pub fn open(path: impl AsRef<Path>) -> Result<Self, BundleError> {
34 let path = path.as_ref().to_path_buf();
35 let file = open_executable_source(&path)?;
36 Ok(Self { file, path })
37 }
38
39 #[must_use]
41 pub fn path(&self) -> &Path {
42 &self.path
43 }
44
45 pub(crate) fn reader(&self) -> Result<std::fs::File, BundleError> {
46 let mut file = self
47 .file
48 .try_clone()
49 .map_err(|source| BundleError::ReadFile {
50 path: self.path.clone(),
51 source,
52 })?;
53 file.seek(SeekFrom::Start(0))
54 .map_err(|source| BundleError::ReadFile {
55 path: self.path.clone(),
56 source,
57 })?;
58 Ok(file)
59 }
60}
61
62#[derive(Clone, Debug, Deserialize, Serialize)]
64#[serde(deny_unknown_fields)]
65pub struct BinaryReference {
66 pub(crate) path: BundlePath,
67 pub(crate) digest: Sha256Digest,
68 pub(crate) size_bytes: u64,
69 pub(crate) contract: ParticipantContract,
70}
71
72impl BinaryReference {
73 pub fn from_source(
75 path: BundlePath,
76 contract: ParticipantContract,
77 source: &BinarySource,
78 ) -> Result<Self, BundleError> {
79 let file = source.reader()?;
80 let size_bytes = file
81 .metadata()
82 .map_err(|source_error| BundleError::ReadFile {
83 path: source.path.clone(),
84 source: source_error,
85 })?
86 .len();
87 Ok(Self {
88 path,
89 digest: Sha256Digest::from_reader(file).map_err(|source_error| {
90 BundleError::ReadFile {
91 path: source.path.clone(),
92 source: source_error,
93 }
94 })?,
95 size_bytes,
96 contract,
97 })
98 }
99
100 #[must_use]
101 pub const fn path(&self) -> &BundlePath {
102 &self.path
103 }
104 #[must_use]
105 pub const fn digest(&self) -> Sha256Digest {
106 self.digest
107 }
108 #[must_use]
109 pub const fn size_bytes(&self) -> u64 {
110 self.size_bytes
111 }
112 #[must_use]
113 pub const fn contract(&self) -> &ParticipantContract {
114 &self.contract
115 }
116
117 pub(crate) fn validate(&self, id: &ParticipantArtifactId) -> Result<(), DocumentError> {
118 if self.contract.id != *id {
119 return Err(DocumentError::ArtifactContractMismatch {
120 artifact: id.clone(),
121 contract: self.contract.id.clone(),
122 });
123 }
124 if !self.path.starts_with_directory(BIN_DIR) {
125 return Err(DocumentError::ArtifactOutsideBin {
126 artifact: id.clone(),
127 path: self.path.clone(),
128 });
129 }
130 Ok(())
131 }
132}