daml_lf/archive.rs
1use crate::element::DamlPackage;
2use crate::lf_protobuf::com::daml::daml_lf::Archive;
3use crate::DamlLfResult;
4use crate::{convert, DamlLfArchivePayload};
5use bytes::Bytes;
6use prost::Message;
7use std::ffi::OsStr;
8use std::fs::File;
9use std::io::Read;
10use std::path::Path;
11
12/// The default name for an unnamed archive.
13pub const DEFAULT_ARCHIVE_NAME: &str = "Unnamed";
14
15/// A `Daml LF` archive (aka a `dalf` file).
16///
17/// A `DamlLfArchive` contains a `name`, a `payload` (aka "package"), a `hash` (aka "package id") of that `payload` for
18/// a given `hash_function`.
19#[derive(Debug, Clone)]
20pub struct DamlLfArchive {
21 pub name: String,
22 pub payload: DamlLfArchivePayload,
23 pub hash_function: DamlLfHashFunction,
24 pub hash: String,
25}
26
27impl DamlLfArchive {
28 /// Create an archive from an existing `payload`, `hash_function` and `hash`.
29 ///
30 /// Note that this method does not validate that the supplied `hash` is valid for the supplied `payload` and
31 /// `hash_function` and thus could create an invalid archive.
32 pub fn new(
33 name: impl Into<String>,
34 payload: impl Into<DamlLfArchivePayload>,
35 hash_function: impl Into<DamlLfHashFunction>,
36 hash: impl Into<String>,
37 ) -> Self {
38 Self {
39 name: name.into(),
40 payload: payload.into(),
41 hash_function: hash_function.into(),
42 hash: hash.into(),
43 }
44 }
45
46 /// Deserialize an archive from the protobuf binary representation with a default name.
47 ///
48 /// Deserialize the supplied protobuf `bytes` into a `DamlLfArchive`. The embedded `payload` (bytes) will also
49 /// be deserialized into a [`DamlLfArchivePayload`].
50 ///
51 /// # Errors
52 ///
53 /// If the provided bytes cannot be deserialized into an archive (or the embedded `payload` cannot be deserialized
54 /// into a [`DamlLfArchivePayload`]) then [`DamlLfParseError`] will be returned.
55 ///
56 /// If the embedded `payload` is not of a known version then [`UnknownVersion`] will be returned.
57 ///
58 /// Archives of `Daml LF` `v0` are not supported and will result in a [`UnsupportedVersion`] being returned.
59 ///
60 /// # Examples
61 ///
62 /// ```no_run
63 /// # use daml_lf::{DamlLfArchive, DamlLfHashFunction};
64 /// # use daml_lf::DamlLfResult;
65 /// # fn main() -> DamlLfResult<()> {
66 /// let buffer = Vec::<u8>::new();
67 /// let archive = DamlLfArchive::from_bytes(buffer)?;
68 /// assert_eq!(&DamlLfHashFunction::Sha256, archive.hash_function());
69 /// # Ok(())
70 /// # }
71 /// ```
72 /// [`DamlLfParseError`]: crate::DamlLfError::DamlLfParseError
73 /// [`UnknownVersion`]: crate::DamlLfError::UnknownVersion
74 /// [`UnsupportedVersion`]: crate::DamlLfError::UnsupportedVersion
75 /// [`DamlLfArchivePayload`]: DamlLfArchivePayload
76 pub fn from_bytes(bytes: impl Into<Bytes>) -> DamlLfResult<Self> {
77 Self::from_bytes_named(DEFAULT_ARCHIVE_NAME, bytes)
78 }
79
80 /// Deserialize a named archive from the protobuf binary representation.
81 ///
82 /// Deserialize the supplied protobuf `bytes` into a `DamlLfArchive`. The embedded `payload` (bytes) will also
83 /// be deserialized into a [`DamlLfArchivePayload`].
84 ///
85 /// # Errors
86 ///
87 /// If the provided bytes cannot be deserialized into an archive (or the embedded `payload` cannot be deserialized
88 /// into a [`DamlLfArchivePayload`]) then [`DamlLfParseError`] will be returned.
89 ///
90 /// If the embedded `payload` is not of a known version then [`UnknownVersion`] will be returned.
91 ///
92 /// Archives of `Daml LF` `v0` are not supported and will result in a [`UnsupportedVersion`] being returned.
93 ///
94 /// # Examples
95 ///
96 /// ```no_run
97 /// # use daml_lf::{DamlLfArchive, DamlLfHashFunction};
98 /// # use daml_lf::DamlLfResult;
99 /// # fn main() -> DamlLfResult<()> {
100 /// let buffer = Vec::<u8>::new();
101 /// let archive = DamlLfArchive::from_bytes_named("foo", buffer)?;
102 /// assert_eq!(&DamlLfHashFunction::Sha256, archive.hash_function());
103 /// assert_eq!("foo", archive.name());
104 /// # Ok(())
105 /// # }
106 /// ```
107 /// [`DamlLfParseError`]: crate::DamlLfError::DamlLfParseError
108 /// [`UnknownVersion`]: crate::DamlLfError::UnknownVersion
109 /// [`UnsupportedVersion`]: crate::DamlLfError::UnsupportedVersion
110 /// [`DamlLfArchivePayload`]: DamlLfArchivePayload
111 pub fn from_bytes_named(name: impl Into<String>, bytes: impl Into<Bytes>) -> DamlLfResult<Self> {
112 let archive: Archive = Archive::decode(bytes.into())?;
113 let payload = DamlLfArchivePayload::from_bytes(archive.payload)?;
114 let archive_name = name.into();
115 let sanitized_name = archive_name.rfind(&archive.hash).map_or(&archive_name[..], |i| &archive_name[..i - 1]);
116 Ok(Self::new(sanitized_name, payload, DamlLfHashFunction::Sha256, archive.hash))
117 }
118
119 /// Read and parse an archive from a `dalf` file.
120 ///
121 /// # Errors
122 ///
123 /// If the provided file cannot be read an [`IOError`] will be returned which contains the underlying IO error.
124 ///
125 /// If the contents of the file cannot be deserialized into an archive (or the embedded `payload` cannot be
126 /// deserialized into a [`DamlLfArchivePayload`]) then [`DamlLfParseError`] will be returned.
127 ///
128 /// If the embedded `payload` is not of a known version then [`UnknownVersion`] will be returned.
129 ///
130 /// Archives of `Daml LF` `v0` are not supported and will result in a [`UnsupportedVersion`] being returned.
131 ///
132 /// # Examples
133 ///
134 /// ```no_run
135 /// # use daml_lf::{DamlLfArchive, DamlLfHashFunction};
136 /// # use daml_lf::DamlLfResult;
137 /// # fn main() -> DamlLfResult<()> {
138 /// let archive = DamlLfArchive::from_file("Example.dalf")?;
139 /// assert_eq!(&DamlLfHashFunction::Sha256, archive.hash_function());
140 /// assert_eq!("Example", archive.name());
141 /// # Ok(())
142 /// # }
143 /// ```
144 /// [`IOError`]: crate::DamlLfError::IoError
145 /// [`DamlLfParseError`]: crate::DamlLfError::DamlLfParseError
146 /// [`UnknownVersion`]: crate::DamlLfError::UnknownVersion
147 /// [`UnsupportedVersion`]: crate::DamlLfError::UnsupportedVersion
148 /// [`DamlLfArchivePayload`]: DamlLfArchivePayload
149 pub fn from_file(dalf_path: impl AsRef<Path>) -> DamlLfResult<Self> {
150 let mut buffer = Vec::new();
151 let mut dalf_file = File::open(dalf_path.as_ref())?;
152 dalf_file.read_to_end(&mut buffer)?;
153 let archive_name_stem = dalf_path.as_ref().file_stem().and_then(OsStr::to_str).unwrap_or(DEFAULT_ARCHIVE_NAME);
154 Self::from_bytes_named(archive_name_stem, buffer)
155 }
156
157 /// Create a [`DamlArchive`] from a [`DamlLfArchive`] and apply it to `f`.
158 ///
159 /// See [`DarFile::apply`] for details.
160 ///
161 /// [`DamlArchive`]: crate::element::DamlArchive
162 /// [`DarFile::apply`]: crate::dar::DarFile::apply
163 pub fn apply<R, F>(&self, f: F) -> DamlLfResult<R>
164 where
165 F: FnOnce(&DamlPackage<'_>) -> R,
166 {
167 convert::apply_dalf(self, f)
168 }
169
170 /// The name of this archive.
171 pub fn name(&self) -> &str {
172 &self.name
173 }
174
175 /// The payload (aka "package") contained within this archive.
176 pub const fn payload(&self) -> &DamlLfArchivePayload {
177 &self.payload
178 }
179
180 /// The hashing function used to generate this archives `hash`.
181 pub const fn hash_function(&self) -> &DamlLfHashFunction {
182 &self.hash_function
183 }
184
185 /// The hash of this archive (aka "package id").
186 pub fn hash(&self) -> &str {
187 &self.hash
188 }
189}
190
191/// The hash function used to compute
192#[derive(Debug, Clone, Eq, PartialEq)]
193pub enum DamlLfHashFunction {
194 Sha256,
195}