kaste/
lib.rs

1use std::{
2    error::Error,
3    fs::{self, File},
4    io::{self, BufReader, BufWriter, ErrorKind},
5    path::PathBuf,
6};
7
8use serde::{Deserialize, Serialize};
9
10pub struct Bucket {
11    pub rdns: String,
12    pub shared: bool,
13}
14
15pub enum BucketChild {
16    Resource(String),
17    SubBucket(String),
18}
19
20impl Bucket {
21    pub fn path(&self) -> PathBuf {
22        let rdns_path = self.rdns.replace('.', "/");
23        let mut path = dirs::data_dir().unwrap();
24        path.push("bucket");
25        path.push(rdns_path);
26        path
27    }
28
29    pub fn new<S>(rdns: S, shared: bool) -> Self
30    where
31        S : Into<String>
32    {
33        let result = Self { rdns: rdns.into(), shared };
34        let path = result.path();
35
36        if let Err(e) = fs::create_dir_all(&path) {
37            if e.kind() == ErrorKind::AlreadyExists {
38                assert!(fs::metadata(&path).unwrap().file_type().is_dir());
39            }
40        }
41
42        result
43    }
44
45    pub fn get_resource_path(&self, resource: String) -> PathBuf {
46        self.path().join(resource)
47    }
48
49    pub fn read<R>(&self, resource: String) -> Result<R, Box<dyn Error>>
50    where
51        R: for<'a> Deserialize<'a>,
52    {
53        Ok(serde_json::from_reader(BufReader::new(File::open(
54            self.get_resource_path(resource),
55        )?))?)
56    }
57
58    pub fn write<R>(&self, resource: String, data: R) -> Result<(), Box<dyn Error>>
59    where
60        R: Serialize,
61    {
62        Ok(serde_json::to_writer(
63            BufWriter::new(File::open(self.get_resource_path(resource))?),
64            &data,
65        )?)
66    }
67
68    pub fn list_contents(&self) -> Result<Vec<BucketChild>, io::Error> {
69        let dir = fs::read_dir(self.path())?;
70
71        let entries = dir.map(|entry| {
72            entry.and_then(|entry| {
73                if entry.file_type()?.is_dir() {
74                    Ok(BucketChild::SubBucket(
75                        entry.file_name().to_string_lossy().to_string(),
76                    ))
77                } else {
78                    Ok(BucketChild::Resource(
79                        entry.file_name().to_string_lossy().to_string(),
80                    ))
81                }
82            })
83        });
84
85        let mut result = Vec::new();
86        for entry in entries {
87            result.push(entry?);
88        }
89
90        Ok(result)
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn bucket_path() {
100        let bucket = Bucket::new("com.example.Example", false);
101        assert_eq!(bucket.path(), dirs::data_dir().unwrap().join("bucket/com/example/Example"));
102    }
103}