daml-lf 0.2.2

API for working with Daml-LF data
Documentation
use crate::element::DamlPackage;
use crate::error::{DamlLfError, DamlLfResult};
use crate::lf_protobuf::com::daml::daml_lf::archive_payload::Sum;
use crate::lf_protobuf::com::daml::daml_lf::ArchivePayload;
use crate::lf_protobuf::com::daml::daml_lf_1;
use crate::lf_protobuf::com::daml::daml_lf_1::module::Name;
use crate::{convert, LanguageV1MinorVersion, LanguageVersion};
use bytes::Bytes;
use itertools::Itertools;
use prost::Message;
use std::convert::TryFrom;

/// A `Daml LF` archive payload (aka "package").
///
/// A `Daml LF` archive payload contains a `package` and a `language_version`.
#[derive(Debug, Clone)]
pub struct DamlLfArchivePayload {
    pub language_version: LanguageVersion,
    pub package: DamlLfPackage,
}

impl DamlLfArchivePayload {
    /// Create a `DamlLfArchivePayload` from an existing [`DamlLfPackage`] and `language_version`.
    ///
    /// [`DamlLfPackage`]: enum.DamlLfPackage.html
    pub const fn new(language_version: LanguageVersion, package: DamlLfPackage) -> Self {
        Self {
            language_version,
            package,
        }
    }

    /// Create a `DamlLfArchivePayload` from a serialized protobuf byte buffer.
    ///
    /// This method is suitable for use with the bytes returned by the [`payload()`] method of [`DamlPackage`] which is
    /// returned by the [`get_package`] and [`get_package_sync`] methods.
    ///
    /// # Errors
    ///
    /// If the `payload_buffer` cannot be deserialized into a `DamlLfArchivePayload` then
    /// [`DamlLfParseError`] will be returned.
    ///
    /// If the deserialized `DamlLfArchivePayload` is not of a known version then [`UnknownVersion`]
    /// will be returned.
    ///
    /// Archives of `Daml LF` `v0` are not supported and will result in a [`UnsupportedVersion`]
    /// being returned.
    ///
    /// ```no_run
    /// # use daml_lf::DamlLfResult;
    /// # use daml_lf::DamlLfArchivePayload;
    /// # fn main() -> DamlLfResult<()> {
    /// let buffer = Vec::<u8>::new();
    /// let payload = DamlLfArchivePayload::from_bytes(buffer)?;
    /// assert_eq!(true, payload.contains_module("PingPong"));
    /// # Ok(())
    /// # }
    /// ```
    /// [`get_package`]:
    /// ../daml-grpc/service/daml_package_service/struct.DamlPackageService.html#method.get_package
    /// [`get_package_sync`]:
    /// ../daml-grpc/service/daml_package_service/struct.DamlPackageService.html#method.get_package_sync
    /// [`payload()`]: https://docs.rs/daml-grpc/0.2.2/daml_grpc/data/package/struct.DamlPackage.html#method.payload
    /// [`DamlPackage`]: https://docs.rs/daml-grpc/0.2.2/daml_grpc/data/package/struct.DamlPackage.html
    /// [`UnsupportedVersion`]: DamlLfError::UnsupportedVersion
    /// [`DamlLfParseError`]: DamlLfError::DamlLfParseError
    /// [`UnknownVersion`]: DamlLfError::UnknownVersion
    pub fn from_bytes(payload_buffer: impl Into<Bytes>) -> DamlLfResult<Self> {
        let payload: ArchivePayload = ArchivePayload::decode(payload_buffer.into())?;
        match payload.sum {
            Some(Sum::DamlLf1(p)) => Ok(Self::new(
                LanguageVersion::new_v1(LanguageV1MinorVersion::try_from(payload.minor.as_str())?),
                DamlLfPackage::V1(p),
            )),
            _ => Err(DamlLfError::new_unknown_version("none")),
        }
    }

    /// Create a [`DamlArchive`] from this [`DamlLfArchivePayload`] and apply it to `f`.
    ///
    /// See [`DarFile::apply`] for details.
    ///
    /// [`DamlArchive`]: crate::element::DamlArchive
    /// [`DarFile::apply`]: crate::dar::DarFile::apply
    pub fn apply<R, F>(self, f: F) -> DamlLfResult<R>
    where
        F: FnOnce(&DamlPackage<'_>) -> R,
    {
        convert::apply_payload(self, f)
    }

    /// Returns true if the `package` within this `DamlLfArchivePayload` contains `module`, false otherwise.
    ///
    /// The supplied `module` name is assumed to be in `DottedName` format, i.e. `TopModule.SubModule.Module`.
    ///
    /// ```no_run
    /// # use daml_lf::DamlLfResult;
    /// # use daml_lf::DamlLfArchivePayload;
    /// # fn main() -> DamlLfResult<()> {
    /// let buffer = Vec::<u8>::new();
    /// let payload = DamlLfArchivePayload::from_bytes(buffer)?;
    /// assert_eq!(true, payload.contains_module("PingPong"));
    /// # Ok(())
    /// # }
    /// ```
    pub fn contains_module(&self, module: &str) -> bool {
        match &self.package {
            DamlLfPackage::V1(package) => package.modules.iter().any(|m| match &m.name {
                Some(name) => self.decode_dotted_name(name) == module,
                _ => false,
            }),
        }
    }

    /// Returns a list of all module names with the `package` contained within this `DamlLfArchivePayload`.
    ///
    /// The returned module names are strings in `DottedName` format, i.e. `TopModule.SubModule.Module`.
    ///
    /// ```no_run
    /// # use daml_lf::DamlLfResult;
    /// # use daml_lf::DamlLfArchivePayload;
    /// # fn main() -> DamlLfResult<()> {
    /// let buffer = Vec::<u8>::new();
    /// let payload = DamlLfArchivePayload::from_bytes(buffer)?;
    /// assert_eq!(vec!["PingPong", "Module1.Module2"], payload.list_modules());
    /// # Ok(())
    /// # }
    /// ```
    pub fn list_modules(&self) -> Vec<String> {
        match &self.package {
            DamlLfPackage::V1(package) =>
                package.modules.iter().filter_map(|m| m.name.as_ref().map(|dn| self.decode_dotted_name(dn))).collect(),
        }
    }

    /// The `language_version` version of this `payload`.
    pub const fn language_version(&self) -> &LanguageVersion {
        &self.language_version
    }

    /// The package embedded in this `payload`.
    pub const fn package(&self) -> &DamlLfPackage {
        &self.package
    }

    fn decode_dotted_name(&self, name: &Name) -> String {
        match &self.package {
            DamlLfPackage::V1(package) => match name {
                Name::NameInternedDname(i) => package
                    .interned_dotted_names
                    .get(*i as usize)
                    .map(|dn| {
                        dn.segments_interned_str
                            .iter()
                            .map(|&i| package.interned_strings.get(i as usize).expect("Package.interned_strings"))
                            .join(".")
                    })
                    .expect("Package.interned_dotted_names"),
                Name::NameDname(dn) => dn.segments.join("."),
            },
        }
    }
}

/// The supported `Daml LF` package formats.
#[derive(Debug, Clone)]
pub enum DamlLfPackage {
    V1(daml_lf_1::Package),
}