lib/
utils.rs

1//! Defines utilities for this crate.
2
3use std::collections::HashMap;
4use std::ffi::OsStr;
5use std::hash::BuildHasher;
6use std::io;
7use std::path::Path;
8
9use serde::ser::SerializeSeq;
10use serde::{de, ser, Deserialize, Serialize};
11
12use super::strings;
13
14/// Recursively copies all files from one directory into another.
15///
16/// # Arguments
17///
18/// * `source` - The source directory.
19/// * `destination` - The destination directory.
20///
21/// # Errors
22///
23/// Will return `Err` if any IO errors are encountered.
24//
25// <https://stackoverflow.com/a/65192210/16968574>
26pub fn copy_dir<S, D>(source: S, destination: D) -> io::Result<()>
27where
28    S: AsRef<Path>,
29    D: AsRef<Path>,
30{
31    let source = source.as_ref();
32    let destination = destination.as_ref();
33
34    std::fs::create_dir_all(destination)?;
35
36    for entry in std::fs::read_dir(source)? {
37        let entry = entry?;
38
39        if entry.path().is_dir() {
40            copy_dir(entry.path(), destination.join(entry.file_name()))?;
41        } else {
42            std::fs::copy(entry.path(), destination.join(entry.file_name()))?;
43        }
44    }
45
46    log::debug!(
47        "copied directory {} to {}",
48        &source.display(),
49        &destination.display(),
50    );
51
52    Ok(())
53}
54
55/// Returns the file extension of a path.
56///
57/// # Arguments
58///
59/// * `path` - The path to extract the file extension from.
60///
61/// Returns `None` if the `source` path terminates in `..` or is `/`.
62#[must_use]
63pub fn get_file_extension<P>(path: &P) -> Option<&str>
64where
65    P: AsRef<Path>,
66{
67    path.as_ref().extension().and_then(OsStr::to_str)
68}
69
70/// Returns the filename of a path.
71///
72/// # Arguments
73///
74/// * `path` - The path to extract the filename from.
75///
76/// Returns `None` if the `source` path terminates in `..` or is `/`.
77#[must_use]
78pub fn get_filename<P>(path: &P) -> Option<&str>
79where
80    P: AsRef<Path>,
81{
82    path.as_ref().file_name().and_then(OsStr::to_str)
83}
84
85/// Returns the file stem of a path.
86///
87/// # Arguments
88///
89/// * `path` - The path to extract the file stem from.
90///
91/// Returns `None` if the `source` path terminates in `..` or is `/`.
92#[must_use]
93pub fn get_file_stem<P>(path: &P) -> Option<&str>
94where
95    P: AsRef<Path>,
96{
97    path.as_ref().file_stem().and_then(OsStr::to_str)
98}
99
100/// Custom deserialization method to deserialize and sanitize a string.
101#[allow(clippy::missing_errors_doc)]
102pub fn deserialize_and_sanitize<'de, D>(deserializer: D) -> std::result::Result<String, D::Error>
103where
104    D: de::Deserializer<'de>,
105{
106    let s: &str = Deserialize::deserialize(deserializer)?;
107    Ok(strings::sanitize(s))
108}
109
110/// Custom serialization method to convert a `HashMap<K, V>` to `Vec<V>`.
111// https://rust-lang.github.io/rust-clippy/master/index.html
112#[allow(clippy::missing_errors_doc)]
113pub fn serialize_hashmap_to_vec<S, K, V, B>(
114    map: &HashMap<K, V, B>,
115    serializer: S,
116) -> std::result::Result<S::Ok, S::Error>
117where
118    S: ser::Serializer,
119    V: Serialize,
120    B: BuildHasher,
121{
122    let values: Vec<&V> = map.values().collect();
123    let mut seq = serializer.serialize_seq(Some(values.len()))?;
124    for value in values {
125        seq.serialize_element(value)?;
126    }
127    seq.end()
128}
129
130#[cfg(test)]
131pub(crate) mod testing {
132    use std::path::PathBuf;
133
134    use crate::defaults::test::TemplatesDirectory;
135
136    /// Loads a test template from the [`TEST_TEMPLATES`][test-templates] directory.
137    ///
138    /// # Arguments
139    ///
140    /// * `directory` - The template directory.
141    /// * `filename` - The template filename.
142    ///
143    /// [test-templates]: crate::defaults::TEST_TEMPLATES
144    #[allow(clippy::missing_panics_doc)]
145    pub fn load_template_str(directory: TemplatesDirectory, filename: &str) -> String {
146        let path = PathBuf::from(directory).join(filename);
147
148        std::fs::read_to_string(path).unwrap()
149    }
150}