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