comtrya_lib/manifests/
load.rs1use super::Manifest;
2use crate::{
3 contexts::{to_tera, Contexts},
4 manifests::get_manifest_name,
5 tera_functions::register_functions,
6};
7use ignore::WalkBuilder;
8use std::{
9 collections::HashMap, error::Error, ffi::OsStr, fs::canonicalize, ops::Deref, path::PathBuf,
10};
11use tera::Tera;
12use tracing::{error, span};
13
14pub fn load(manifest_path: PathBuf, contexts: &Contexts) -> HashMap<String, Manifest> {
15 let mut manifests: HashMap<String, Manifest> = HashMap::new();
16
17 let mut walker = WalkBuilder::new(&manifest_path);
18
19 walker
21 .standard_filters(true)
22 .follow_links(false)
23 .same_file_system(true)
24 .max_depth(Some(9))
26 .filter_entry(|entry| {
27 !(entry.file_type().map_or(false, |ft| ft.is_dir())
28 && entry.file_name() == OsStr::new("files"))
29 })
30 .build()
31 .filter(|entry| {
33 !entry
34 .as_ref()
35 .ok()
36 .and_then(|entry| entry.metadata().ok().map(|entry| entry.is_dir()))
37 .unwrap_or(false)
38 })
39 .filter(|entry| {
40 entry
41 .as_ref()
42 .ok()
43 .and_then(|entry| entry.file_name().to_str())
44 .map(|file_name| {
45 file_name.ends_with(".yaml")
46 || file_name.ends_with(".yml")
47 || file_name.ends_with(".toml")
48 })
49 .unwrap_or(false)
50 })
51 .for_each(|entry| {
52 if let Ok(filename) = entry {
53 let span = span!(
54 tracing::Level::INFO,
55 "manifest_load",
56 manifest = filename.file_name().to_str()
57 )
58 .entered();
59
60 let entry = canonicalize(filename.into_path()).ok().unwrap_or_default();
61 let contents =
62 std::fs::read_to_string(entry.clone()).unwrap_or_else(|_| String::from(""));
63 let template = contents.as_str();
64
65 let mut tera = Tera::default();
66 register_functions(&mut tera);
67
68 let template = match tera.render_str(template, &to_tera(contexts)) {
69 Ok(template) => template,
70 Err(err) => {
71 match err.source() {
72 Some(err) => error!(message = err.source()),
73 None => error!(message = err.to_string().as_str()),
74 }
75
76 span.exit();
77
78 return;
79 }
80 };
81
82 let manifest: anyhow::Result<Manifest> = match entry
83 .extension()
84 .and_then(OsStr::to_str)
85 {
86 Some("yaml") | Some("yml") => {
87 serde_yml::from_str::<Manifest>(template.deref()).map_err(anyhow::Error::from)
88 }
89 Some("toml") => toml::from_str::<Manifest>(template.deref()).map_err(anyhow::Error::from),
90 _ => {
91 error!("Unrecognized file extension for manifest");
92 span.exit();
93
94 return;
95 }
96 };
97
98 match manifest {
99 Ok(mut manifest) => {
100 let name = get_manifest_name(&manifest_path, &entry)
101 .expect("Failed to get manifest name");
102
103 manifest.root_dir = entry.parent().map(|parent| parent.to_path_buf());
104
105 manifest.name = Some(name.clone());
106
107 manifests.insert(name, manifest);
108 }
109 Err(err) => {
110 let manifest_name =
111 get_manifest_name(&manifest_path, &entry).unwrap_or_default();
112
113 error!("Manifest '{manifest_name}' in file with path '{}' cannot be parsed. Reason: {err}", &entry.display());
114 }
115 }
116
117 span.exit();
118 }
119 });
120
121 manifests
122}