wash_lib/
drain.rs

1//! Remove cached wasmCloud files like OCI artifacts or downloaded binaries
2
3use std::{env, fs, io::Result, path::PathBuf};
4
5use crate::config::{dev_dir, downloads_dir};
6
7/// A type that allows you to clean up (i.e. drain) a set of caches and folders used by wasmcloud
8#[derive(Debug, Clone)]
9#[cfg_attr(feature = "cli", derive(clap::Subcommand))]
10pub enum Drain {
11    /// Remove all cached files created by wasmcloud
12    All,
13    /// Remove cached files downloaded from OCI registries by wasmCloud
14    Oci,
15    /// Remove cached binaries extracted from provider archives
16    Lib,
17    /// Remove files and logs from wash dev sessions
18    Dev,
19    /// Remove downloaded and generated files from launching wasmCloud hosts
20    Downloads,
21}
22
23impl IntoIterator for &Drain {
24    type Item = PathBuf;
25    type IntoIter = std::vec::IntoIter<Self::Item>;
26
27    fn into_iter(self) -> Self::IntoIter {
28        let paths = match self {
29            Drain::All => vec![
30                /* Lib    */ env::temp_dir().join("wasmcloudcache"),
31                /* Oci    */ env::temp_dir().join("wasmcloud_ocicache"),
32                /* Downloads */ downloads_dir().unwrap_or_default(),
33            ],
34            Drain::Lib => vec![env::temp_dir().join("wasmcloudcache")],
35            Drain::Oci => vec![env::temp_dir().join("wasmcloud_ocicache")],
36            Drain::Dev => vec![dev_dir().unwrap_or_default()],
37            Drain::Downloads => vec![downloads_dir().unwrap_or_default()],
38        };
39        paths.into_iter()
40    }
41}
42
43impl Drain {
44    /// Cleans up all data based on the type of Drain requested. Returns a list of paths that were
45    /// cleaned
46    pub fn drain(self) -> Result<Vec<PathBuf>> {
47        self.into_iter()
48            .filter(|path| path.exists())
49            .map(remove_dir_contents)
50            .collect::<Result<Vec<PathBuf>>>()
51    }
52}
53
54fn remove_dir_contents(path: PathBuf) -> Result<PathBuf> {
55    for entry in fs::read_dir(&path)? {
56        let path = entry?.path();
57        if path.is_dir() {
58            fs::remove_dir_all(&path)?;
59        } else if path.is_file() {
60            fs::remove_file(&path)?;
61        }
62    }
63    Ok(path)
64}
65
66#[cfg(test)]
67mod test {
68    use super::*;
69
70    #[test]
71    fn test_dir_clean() {
72        let tempdir = tempfile::tempdir().expect("Unable to create tempdir");
73
74        let subdir = tempdir.path().join("foobar");
75        fs::create_dir(&subdir).unwrap();
76
77        // Create the files and drop the handles
78        {
79            fs::File::create(subdir.join("baz")).unwrap();
80            fs::File::create(tempdir.path().join("baz")).unwrap();
81        }
82
83        remove_dir_contents(tempdir.path().to_owned())
84            .expect("Shouldn't get an error when cleaning files");
85        assert!(
86            tempdir
87                .path()
88                .read_dir()
89                .expect("Top level dir should still exist")
90                .next()
91                .is_none(),
92            "Directory should be empty"
93        );
94    }
95}