1use 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
14pub 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#[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#[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#[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#[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#[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 #[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}