daml_lf/
payload.rs

1use crate::element::DamlPackage;
2use crate::error::{DamlLfError, DamlLfResult};
3use crate::lf_protobuf::com::daml::daml_lf::archive_payload::Sum;
4use crate::lf_protobuf::com::daml::daml_lf::ArchivePayload;
5use crate::lf_protobuf::com::daml::daml_lf_1;
6use crate::lf_protobuf::com::daml::daml_lf_1::module::Name;
7use crate::{convert, LanguageV1MinorVersion, LanguageVersion};
8use bytes::Bytes;
9use itertools::Itertools;
10use prost::Message;
11use std::convert::TryFrom;
12
13/// A `Daml LF` archive payload (aka "package").
14///
15/// A `Daml LF` archive payload contains a `package` and a `language_version`.
16#[derive(Debug, Clone)]
17pub struct DamlLfArchivePayload {
18    pub language_version: LanguageVersion,
19    pub package: DamlLfPackage,
20}
21
22impl DamlLfArchivePayload {
23    /// Create a `DamlLfArchivePayload` from an existing [`DamlLfPackage`] and `language_version`.
24    ///
25    /// [`DamlLfPackage`]: enum.DamlLfPackage.html
26    pub const fn new(language_version: LanguageVersion, package: DamlLfPackage) -> Self {
27        Self {
28            language_version,
29            package,
30        }
31    }
32
33    /// Create a `DamlLfArchivePayload` from a serialized protobuf byte buffer.
34    ///
35    /// This method is suitable for use with the bytes returned by the [`payload()`] method of [`DamlPackage`] which is
36    /// returned by the [`get_package`] and [`get_package_sync`] methods.
37    ///
38    /// # Errors
39    ///
40    /// If the `payload_buffer` cannot be deserialized into a `DamlLfArchivePayload` then
41    /// [`DamlLfParseError`] will be returned.
42    ///
43    /// If the deserialized `DamlLfArchivePayload` is not of a known version then [`UnknownVersion`]
44    /// will be returned.
45    ///
46    /// Archives of `Daml LF` `v0` are not supported and will result in a [`UnsupportedVersion`]
47    /// being returned.
48    ///
49    /// ```no_run
50    /// # use daml_lf::DamlLfResult;
51    /// # use daml_lf::DamlLfArchivePayload;
52    /// # fn main() -> DamlLfResult<()> {
53    /// let buffer = Vec::<u8>::new();
54    /// let payload = DamlLfArchivePayload::from_bytes(buffer)?;
55    /// assert_eq!(true, payload.contains_module("PingPong"));
56    /// # Ok(())
57    /// # }
58    /// ```
59    /// [`get_package`]:
60    /// ../daml-grpc/service/daml_package_service/struct.DamlPackageService.html#method.get_package
61    /// [`get_package_sync`]:
62    /// ../daml-grpc/service/daml_package_service/struct.DamlPackageService.html#method.get_package_sync
63    /// [`payload()`]: https://docs.rs/daml-grpc/0.2.2/daml_grpc/data/package/struct.DamlPackage.html#method.payload
64    /// [`DamlPackage`]: https://docs.rs/daml-grpc/0.2.2/daml_grpc/data/package/struct.DamlPackage.html
65    /// [`UnsupportedVersion`]: DamlLfError::UnsupportedVersion
66    /// [`DamlLfParseError`]: DamlLfError::DamlLfParseError
67    /// [`UnknownVersion`]: DamlLfError::UnknownVersion
68    pub fn from_bytes(payload_buffer: impl Into<Bytes>) -> DamlLfResult<Self> {
69        let payload: ArchivePayload = ArchivePayload::decode(payload_buffer.into())?;
70        match payload.sum {
71            Some(Sum::DamlLf1(p)) => Ok(Self::new(
72                LanguageVersion::new_v1(LanguageV1MinorVersion::try_from(payload.minor.as_str())?),
73                DamlLfPackage::V1(p),
74            )),
75            _ => Err(DamlLfError::new_unknown_version("none")),
76        }
77    }
78
79    /// Create a [`DamlArchive`] from this [`DamlLfArchivePayload`] and apply it to `f`.
80    ///
81    /// See [`DarFile::apply`] for details.
82    ///
83    /// [`DamlArchive`]: crate::element::DamlArchive
84    /// [`DarFile::apply`]: crate::dar::DarFile::apply
85    pub fn apply<R, F>(self, f: F) -> DamlLfResult<R>
86    where
87        F: FnOnce(&DamlPackage<'_>) -> R,
88    {
89        convert::apply_payload(self, f)
90    }
91
92    /// Returns true if the `package` within this `DamlLfArchivePayload` contains `module`, false otherwise.
93    ///
94    /// The supplied `module` name is assumed to be in `DottedName` format, i.e. `TopModule.SubModule.Module`.
95    ///
96    /// ```no_run
97    /// # use daml_lf::DamlLfResult;
98    /// # use daml_lf::DamlLfArchivePayload;
99    /// # fn main() -> DamlLfResult<()> {
100    /// let buffer = Vec::<u8>::new();
101    /// let payload = DamlLfArchivePayload::from_bytes(buffer)?;
102    /// assert_eq!(true, payload.contains_module("PingPong"));
103    /// # Ok(())
104    /// # }
105    /// ```
106    pub fn contains_module(&self, module: &str) -> bool {
107        match &self.package {
108            DamlLfPackage::V1(package) => package.modules.iter().any(|m| match &m.name {
109                Some(name) => self.decode_dotted_name(name) == module,
110                _ => false,
111            }),
112        }
113    }
114
115    /// Returns a list of all module names with the `package` contained within this `DamlLfArchivePayload`.
116    ///
117    /// The returned module names are strings in `DottedName` format, i.e. `TopModule.SubModule.Module`.
118    ///
119    /// ```no_run
120    /// # use daml_lf::DamlLfResult;
121    /// # use daml_lf::DamlLfArchivePayload;
122    /// # fn main() -> DamlLfResult<()> {
123    /// let buffer = Vec::<u8>::new();
124    /// let payload = DamlLfArchivePayload::from_bytes(buffer)?;
125    /// assert_eq!(vec!["PingPong", "Module1.Module2"], payload.list_modules());
126    /// # Ok(())
127    /// # }
128    /// ```
129    pub fn list_modules(&self) -> Vec<String> {
130        match &self.package {
131            DamlLfPackage::V1(package) =>
132                package.modules.iter().filter_map(|m| m.name.as_ref().map(|dn| self.decode_dotted_name(dn))).collect(),
133        }
134    }
135
136    /// The `language_version` version of this `payload`.
137    pub const fn language_version(&self) -> &LanguageVersion {
138        &self.language_version
139    }
140
141    /// The package embedded in this `payload`.
142    pub const fn package(&self) -> &DamlLfPackage {
143        &self.package
144    }
145
146    fn decode_dotted_name(&self, name: &Name) -> String {
147        match &self.package {
148            DamlLfPackage::V1(package) => match name {
149                Name::NameInternedDname(i) => package
150                    .interned_dotted_names
151                    .get(*i as usize)
152                    .map(|dn| {
153                        dn.segments_interned_str
154                            .iter()
155                            .map(|&i| package.interned_strings.get(i as usize).expect("Package.interned_strings"))
156                            .join(".")
157                    })
158                    .expect("Package.interned_dotted_names"),
159                Name::NameDname(dn) => dn.segments.join("."),
160            },
161        }
162    }
163}
164
165/// The supported `Daml LF` package formats.
166#[derive(Debug, Clone)]
167pub enum DamlLfPackage {
168    V1(daml_lf_1::Package),
169}