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}