Skip to main content

abbaye/
utils.rs

1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4use flate2::Compression;
5use flate2::write::GzEncoder;
6use miette::{IntoDiagnostic, Result};
7
8/// Expands shell-style variables in a path string.
9///
10/// This function takes a path containing variables in `$VAR` or `${VAR}` syntax
11/// and substitutes them with values from the provided iterator of key-value pairs.
12/// Any variables not found in the provided mappings are left unchanged.
13///
14/// # Generic Parameters
15///
16/// * `P` - A type that can be converted to a `Path`
17/// * `I` - An iterator-like type that produces `(S, S)` tuples
18/// * `S` - A type that can be read as `&str`
19///
20/// # Arguments
21///
22/// * `path` - The path string or `Path` object containing variables
23/// * `vars` - An iterator of `(key, value)` pairs for variable substitution
24///
25/// # Returns
26///
27/// A `PathBuf` with all variables substituted with their corresponding values.
28///
29/// # Examples
30///
31/// ```
32/// use std::collections::HashMap;
33///
34/// let mut vars = HashMap::new();
35/// vars.insert("USER", "john");
36/// vars.insert("PROJECT", "rust");
37///
38/// let expanded = expand_variables("/home/$USER/$PROJECT", vars);
39/// assert_eq!(expanded.to_string_lossy(), "/home/john/rust");
40/// ```
41///
42/// With a vector:
43///
44/// ```
45/// let vars = vec![("USER", "john"), ("PROJECT", "rust")];
46/// let expanded = expand_variables("/home/$USER/${PROJECT}", vars);
47/// assert_eq!(expanded.to_string_lossy(), "/home/john/rust");
48/// ```
49pub fn expand_variables<P, I, S>(path: P, vars: I) -> PathBuf
50where
51    P: AsRef<Path>,
52    I: IntoIterator<Item = (S, S)>,
53    S: AsRef<str>,
54{
55    let mut path_str = path.as_ref().to_string_lossy().to_string();
56
57    // Convert iterator into a HashMap for efficient lookup
58    let var_map: HashMap<String, String> = vars
59        .into_iter()
60        .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string()))
61        .collect();
62
63    // Replace each variable
64    for (key, value) in var_map {
65        path_str = path_str.replace(&format!("${{{}}}", key), &value);
66        path_str = path_str.replace(&format!("${}", key), &value);
67    }
68
69    PathBuf::from(path_str)
70}
71
72/// Pack `src` directory into a `.tar.gz` archive at `dest`.
73///
74/// The top-level entry inside the archive is named after the source directory.
75pub(crate) fn archive_dir(src: &Path, dest: &Path) -> Result<()> {
76    let dir_name = src
77        .file_name()
78        .map(|n| n.to_string_lossy().into_owned())
79        .unwrap_or_else(|| "docs".to_owned());
80
81    let file = std::fs::File::create(dest).into_diagnostic()?;
82    let enc = GzEncoder::new(file, Compression::default());
83    let mut archive = tar::Builder::new(enc);
84    archive.append_dir_all(&dir_name, src).into_diagnostic()?;
85    archive
86        .into_inner()
87        .into_diagnostic()?
88        .finish()
89        .into_diagnostic()?;
90    Ok(())
91}