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}