endbasic_std/storage/
mem.rs1use crate::storage::{DiskSpace, Drive, DriveFactory, DriveFiles, FileAcls, Metadata};
19use async_trait::async_trait;
20use std::collections::{BTreeMap, HashMap, HashSet};
21use std::io;
22use std::str;
23
24#[derive(Default)]
26pub struct InMemoryDrive {
27 programs: HashMap<String, (String, HashSet<String>)>,
28
29 pub(crate) fake_disk_quota: Option<DiskSpace>,
33 pub(crate) fake_disk_free: Option<DiskSpace>,
34}
35
36#[async_trait(?Send)]
37impl Drive for InMemoryDrive {
38 async fn delete(&mut self, name: &str) -> io::Result<()> {
39 match self.programs.remove(name) {
40 Some(_) => Ok(()),
41 None => Err(io::Error::new(io::ErrorKind::NotFound, "Entry not found")),
42 }
43 }
44
45 async fn enumerate(&self) -> io::Result<DriveFiles> {
46 let date = time::OffsetDateTime::from_unix_timestamp(1_588_757_875).unwrap();
47
48 let mut entries = BTreeMap::new();
49 for (name, (contents, _readers)) in &self.programs {
50 entries.insert(name.clone(), Metadata { date, length: contents.len() as u64 });
51 }
52 Ok(DriveFiles::new(entries, self.fake_disk_quota, self.fake_disk_free))
53 }
54
55 async fn get(&self, name: &str) -> io::Result<String> {
56 match self.programs.get(name) {
57 Some((content, _readers)) => Ok(content.to_owned()),
58 None => Err(io::Error::new(io::ErrorKind::NotFound, "Entry not found")),
59 }
60 }
61
62 async fn get_acls(&self, name: &str) -> io::Result<FileAcls> {
63 match self.programs.get(name) {
64 Some((_content, readers)) => {
65 let mut readers = readers.iter().map(String::to_owned).collect::<Vec<String>>();
66 readers.sort();
69 Ok(FileAcls::default().with_readers(readers))
70 }
71 None => Err(io::Error::new(io::ErrorKind::NotFound, "Entry not found")),
72 }
73 }
74
75 async fn put(&mut self, name: &str, content: &str) -> io::Result<()> {
76 if let Some((prev_content, _readers)) = self.programs.get_mut(name) {
77 content.clone_into(prev_content);
78 return Ok(());
79 };
80 self.programs.insert(name.to_owned(), (content.to_owned(), HashSet::new()));
81 Ok(())
82 }
83
84 async fn update_acls(
85 &mut self,
86 name: &str,
87 add: &FileAcls,
88 remove: &FileAcls,
89 ) -> io::Result<()> {
90 let readers = match self.programs.get_mut(name) {
91 Some((_content, readers)) => readers,
92 None => return Err(io::Error::new(io::ErrorKind::NotFound, "Entry not found")),
93 };
94 for reader in remove.readers() {
95 readers.remove(reader);
96 }
97 for reader in add.readers() {
98 readers.insert(reader.to_owned());
99 }
100 Ok(())
101 }
102}
103
104#[derive(Default)]
106pub struct InMemoryDriveFactory {}
107
108impl DriveFactory for InMemoryDriveFactory {
109 fn create(&self, target: &str) -> io::Result<Box<dyn Drive>> {
110 if target.is_empty() {
111 Ok(Box::from(InMemoryDrive::default()))
112 } else {
113 Err(io::Error::new(
114 io::ErrorKind::InvalidInput,
115 "Cannot specify a path to mount an in-memory drive",
116 ))
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 fn readers(r: &[&str]) -> FileAcls {
127 FileAcls::default().with_readers(r.iter().map(|x| x.to_string()).collect::<Vec<String>>())
128 }
129
130 #[tokio::test]
131 async fn test_inmemorydrive_put_respects_acls() {
132 let mut drive = InMemoryDrive::default();
133 drive.put("untouched", "some content").await.unwrap();
134
135 drive.put("file", "before").await.unwrap();
136 drive.update_acls("file", &readers(&["r1"]), &FileAcls::default()).await.unwrap();
137
138 assert_eq!("before", drive.get("file").await.unwrap());
139 assert_eq!(readers(&["r1"]), drive.get_acls("file").await.unwrap());
140
141 drive.put("file", "after").await.unwrap();
142
143 assert_eq!("after", drive.get("file").await.unwrap());
144 assert_eq!(readers(&["r1"]), drive.get_acls("file").await.unwrap());
145 }
146
147 #[tokio::test]
148 async fn test_inmemorydrive_get_update_acls() {
149 let mut drive = InMemoryDrive::default();
150 drive.put("untouched", "some content").await.unwrap();
151
152 let err =
153 drive.update_acls("file", &readers(&["r0"]), &FileAcls::default()).await.unwrap_err();
154 assert_eq!(io::ErrorKind::NotFound, err.kind());
155
156 drive.put("file", "some content").await.unwrap();
157 assert_eq!(FileAcls::default(), drive.get_acls("file").await.unwrap());
158
159 drive.update_acls("file", &readers(&["r1", "r2"]), &readers(&["r3"])).await.unwrap();
161 assert_eq!(readers(&["r1", "r2"]), drive.get_acls("file").await.unwrap());
162
163 drive.update_acls("file", &readers(&["r2", "r2", "r3"]), &readers(&["r1"])).await.unwrap();
165 assert_eq!(readers(&["r2", "r3"]), drive.get_acls("file").await.unwrap());
166
167 assert_eq!(FileAcls::default(), drive.get_acls("untouched").await.unwrap());
169 }
170
171 #[test]
172 fn test_inmemorydrive_system_path() {
173 let drive = InMemoryDrive::default();
174 assert!(drive.system_path("foo").is_none());
175 }
176}