1use crate::error::{DamlLfError, DamlLfResult};
2use itertools::Itertools;
3use std::convert::Into;
4use std::fmt::{Display, Formatter};
5use yaml_rust::YamlLoader;
6
7const MANIFEST_VERSION_KEY: &str = "Manifest-Version";
8const CREATED_BY_KEY: &str = "Created-By";
9const DALF_MAIN_KEY: &str = "Main-Dalf";
10const DALFS_KEY: &str = "Dalfs";
11const FORMAT_KEY: &str = "Format";
12const ENCRYPTION_KEY: &str = "Encryption";
13const VERSION_1_VALUE: &str = "1.0";
14const NON_ENCRYPTED_VALUE: &str = "non-encrypted";
15const DAML_LF_VALUE: &str = "daml-lf";
16
17#[derive(Copy, Clone, Debug, Eq, PartialEq)]
19pub enum DarManifestVersion {
20 Unknown,
21 V1,
22}
23
24impl Display for DarManifestVersion {
25 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26 match self {
27 DarManifestVersion::V1 | DarManifestVersion::Unknown => VERSION_1_VALUE.fmt(f),
28 }
29 }
30}
31
32#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34pub enum DarManifestFormat {
35 Unknown,
36 DamlLf,
37}
38
39impl Display for DarManifestFormat {
40 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41 match self {
42 DarManifestFormat::DamlLf | DarManifestFormat::Unknown => DAML_LF_VALUE.fmt(f),
43 }
44 }
45}
46
47#[derive(Copy, Clone, Debug, Eq, PartialEq)]
49pub enum DarEncryptionType {
50 Unknown,
51 NotEncrypted,
52}
53
54impl Display for DarEncryptionType {
55 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56 match self {
57 DarEncryptionType::NotEncrypted | DarEncryptionType::Unknown => NON_ENCRYPTED_VALUE.fmt(f),
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
79pub struct DarManifest {
80 version: DarManifestVersion,
81 created_by: String,
82 dalf_main: String,
83 dalf_dependencies: Vec<String>,
84 format: DarManifestFormat,
85 encryption: DarEncryptionType,
86}
87
88impl DarManifest {
89 pub fn new(
91 version: impl Into<DarManifestVersion>,
92 created_by: impl Into<String>,
93 dalf_main: impl Into<String>,
94 dalf_dependencies: Vec<String>,
95 format: impl Into<DarManifestFormat>,
96 encryption: impl Into<DarEncryptionType>,
97 ) -> Self {
98 Self {
99 version: version.into(),
100 created_by: created_by.into(),
101 dalf_main: dalf_main.into(),
102 dalf_dependencies,
103 format: format.into(),
104 encryption: encryption.into(),
105 }
106 }
107
108 pub fn new_implied(dalf_main: impl Into<String>, dalf_dependencies: Vec<String>) -> Self {
110 Self::new(
111 DarManifestVersion::Unknown,
112 "implied",
113 dalf_main,
114 dalf_dependencies,
115 DarManifestFormat::Unknown,
116 DarEncryptionType::Unknown,
117 )
118 }
119
120 pub fn parse(manifest: &str) -> DamlLfResult<Self> {
156 let docs = YamlLoader::load_from_str(manifest)?;
157 let doc = docs.first().ok_or_else(|| DamlLfError::new_dar_parse_error("unexpected manifest format"))?;
158
159 let manifest_version = match doc[MANIFEST_VERSION_KEY].as_f64() {
160 Some(s) if format!("{:.*}", 1, s) == VERSION_1_VALUE => Ok(DarManifestVersion::V1),
161 Some(s) => Err(DamlLfError::new_dar_parse_error(format!(
162 "unexpected value for {}, found {}",
163 MANIFEST_VERSION_KEY, s
164 ))),
165 None => Ok(DarManifestVersion::Unknown),
166 }?;
167
168 let created_by = doc[CREATED_BY_KEY].as_str().map_or_else(|| "", |s| s);
169
170 let dalf_main = doc[DALF_MAIN_KEY]
171 .as_str()
172 .map(strip_string)
173 .ok_or_else(|| DamlLfError::new_dar_parse_error(format!("key {} not found", DALF_MAIN_KEY)))?;
174
175 let dalf_dependencies = match doc[DALFS_KEY].as_str() {
176 Some(s) => Ok(s
177 .split(',')
178 .filter_map(|dalf: &str| {
179 let stripped_dalf = strip_string(dalf);
180 (stripped_dalf != dalf_main).then(|| stripped_dalf)
181 })
182 .collect()),
183 None => Err(DamlLfError::new_dar_parse_error(format!("key {} not found", DALFS_KEY))),
184 }?;
185
186 let format = match doc[FORMAT_KEY].as_str() {
187 Some(s) if s.to_lowercase() == DAML_LF_VALUE => Ok(DarManifestFormat::DamlLf),
188 Some(s) =>
189 Err(DamlLfError::new_dar_parse_error(format!("unexpected value for {}, found {}", DAML_LF_VALUE, s))),
190 None => Err(DamlLfError::new_dar_parse_error(format!("key {} not found", DAML_LF_VALUE))),
191 }?;
192
193 let encryption = match doc[ENCRYPTION_KEY].as_str() {
194 Some(s) if s.to_lowercase() == NON_ENCRYPTED_VALUE => Ok(DarEncryptionType::NotEncrypted),
195 Some(s) => Err(DamlLfError::new_dar_parse_error(format!(
196 "unexpected value for {}, found {}",
197 NON_ENCRYPTED_VALUE, s
198 ))),
199 None => Err(DamlLfError::new_dar_parse_error(format!("key {} not found", NON_ENCRYPTED_VALUE))),
200 }?;
201
202 Ok(Self::new(manifest_version, created_by, dalf_main, dalf_dependencies, format, encryption))
203 }
204
205 pub fn render(&self) -> String {
207 vec![
208 make_manifest_entry(MANIFEST_VERSION_KEY, self.version().to_string()),
209 make_manifest_entry(CREATED_BY_KEY, self.created_by()),
210 make_manifest_entry(DALF_MAIN_KEY, self.dalf_main()),
211 make_manifest_entry(DALFS_KEY, self.dalf_dependencies().iter().join(", ")),
212 make_manifest_entry(FORMAT_KEY, self.format().to_string()),
213 make_manifest_entry(ENCRYPTION_KEY, self.encryption().to_string()),
214 ]
215 .join("\n")
216 }
217
218 pub const fn version(&self) -> DarManifestVersion {
220 self.version
221 }
222
223 pub fn created_by(&self) -> &str {
225 &self.created_by
226 }
227
228 pub fn dalf_main(&self) -> &str {
230 &self.dalf_main
231 }
232
233 pub const fn dalf_dependencies(&self) -> &Vec<String> {
235 &self.dalf_dependencies
236 }
237
238 pub const fn format(&self) -> DarManifestFormat {
240 self.format
241 }
242
243 pub const fn encryption(&self) -> DarEncryptionType {
245 self.encryption
246 }
247}
248
249fn strip_string(s: impl AsRef<str>) -> String {
250 s.as_ref().chars().filter(|&c| !char::is_whitespace(c)).collect()
251}
252
253fn make_manifest_entry(key: impl AsRef<str>, value: impl AsRef<str>) -> String {
254 split_manifest_string(format!("{}: {}", key.as_ref(), value.as_ref()))
255}
256
257fn split_manifest_string(s: impl AsRef<str>) -> String {
260 let split_lines: Vec<String> =
261 s.as_ref().as_bytes().chunks(71).map(String::from_utf8_lossy).map(String::from).collect();
262 match split_lines.as_slice() {
263 [] => "".to_owned(),
264 [head] => head.clone(),
265 [head, tail @ ..] => {
266 let new_tail: String = tail.iter().map(|s| format!(" {}", s)).join("\n");
267 format!("{}\n{}", head, new_tail)
268 },
269 }
270}
271
272#[cfg(test)]
273mod test {
274 use crate::error::{DamlLfError, DamlLfResult};
275 use crate::manifest::{
276 split_manifest_string, DarEncryptionType, DarManifest, DarManifestFormat, DarManifestVersion,
277 };
278 use trim_margin::MarginTrimmable;
279
280 #[test]
281 fn test_split_manifest_line() {
282 let long = "Main-Dalf: \
283 TestingTypes-1.0.0-6c314cb04bcb26cb62aa6ebf0f8ed4bdc3cbf709847be908c9920df5574daacc/\
284 TestingTypes-1.0.0-6c314cb04bcb26cb62aa6ebf0f8ed4bdc3cbf709847be908c9920df5574daacc.dalf";
285 let expected = "
286 |Main-Dalf: TestingTypes-1.0.0-6c314cb04bcb26cb62aa6ebf0f8ed4bdc3cbf7098
287 | 47be908c9920df5574daacc/TestingTypes-1.0.0-6c314cb04bcb26cb62aa6ebf0f8e
288 | d4bdc3cbf709847be908c9920df5574daacc.dalf"
289 .trim_margin()
290 .expect("invalid test string");
291 let split = split_manifest_string(long);
292 assert_eq!(split, expected);
293 }
294
295 #[test]
296 pub fn test_split_dalfs() -> DamlLfResult<()> {
297 let manifest_str = "
298 |Manifest-Version: 1.0
299 |Created-By: damlc
300 |Main-Dalf: com.daml.lf.archive:DarReaderTest:0.1.dalf
301 |Dalfs: com.daml.lf.archive:DarReaderTest:0.1.dalf, daml-pri
302 | m.dalf
303 |Format: daml-lf
304 |Encryption: non-encrypted"
305 .trim_margin()
306 .expect("invalid test string");
307 let manifest = DarManifest::parse(&manifest_str[..])?;
308 assert_eq!(DarManifestVersion::V1, manifest.version());
309 assert_eq!("damlc", manifest.created_by());
310 assert_eq!("com.daml.lf.archive:DarReaderTest:0.1.dalf", manifest.dalf_main());
311 assert_eq!(&vec!["daml-prim.dalf"], manifest.dalf_dependencies());
312 assert_eq!(DarManifestFormat::DamlLf, manifest.format());
313 assert_eq!(DarEncryptionType::NotEncrypted, manifest.encryption());
314 Ok(())
315 }
316
317 #[test]
318 pub fn test_split_all_dalf() -> DamlLfResult<()> {
319 let manifest_str = "
320 |Manifest-Version: 1.0
321 |Created-By: damlc
322 |Sdk-Version: 0.13.16
323 |Main-Dalf: test-0.0.1-7390c3f7a0f5c4aed2cf8da2dc757885ac20ab8f2eb616a1ed
324 | b7cf57f8161d3a/test.dalf
325 |Dalfs: test-0.0.1-7390c3f7a0f5c4aed2cf8da2dc757885ac20ab8f2eb616a1edb7cf
326 | 57f8161d3a/test.dalf, test-0.0.1-7390c3f7a0f5c4aed2cf8da2dc757885ac20ab
327 | 8f2eb616a1edb7cf57f8161d3a/daml-prim.dalf, test-0.0.1-7390c3f7a0f5c4aed
328 | 2cf8da2dc757885ac20ab8f2eb616a1edb7cf57f8161d3a/daml-stdlib.dalf
329 |Format: daml-lf
330 |Encryption: non-encrypted"
331 .trim_margin()
332 .expect("invalid test string");
333 let manifest = DarManifest::parse(&manifest_str[..])?;
334 assert_eq!(DarManifestVersion::V1, manifest.version());
335 assert_eq!("damlc", manifest.created_by());
336 assert_eq!(
337 "test-0.0.1-7390c3f7a0f5c4aed2cf8da2dc757885ac20ab8f2eb616a1edb7cf57f8161d3a/test.dalf",
338 manifest.dalf_main()
339 );
340 assert_eq!(
341 &vec![
342 "test-0.0.1-7390c3f7a0f5c4aed2cf8da2dc757885ac20ab8f2eb616a1edb7cf57f8161d3a/daml-prim.dalf",
343 "test-0.0.1-7390c3f7a0f5c4aed2cf8da2dc757885ac20ab8f2eb616a1edb7cf57f8161d3a/daml-stdlib.dalf"
344 ],
345 manifest.dalf_dependencies()
346 );
347 assert_eq!(DarManifestFormat::DamlLf, manifest.format());
348 assert_eq!(DarEncryptionType::NotEncrypted, manifest.encryption());
349 Ok(())
350 }
351
352 #[test]
353 pub fn test_multiple_dalfs() -> DamlLfResult<()> {
354 let manifest_str = "
355 |Main-Dalf: A.dalf
356 |Dalfs: B.dalf, C.dalf, A.dalf, E.dalf
357 |Format: daml-lf
358 |Encryption: non-encrypted"
359 .trim_margin()
360 .expect("invalid test string");
361 let manifest = DarManifest::parse(&manifest_str[..])?;
362 assert_eq!(DarManifestVersion::Unknown, manifest.version());
363 assert_eq!("", manifest.created_by());
364 assert_eq!("A.dalf", manifest.dalf_main());
365 assert_eq!(&vec!["B.dalf", "C.dalf", "E.dalf"], manifest.dalf_dependencies());
366 assert_eq!(DarManifestFormat::DamlLf, manifest.format());
367 assert_eq!(DarEncryptionType::NotEncrypted, manifest.encryption());
368 Ok(())
369 }
370
371 #[test]
372 pub fn test_single_main_dalf() -> DamlLfResult<()> {
373 let manifest_str = "
374 |Main-Dalf: A.dalf
375 |Dalfs: A.dalf
376 |Format: daml-lf
377 |Encryption: non-encrypted"
378 .trim_margin()
379 .expect("invalid test string");
380 let manifest = DarManifest::parse(&manifest_str[..])?;
381 assert_eq!(DarManifestVersion::Unknown, manifest.version());
382 assert_eq!("", manifest.created_by());
383 assert_eq!("A.dalf", manifest.dalf_main());
384 assert_eq!(&Vec::<String>::new(), manifest.dalf_dependencies());
385 assert_eq!(DarManifestFormat::DamlLf, manifest.format());
386 assert_eq!(DarEncryptionType::NotEncrypted, manifest.encryption());
387 Ok(())
388 }
389
390 #[test]
391 pub fn test_invalid_format() {
392 let manifest_str = "
393 |Main-Dalf: A.dalf
394 |Dalfs: B.dalf, C.dalf, A.dalf, E.dalf
395 |Format: anything-different-from-daml-lf
396 |Encryption: non-encrypted"
397 .trim_margin()
398 .expect("invalid test string");
399 let manifest = DarManifest::parse(&manifest_str[..]);
400 match manifest.err().expect("expected failure") {
401 DamlLfError::DarParseError(s) =>
402 assert_eq!("unexpected value for daml-lf, found anything-different-from-daml-lf", s),
403 _ => panic!("expected failure"),
404 }
405 }
406}