1use async_trait::async_trait;
19use std::collections::{BTreeMap, HashMap};
20use std::fmt::{self};
21use std::io;
22use std::path::PathBuf;
23use std::str;
24use time::error::Format;
25
26mod cmds;
27pub use cmds::*;
28mod fs;
29pub use fs::*;
30mod mem;
31pub use mem::*;
32
33pub(crate) fn time_format_error_to_io_error(e: Format) -> io::Error {
35 match e {
36 Format::StdIo(e) => e,
37 e => io::Error::new(io::ErrorKind::Other, format!("{}", e)),
38 }
39}
40
41#[derive(Clone, Debug, Eq, PartialEq)]
43pub struct Metadata {
44 pub date: time::OffsetDateTime,
46
47 pub length: u64,
49}
50
51#[derive(Clone, Debug, Default, Eq, PartialEq)]
53pub struct FileAcls {
54 pub readers: Vec<String>,
56}
57
58impl FileAcls {
59 pub fn is_empty(&self) -> bool {
61 self.readers.is_empty()
62 }
63
64 pub fn with_readers<T: Into<Vec<String>>>(mut self, readers: T) -> Self {
66 self.readers.extend(readers.into());
67 self
68 }
69
70 pub fn readers(&self) -> &[String] {
72 &self.readers
73 }
74
75 pub fn add_reader<R: Into<String>>(&mut self, reader: R) {
77 self.readers.push(reader.into());
78 }
79}
80
81#[derive(Clone, Copy, Debug, Eq, PartialEq)]
83pub struct DiskSpace {
84 pub bytes: u64,
86
87 pub files: u64,
89}
90
91impl DiskSpace {
92 pub fn new(bytes: u64, files: u64) -> Self {
94 Self { bytes, files }
95 }
96
97 pub fn bytes(&self) -> u64 {
99 self.bytes
100 }
101
102 pub fn files(&self) -> u64 {
104 self.files
105 }
106}
107
108#[derive(Debug)]
111pub struct DriveFiles {
112 dirents: BTreeMap<String, Metadata>,
113 disk_quota: Option<DiskSpace>,
114 disk_free: Option<DiskSpace>,
115}
116
117impl DriveFiles {
118 pub fn new(
120 dirents: BTreeMap<String, Metadata>,
121 disk_quota: Option<DiskSpace>,
122 disk_free: Option<DiskSpace>,
123 ) -> Self {
124 Self { dirents, disk_quota, disk_free }
125 }
126
127 pub fn dirents(&self) -> &BTreeMap<String, Metadata> {
129 &self.dirents
130 }
131
132 pub fn disk_quota(&self) -> &Option<DiskSpace> {
134 &self.disk_quota
135 }
136
137 pub fn disk_free(&self) -> &Option<DiskSpace> {
139 &self.disk_free
140 }
141}
142
143#[async_trait(?Send)]
145pub trait Drive {
146 async fn delete(&mut self, name: &str) -> io::Result<()>;
148
149 async fn enumerate(&self) -> io::Result<DriveFiles>;
151
152 async fn get(&self, name: &str) -> io::Result<String>;
154
155 async fn get_acls(&self, _name: &str) -> io::Result<FileAcls> {
157 Err(io::Error::new(io::ErrorKind::Other, "Operation not supported by drive"))
158 }
159
160 async fn put(&mut self, name: &str, content: &str) -> io::Result<()>;
162
163 async fn update_acls(
166 &mut self,
167 _name: &str,
168 _add: &FileAcls,
169 _remove: &FileAcls,
170 ) -> io::Result<()> {
171 Err(io::Error::new(io::ErrorKind::Other, "Operation not supported by drive"))
172 }
173
174 fn system_path(&self, _name: &str) -> Option<PathBuf> {
176 None
177 }
178}
179
180#[derive(Clone, Debug, Eq, Hash, PartialEq)]
184struct DriveKey(String);
185
186impl DriveKey {
187 fn new<T: Into<String>>(drive: T) -> io::Result<DriveKey> {
189 let drive = drive.into();
190 if !DriveKey::is_valid(&drive) {
191 return Err(io::Error::new(
192 io::ErrorKind::InvalidInput,
193 format!("Invalid drive name '{}'", drive),
194 ));
195 }
196 Ok(DriveKey(drive.to_uppercase()))
197 }
198
199 fn is_valid(s: &str) -> bool {
201 !s.is_empty() && !s.chars().any(|c| c == ':' || c == '\\' || c == '/' || c == '.')
202 }
203}
204
205impl fmt::Display for DriveKey {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 self.0.fmt(f)
208 }
209}
210
211#[derive(Debug)]
217struct Location {
218 drive: Option<DriveKey>,
219 path: String,
220}
221
222impl Location {
223 fn new(s: &str) -> io::Result<Self> {
225 let fields = s.split(':').collect::<Vec<&str>>();
226 let (drive, mut path) = match fields.as_slice() {
227 [drive, path] => (Some(DriveKey::new(*drive)?), *path),
228 [path] => (None, *path),
229 _ => {
230 return Err(io::Error::new(
231 io::ErrorKind::InvalidInput,
232 format!("Too many : separators in path '{}'", s),
233 ))
234 }
235 };
236
237 if path.is_empty() {
238 path = "/";
239 } else {
240 if !Location::is_path_valid(path)
241 || path == "."
242 || path == ".."
243 || path == "/."
244 || path == "/.."
245 {
246 return Err(io::Error::new(
247 io::ErrorKind::InvalidInput,
248 format!("Invalid path '{}'", s),
249 ));
250 }
251 let slashes = path.chars().fold(0, |a, c| if c == '/' { a + 1 } else { a });
252 if (slashes == 1 && !path.starts_with('/')) || slashes > 1 {
253 return Err(io::Error::new(
254 io::ErrorKind::InvalidInput,
255 format!("Too many / separators in path '{}'", s),
256 ));
257 }
258 }
259
260 Ok(Self { drive, path: path.to_owned() })
261 }
262
263 fn with_drive_root(drive: DriveKey) -> Self {
265 Self { drive: Some(drive), path: "/".to_owned() }
266 }
267
268 fn is_path_valid(s: &str) -> bool {
270 !s.is_empty() && !s.chars().any(|c| c == ':' || c == '\\')
271 }
272
273 fn leaf_name(&self) -> Option<&str> {
275 if self.path == "/" {
276 None
277 } else if self.path.starts_with('/') {
278 Some(&self.path[1..])
279 } else {
280 Some(&self.path)
281 }
282 }
283}
284
285impl fmt::Display for Location {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 match &self.drive {
288 Some(drive) => write!(f, "{}:{}", drive.0, self.path),
289 None => self.path.fmt(f),
290 }
291 }
292}
293
294pub trait DriveFactory {
296 fn create(&self, target: &str) -> io::Result<Box<dyn Drive>>;
298}
299
300fn split_uri(uri: &str) -> io::Result<(&str, &str)> {
302 match uri.find("://") {
303 Some(pos) if pos > 0 => Ok((&uri[0..pos], &uri[pos + 3..])),
304 _ => Err(io::Error::new(
305 io::ErrorKind::InvalidInput,
306 "Mount URI must be of the form scheme://path",
307 )),
308 }
309}
310
311struct MountedDrive {
313 uri: String,
314 drive: Box<dyn Drive>,
315}
316
317pub struct Storage {
322 factories: HashMap<String, Box<dyn DriveFactory>>,
324
325 drives: HashMap<DriveKey, MountedDrive>,
327
328 current: DriveKey,
330}
331
332impl Default for Storage {
333 fn default() -> Self {
335 let mut factories: HashMap<String, Box<dyn DriveFactory>> = HashMap::default();
336 factories.insert("memory".to_owned(), Box::from(InMemoryDriveFactory::default()));
337
338 let drive: Box<dyn Drive> = Box::from(InMemoryDrive::default());
339
340 let mut drives = HashMap::new();
341 let key = DriveKey::new("MEMORY").expect("Hardcoded drive name must be valid");
342 let mounted_drive = MountedDrive { uri: "memory://".to_owned(), drive };
343 drives.insert(key.clone(), mounted_drive);
344 Self { factories, drives, current: key }
345 }
346}
347
348impl Storage {
349 pub fn register_scheme(&mut self, scheme: &str, factory: Box<dyn DriveFactory>) {
352 assert_eq!(scheme.to_lowercase(), scheme);
353 let previous = self.factories.insert(scheme.to_owned(), factory);
354 assert!(previous.is_none(), "Tried to register {} twice", scheme);
355 }
356
357 pub fn has_scheme(&self, scheme: &str) -> bool {
359 self.factories.contains_key(scheme)
360 }
361
362 pub fn make_canonical(&self, raw_location: &str) -> io::Result<String> {
364 let mut location = Location::new(raw_location)?;
365 if location.drive.is_none() {
366 location.drive = Some(self.current.clone());
367 }
368 Ok(location.to_string())
369 }
370
371 fn attach(&mut self, name: &str, uri: &str, drive: Box<dyn Drive>) -> io::Result<()> {
375 let key = DriveKey::new(name)?;
376 if self.drives.contains_key(&key) {
377 return Err(io::Error::new(
378 io::ErrorKind::AlreadyExists,
379 format!("Drive '{}' is already mounted", name),
380 ));
381 }
382 let mounted_drive = MountedDrive { uri: uri.to_owned(), drive };
383 self.drives.insert(DriveKey::new(name)?, mounted_drive);
384 Ok(())
385 }
386
387 pub fn mount(&mut self, name: &str, uri: &str) -> io::Result<()> {
391 let (scheme, path) = split_uri(uri)?;
392 let drive = match self.factories.get(&scheme.to_lowercase()) {
393 Some(factory) => factory.create(path)?,
394 None => {
395 return Err(io::Error::new(
396 io::ErrorKind::InvalidInput,
397 format!("Unknown mount scheme '{}'", scheme),
398 ))
399 }
400 };
401 self.attach(name, uri, drive)
402 }
403
404 pub fn unmount(&mut self, name: &str) -> io::Result<()> {
409 let key = DriveKey::new(name)?;
410 if !self.drives.contains_key(&key) {
411 return Err(io::Error::new(
412 io::ErrorKind::NotFound,
413 format!("Drive '{}' is not mounted", name),
414 ));
415 }
416 if self.current == key {
417 return Err(io::Error::new(
418 io::ErrorKind::AlreadyExists,
419 format!("Cannot unmount the current drive '{}'", name),
420 ));
421 }
422 assert!(
423 self.drives.len() > 1,
424 "There must be more than one drive if the current drive is not the given name"
425 );
426 self.drives.remove(&key).expect("Drive presence in map checked above");
427 Ok(())
428 }
429
430 pub fn mounted(&self) -> BTreeMap<&str, &str> {
433 let mut info = BTreeMap::new();
434 for (name, mounted_drive) in &self.drives {
435 info.insert(name.0.as_str(), mounted_drive.uri.as_str());
436 }
437 info
438 }
439
440 pub fn cd(&mut self, location: &str) -> io::Result<()> {
445 let location = Location::new(location)?;
446 if location.leaf_name().is_some() {
447 return Err(io::Error::new(io::ErrorKind::InvalidInput, "Cannot cd to a file"));
448 }
449
450 match location.drive {
451 Some(drive) => {
452 if !self.drives.contains_key(&drive) {
453 return Err(io::Error::new(
454 io::ErrorKind::AlreadyExists,
455 format!("Drive '{}' is not mounted", drive),
456 ));
457 }
458 self.current = drive;
459 Ok(())
460 }
461 None => Ok(()),
462 }
463 }
464
465 pub fn cwd(&self) -> String {
467 Location::with_drive_root(self.current.clone()).to_string()
468 }
469
470 fn get_drive(&self, location: &Location) -> io::Result<&dyn Drive> {
472 match &location.drive {
473 Some(key) => match self.drives.get(key) {
474 Some(mounted_drive) => Ok(mounted_drive.drive.as_ref()),
475 None => Err(io::Error::new(
476 io::ErrorKind::NotFound,
477 format!("Drive '{}' is not mounted", key),
478 )),
479 },
480 None => Ok(self
481 .drives
482 .get(&self.current)
483 .expect("Current drive out of sync")
484 .drive
485 .as_ref()),
486 }
487 }
488
489 fn get_drive_mut(&mut self, location: &Location) -> io::Result<&mut dyn Drive> {
491 match &location.drive {
492 Some(key) => match self.drives.get_mut(key) {
493 Some(mounted_drive) => Ok(mounted_drive.drive.as_mut()),
494 None => Err(io::Error::new(
495 io::ErrorKind::NotFound,
496 format!("Drive '{}' is not mounted", key),
497 )),
498 },
499 None => Ok(self
500 .drives
501 .get_mut(&self.current)
502 .expect("Current drive out of sync")
503 .drive
504 .as_mut()),
505 }
506 }
507
508 pub async fn delete(&mut self, raw_location: &str) -> io::Result<()> {
510 let location = Location::new(raw_location)?;
511 match location.leaf_name() {
512 Some(name) => self.get_drive_mut(&location)?.delete(name).await,
513 None => Err(io::Error::new(
514 io::ErrorKind::NotFound,
515 format!("Missing file name in path '{}'", raw_location),
516 )),
517 }
518 }
519
520 pub async fn enumerate(&self, raw_location: &str) -> io::Result<DriveFiles> {
522 let location = Location::new(raw_location)?;
523 match location.leaf_name() {
524 Some(_) => Err(io::Error::new(
525 io::ErrorKind::NotFound,
526 format!("Location '{}' is not a directory", raw_location),
527 )),
528 None => self.get_drive(&location)?.enumerate().await,
529 }
530 }
531
532 pub async fn get(&self, raw_location: &str) -> io::Result<String> {
534 let location = Location::new(raw_location)?;
535 match location.leaf_name() {
536 Some(name) => self.get_drive(&location)?.get(name).await,
537 None => Err(io::Error::new(
538 io::ErrorKind::NotFound,
539 format!("Missing file name in path '{}'", raw_location),
540 )),
541 }
542 }
543
544 pub async fn get_acls(&self, raw_location: &str) -> io::Result<FileAcls> {
546 let location = Location::new(raw_location)?;
547 match location.leaf_name() {
548 Some(name) => self.get_drive(&location)?.get_acls(name).await,
549 None => Err(io::Error::new(
550 io::ErrorKind::NotFound,
551 format!("Missing file name in path '{}'", raw_location),
552 )),
553 }
554 }
555
556 pub async fn put(&mut self, raw_location: &str, content: &str) -> io::Result<()> {
558 let location = Location::new(raw_location)?;
559 match location.leaf_name() {
560 Some(name) => self.get_drive_mut(&location)?.put(name, content).await,
561 None => Err(io::Error::new(
562 io::ErrorKind::NotFound,
563 format!("Missing file name in path '{}'", raw_location),
564 )),
565 }
566 }
567
568 pub async fn update_acls(
571 &mut self,
572 raw_location: &str,
573 add: &FileAcls,
574 remove: &FileAcls,
575 ) -> io::Result<()> {
576 let location = Location::new(raw_location)?;
577 match location.leaf_name() {
578 Some(name) => self.get_drive_mut(&location)?.update_acls(name, add, remove).await,
579 None => Err(io::Error::new(
580 io::ErrorKind::NotFound,
581 format!("Missing file name in path '{}'", raw_location),
582 )),
583 }
584 }
585
586 pub fn system_path(&self, raw_location: &str) -> io::Result<Option<PathBuf>> {
588 let location = Location::new(raw_location)?;
589 match location.leaf_name() {
590 Some(name) => Ok(self.get_drive(&location)?.system_path(name)),
591 None => Ok(self.get_drive(&location)?.system_path("")),
592 }
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use super::*;
599 use futures_lite::future::block_on;
600
601 #[test]
602 fn test_split_uri_ok() {
603 assert_eq!(("the-scheme", ""), split_uri("the-scheme://").unwrap());
604 assert_eq!(("foo", "/some:/target"), split_uri("foo:///some:/target").unwrap());
605 assert_eq!(("foo", "bar://baz"), split_uri("foo://bar://baz").unwrap());
606 }
607
608 #[test]
609 fn test_split_uri_errors() {
610 for uri in &["", "://abc", "foo:/", "bar//", "/baz/"] {
611 assert_eq!(
612 "Mount URI must be of the form scheme://path",
613 format!("{}", split_uri(uri).unwrap_err())
614 );
615 }
616 }
617
618 #[test]
619 fn test_drivekey_new() {
620 assert_eq!(DriveKey("FOO".to_owned()), DriveKey::new("foo").unwrap());
621 assert_eq!(DriveKey("A".to_owned()), DriveKey::new("A".to_owned()).unwrap());
622 }
623
624 #[test]
625 fn test_drivekey_errors() {
626 assert_eq!("Invalid drive name ''", format!("{}", DriveKey::new("").unwrap_err()));
627 assert_eq!("Invalid drive name 'a:b'", format!("{}", DriveKey::new("a:b").unwrap_err()));
628 assert_eq!("Invalid drive name 'a\\b'", format!("{}", DriveKey::new("a\\b").unwrap_err()));
629 assert_eq!("Invalid drive name 'a/b'", format!("{}", DriveKey::new("a/b").unwrap_err()));
630 assert_eq!("Invalid drive name 'a.b'", format!("{}", DriveKey::new("a.b").unwrap_err()));
631 }
632
633 #[test]
634 fn test_location_new_ok() {
635 fn check(exp_drive: Option<&str>, exp_path: &str, input: &str) {
636 let rp = Location::new(input).unwrap();
637 assert_eq!(exp_drive.map(|s| DriveKey::new(s).unwrap()), rp.drive);
638 assert_eq!(exp_path, rp.path);
639 }
640
641 check(None, "/", "/");
642 check(None, "/foo.bas", "/foo.bas");
643
644 check(Some("A"), "/", "a:");
645 check(Some("ABC"), "/foo.bas", "abc:/foo.bas");
646 check(Some("ABC"), "Foo.Bas", "abc:Foo.Bas");
647 }
648
649 #[test]
650 fn test_location_new_errors() {
651 fn check(exp_error: &str, input: &str) {
652 let e = Location::new(input).unwrap_err();
653 assert_eq!(io::ErrorKind::InvalidInput, e.kind());
654 assert_eq!(exp_error, format!("{}", e));
655 }
656
657 check("Too many : separators in path 'a:b:c'", "a:b:c");
658
659 check("Invalid drive name ''", ":");
660 check("Invalid drive name 'a\\b'", "a\\b:");
661 check("Invalid drive name 'a/b'", "a/b:");
662 check("Invalid drive name 'a.b'", "a.b:");
663
664 check("Invalid path 'a:\\'", "a:\\");
665 check("Invalid path 'a:.'", "a:.");
666 check("Invalid path 'a:..'", "a:..");
667 check("Invalid path 'a:/.'", "a:/.");
668 check("Invalid path 'a:/..'", "a:/..");
669 check("Invalid path '.'", ".");
670 check("Invalid path '..'", "..");
671 check("Invalid path '/.'", "/.");
672 check("Invalid path '/..'", "/..");
673
674 check("Too many / separators in path 'a://.'", "a://.");
675 check("Too many / separators in path 'a:../'", "a:../");
676 check("Too many / separators in path 'a:b/c'", "a:b/c");
677 check("Too many / separators in path 'a:/b/c'", "a:/b/c");
678 }
679
680 #[test]
681 fn test_location_leaf_name() {
682 assert_eq!(None, Location::new("drv:/").unwrap().leaf_name());
683 assert_eq!(None, Location::new("drv:").unwrap().leaf_name());
684
685 assert_eq!(Some("abc.txt"), Location::new("a:/abc.txt").unwrap().leaf_name());
686 assert_eq!(Some("abc.txt"), Location::new("a:abc.txt").unwrap().leaf_name());
687 assert_eq!(Some("abc.txt"), Location::new("abc.txt").unwrap().leaf_name());
688 }
689
690 fn drive_names(storage: &Storage) -> Vec<String> {
692 let mut names = storage.drives.keys().map(DriveKey::to_string).collect::<Vec<String>>();
693 names.sort();
694 names
695 }
696
697 #[test]
698 fn test_storage_default() {
699 let storage = Storage::default();
700 assert_eq!("MEMORY:/", storage.cwd());
701 }
702
703 #[test]
704 fn test_storage_make_canonical_ok() {
705 let mut storage = Storage::default();
706 storage.mount("some", "memory://").unwrap();
707
708 assert_eq!("MEMORY:foo.bar", storage.make_canonical("foo.bar").unwrap());
709 assert_eq!("MEMORY:/foo.bar", storage.make_canonical("/foo.bar").unwrap());
710 storage.cd("some:/").unwrap();
711 assert_eq!("SOME:foo.bar", storage.make_canonical("foo.bar").unwrap());
712 assert_eq!("SOME:/foo.bar", storage.make_canonical("/foo.bar").unwrap());
713
714 assert_eq!("MEMORY:a", storage.make_canonical("memory:a").unwrap());
715 assert_eq!("MEMORY:/Abc", storage.make_canonical("memory:/Abc").unwrap());
716 assert_eq!("OTHER:a", storage.make_canonical("Other:a").unwrap());
717 assert_eq!("OTHER:/Abc", storage.make_canonical("Other:/Abc").unwrap());
718 }
719
720 #[test]
721 fn test_storage_make_canonical_errors() {
722 let storage = Storage::default();
723 assert_eq!(
724 "Invalid drive name 'a\\b'",
725 format!("{}", storage.make_canonical("a\\b:c").unwrap_err())
726 );
727 }
728
729 #[test]
730 fn test_storage_attach_ok() {
731 let mut storage = Storage::default();
732 storage.attach("zzz1", "z://", Box::from(InMemoryDrive::default())).unwrap();
733 storage.attach("A4", "z://", Box::from(InMemoryDrive::default())).unwrap();
734
735 assert_eq!("MEMORY:/", storage.cwd());
736 assert_eq!(["A4", "MEMORY", "ZZZ1"], drive_names(&storage).as_slice());
737 }
738
739 #[test]
740 fn test_storage_attach_invalid_name() {
741 let mut storage = Storage::default();
742 assert_eq!(
743 "Invalid drive name 'a:b'",
744 format!(
745 "{}",
746 storage.attach("a:b", "z://", Box::from(InMemoryDrive::default())).unwrap_err()
747 )
748 );
749 }
750
751 #[test]
752 fn test_storage_attach_already_attached() {
753 let mut storage = Storage::default();
754 assert_eq!(
755 "Drive 'memory' is already mounted",
756 format!(
757 "{}",
758 storage.attach("memory", "z://", Box::from(InMemoryDrive::default())).unwrap_err()
759 )
760 );
761
762 storage.attach("new", "z://", Box::from(InMemoryDrive::default())).unwrap();
763 assert_eq!(
764 "Drive 'New' is already mounted",
765 format!(
766 "{}",
767 storage.attach("New", "z://", Box::from(InMemoryDrive::default())).unwrap_err()
768 )
769 );
770 }
771
772 #[test]
773 fn test_storage_has_scheme() {
774 let mut storage = Storage::default();
775 storage.register_scheme("fake", Box::from(InMemoryDriveFactory::default()));
776 assert!(storage.has_scheme("fake"));
777 assert!(!storage.has_scheme("fakes"));
778 }
779
780 #[test]
781 fn test_storage_mount_ok() {
782 let mut storage = Storage::default();
783 storage.register_scheme("fake", Box::from(InMemoryDriveFactory::default()));
784 storage.mount("a", "memory://").unwrap();
785 storage.mount("z", "fAkE://").unwrap();
786
787 assert_eq!(["A", "MEMORY", "Z"], drive_names(&storage).as_slice());
788 }
789
790 #[test]
791 fn test_storage_mount_path_redirection() {
792 let root = tempfile::tempdir().unwrap();
793 let dir1 = root.path().join("first");
794 let dir2 = root.path().join("second");
795
796 let mut storage = Storage::default();
797 storage.register_scheme("file", Box::from(DirectoryDriveFactory::default()));
798 storage.mount("c", &format!("file://{}", dir1.display())).unwrap();
799 storage.mount("d", &format!("file://{}", dir2.display())).unwrap();
800
801 block_on(storage.put("c:file1.txt", "hi")).unwrap();
802 block_on(storage.put("d:file2.txt", "bye")).unwrap();
803
804 assert!(dir1.join("file1.txt").exists());
805 assert!(!dir1.join("file2.txt").exists());
806 assert!(!dir2.join("file1.txt").exists());
807 assert!(dir2.join("file2.txt").exists());
808 }
809
810 #[test]
811 fn test_storage_mount_unknown_scheme() {
812 let mut storage = Storage::default();
813 assert_eq!(
814 "Unknown mount scheme 'fake'",
815 format!("{}", storage.mount("a", "fake://abc").unwrap_err())
816 );
817 }
818
819 #[test]
820 fn test_storage_mount_invalid_path_for_scheme() {
821 let mut storage = Storage::default();
822 assert_eq!(
823 "Cannot specify a path to mount an in-memory drive",
824 format!("{}", storage.mount("a", "memory://abc").unwrap_err())
825 );
826 }
827
828 #[test]
829 fn test_storage_unmount_ok() {
830 let mut storage = Storage::default();
831 storage.mount("other", "memory://").unwrap();
832 assert_eq!("MEMORY:/", storage.cwd());
833 assert_eq!(["MEMORY", "OTHER"], drive_names(&storage).as_slice());
834
835 storage.unmount("other").unwrap();
836 assert_eq!("MEMORY:/", storage.cwd());
837 assert_eq!(["MEMORY"], drive_names(&storage).as_slice());
838 }
839
840 #[test]
841 fn test_storage_unmount_not_mounted_error() {
842 let mut storage = Storage::default();
843 assert_eq!(
844 "Drive 'foo' is not mounted",
845 format!("{}", storage.unmount("foo").unwrap_err())
846 );
847 }
848
849 #[test]
850 fn test_storage_unmount_current_drive_error() {
851 let mut storage = Storage::default();
852 storage.mount("other", "memory://").unwrap();
853 assert_eq!(
854 "Cannot unmount the current drive 'memory'",
855 format!("{}", storage.unmount("memory").unwrap_err())
856 );
857 storage.cd("other:").unwrap();
858 storage.unmount("memory").unwrap();
859 assert_eq!("OTHER:/", storage.cwd());
860 assert_eq!(["OTHER"], drive_names(&storage).as_slice());
861 }
862
863 #[test]
864 fn test_storage_mounted() {
865 let mut storage = Storage::default();
866 storage.register_scheme("fake", Box::from(InMemoryDriveFactory::default()));
867 storage.mount("z", "fAkE://").unwrap();
868
869 let mut exp_info = BTreeMap::default();
870 exp_info.insert("MEMORY", "memory://");
871 exp_info.insert("Z", "fAkE://");
872 assert_eq!(exp_info, storage.mounted());
873 }
874
875 #[test]
876 fn test_storage_cd_and_cwd_ok() {
877 let mut storage = Storage::default();
878 storage.mount("other", "memory://").unwrap();
879 assert_eq!("MEMORY:/", storage.cwd());
880 storage.cd("other:/").unwrap();
881 assert_eq!("OTHER:/", storage.cwd());
882 storage.cd("memory:").unwrap();
883 assert_eq!("MEMORY:/", storage.cwd());
884 }
885
886 #[test]
887 fn test_storage_cd_errors() {
888 let mut storage = Storage::default();
889 assert_eq!("Invalid drive name ''", format!("{}", storage.cd(":foo").unwrap_err()));
890 assert_eq!("Invalid path 'a:b\\c'", format!("{}", storage.cd("a:b\\c").unwrap_err()));
891 assert_eq!("Cannot cd to a file", format!("{}", storage.cd("foo:bar.bas").unwrap_err()));
892 assert_eq!("Drive 'A' is not mounted", format!("{}", storage.cd("a:").unwrap_err()));
893 }
894
895 #[test]
896 fn test_storage_file_ops_with_absolute_paths() {
897 let mut storage = Storage::default();
898 storage.mount("other", "memory://").unwrap();
899
900 block_on(storage.put("other:/f1", "some text")).unwrap();
901 block_on(storage.put("other:f2", "other text")).unwrap();
902 {
903 let memory_drive = storage.drives.get(&DriveKey::new("memory").unwrap()).unwrap();
905 assert_eq!(0, block_on(memory_drive.drive.enumerate()).unwrap().dirents().len());
906 let other_drive = storage.drives.get(&DriveKey::new("other").unwrap()).unwrap();
907 assert_eq!(2, block_on(other_drive.drive.enumerate()).unwrap().dirents().len());
908 }
909
910 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
911 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
912 assert_eq!(2, block_on(storage.enumerate("other:/")).unwrap().dirents().len());
913 assert_eq!(2, block_on(storage.enumerate("other:/")).unwrap().dirents().len());
914
915 assert_eq!("some text", block_on(storage.get("OTHER:f1")).unwrap());
916 assert_eq!("other text", block_on(storage.get("OTHER:/f2")).unwrap());
917
918 block_on(storage.delete("other:/f2")).unwrap();
919 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
920 assert_eq!(1, block_on(storage.enumerate("other:")).unwrap().dirents().len());
921 block_on(storage.delete("other:f1")).unwrap();
922 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
923 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
924 }
925
926 #[test]
927 fn test_storage_file_ops_with_relative_paths() {
928 let mut storage = Storage::default();
929 storage.mount("other", "memory://").unwrap();
930
931 block_on(storage.put("/f1", "some text")).unwrap();
932 block_on(storage.put("f2", "other text")).unwrap();
933 {
934 let memory_drive = storage.drives.get(&DriveKey::new("memory").unwrap()).unwrap();
936 assert_eq!(2, block_on(memory_drive.drive.enumerate()).unwrap().dirents().len());
937 let other_drive = storage.drives.get(&DriveKey::new("other").unwrap()).unwrap();
938 assert_eq!(0, block_on(other_drive.drive.enumerate()).unwrap().dirents().len());
939 }
940
941 assert_eq!(2, block_on(storage.enumerate("")).unwrap().dirents().len());
942 assert_eq!(2, block_on(storage.enumerate("/")).unwrap().dirents().len());
943 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
944 assert_eq!(0, block_on(storage.enumerate("other:/")).unwrap().dirents().len());
945
946 assert_eq!("some text", block_on(storage.get("f1")).unwrap());
947 assert_eq!("other text", block_on(storage.get("/f2")).unwrap());
948
949 block_on(storage.delete("/f2")).unwrap();
950 assert_eq!(1, block_on(storage.enumerate("")).unwrap().dirents().len());
951 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
952 block_on(storage.delete("f1")).unwrap();
953 assert_eq!(0, block_on(storage.enumerate("")).unwrap().dirents().len());
954 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
955 }
956
957 #[test]
958 fn test_storage_delete_errors() {
959 let mut storage = Storage::default();
960 assert_eq!(
961 "Invalid drive name ''",
962 format!("{}", block_on(storage.delete(":foo")).unwrap_err())
963 );
964 assert_eq!(
965 "Invalid path 'a:b\\c'",
966 format!("{}", block_on(storage.delete("a:b\\c")).unwrap_err())
967 );
968 assert_eq!(
969 "Missing file name in path 'a:'",
970 format!("{}", block_on(storage.delete("a:")).unwrap_err())
971 );
972 }
973
974 #[test]
975 fn test_storage_enumerate_errors() {
976 let storage = Storage::default();
977 assert_eq!(
978 "Invalid drive name ''",
979 format!("{}", block_on(storage.enumerate(":foo")).unwrap_err())
980 );
981 assert_eq!(
982 "Invalid path 'a:b\\c'",
983 format!("{}", block_on(storage.enumerate("a:b\\c")).unwrap_err())
984 );
985 assert_eq!(
986 "Location 'a:/foo' is not a directory",
987 format!("{}", block_on(storage.enumerate("a:/foo")).unwrap_err())
988 );
989 }
990
991 #[test]
992 fn test_storage_get_errors() {
993 let storage = Storage::default();
994 assert_eq!(
995 "Invalid drive name ''",
996 format!("{}", block_on(storage.get(":foo")).unwrap_err())
997 );
998 assert_eq!(
999 "Invalid path 'a:b\\c'",
1000 format!("{}", block_on(storage.get("a:b\\c")).unwrap_err())
1001 );
1002 assert_eq!(
1003 "Missing file name in path 'a:'",
1004 format!("{}", block_on(storage.get("a:")).unwrap_err())
1005 );
1006 }
1007
1008 #[test]
1009 fn test_storage_put_errors() {
1010 let mut storage = Storage::default();
1011 assert_eq!(
1012 "Invalid drive name ''",
1013 format!("{}", block_on(storage.put(":foo", "")).unwrap_err())
1014 );
1015 assert_eq!(
1016 "Invalid path 'a:b\\c'",
1017 format!("{}", block_on(storage.put("a:b\\c", "")).unwrap_err())
1018 );
1019 assert_eq!(
1020 "Missing file name in path 'a:'",
1021 format!("{}", block_on(storage.put("a:", "")).unwrap_err())
1022 );
1023 }
1024
1025 #[test]
1026 fn test_storage_system_path_ok() {
1027 let dir = tempfile::tempdir().unwrap();
1028 let dir = dir.path().canonicalize().unwrap();
1029
1030 let mut storage = Storage::default();
1031 storage
1032 .attach(
1033 "c",
1034 &format!("file://{}", dir.display()),
1035 Box::from(DirectoryDrive::new(dir.clone()).unwrap()),
1036 )
1037 .unwrap();
1038
1039 assert!(storage.system_path("memory:/foo").unwrap().is_none());
1040 assert_eq!(dir.join("some name"), storage.system_path("c:/some name").unwrap().unwrap());
1041 assert_eq!(dir.join("xyz"), storage.system_path("c:xyz").unwrap().unwrap());
1042 }
1043
1044 #[test]
1045 fn test_storage_system_path_of_cwd() {
1046 let dir = tempfile::tempdir().unwrap();
1047 let dir = dir.path().canonicalize().unwrap();
1048
1049 let mut storage = Storage::default();
1050 storage
1051 .attach(
1052 "c",
1053 &format!("file://{}", dir.display()),
1054 Box::from(DirectoryDrive::new(dir.clone()).unwrap()),
1055 )
1056 .unwrap();
1057
1058 assert!(storage.system_path(&storage.cwd()).unwrap().is_none());
1059
1060 storage.cd("c:/").unwrap();
1061 assert_eq!(dir, storage.system_path(&storage.cwd()).unwrap().unwrap());
1062 }
1063
1064 #[test]
1065 fn test_storage_system_path_errors() {
1066 let dir = tempfile::tempdir().unwrap();
1067 let dir = dir.path();
1068
1069 let mut storage = Storage::default();
1070 storage
1071 .attach(
1072 "c",
1073 &format!("file://{}", dir.display()),
1074 Box::from(DirectoryDrive::new(dir).unwrap()),
1075 )
1076 .unwrap();
1077
1078 assert_eq!(
1079 "Too many / separators in path 'c:a/b'",
1080 format!("{}", storage.system_path("c:a/b").unwrap_err())
1081 );
1082 assert_eq!("Invalid path 'c:..'", format!("{}", storage.system_path("c:..").unwrap_err()));
1083 }
1084}