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::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<Vec<u8>>;
154
155 async fn get_acls(&self, _name: &str) -> io::Result<FileAcls> {
157 Err(io::Error::other("Operation not supported by drive"))
158 }
159
160 async fn put(&mut self, name: &str, content: &[u8]) -> 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::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 fn set_leaf_name(&mut self, name: &str) {
286 if self.path.starts_with('/') {
287 self.path.clear();
288 self.path.push('/');
289 self.path.push_str(name);
290 } else {
291 self.path.clear();
292 self.path.push_str(name);
293 }
294 }
295
296 fn set_extension(&mut self, extension: &str) {
301 debug_assert_eq!(extension, extension.to_ascii_lowercase());
302
303 if let Some(name) = self.leaf_name() {
304 if name.is_empty() {
305 return;
306 }
307
308 if name.rfind('.').is_some() {
309 return;
310 }
311
312 let mut as_uppercase = true;
316 for ch in name.chars() {
317 if ch.is_ascii_lowercase() {
318 as_uppercase = false;
319 break;
320 }
321 }
322
323 self.path.push('.');
324 if as_uppercase {
325 self.path.push_str(&extension.to_ascii_uppercase());
326 } else {
327 self.path.push_str(extension);
328 }
329 }
330 }
331}
332
333impl fmt::Display for Location {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 match &self.drive {
336 Some(drive) => write!(f, "{}:{}", drive.0, self.path),
337 None => self.path.fmt(f),
338 }
339 }
340}
341
342pub trait DriveFactory {
344 fn create(&self, target: &str) -> io::Result<Box<dyn Drive>>;
346}
347
348fn split_uri(uri: &str) -> io::Result<(&str, &str)> {
350 match uri.find("://") {
351 Some(pos) if pos > 0 => Ok((&uri[0..pos], &uri[pos + 3..])),
352 _ => Err(io::Error::new(
353 io::ErrorKind::InvalidInput,
354 "Mount URI must be of the form scheme://path",
355 )),
356 }
357}
358
359struct MountedDrive {
361 uri: String,
362 drive: Box<dyn Drive>,
363}
364
365pub struct Storage {
370 factories: HashMap<String, Box<dyn DriveFactory>>,
372
373 drives: HashMap<DriveKey, MountedDrive>,
375
376 current: DriveKey,
378}
379
380impl Default for Storage {
381 fn default() -> Self {
383 let mut factories: HashMap<String, Box<dyn DriveFactory>> = HashMap::default();
384 factories.insert("memory".to_owned(), Box::from(InMemoryDriveFactory::default()));
385
386 let drive: Box<dyn Drive> = Box::from(InMemoryDrive::default());
387
388 let mut drives = HashMap::new();
389 let key = DriveKey::new("MEMORY").expect("Hardcoded drive name must be valid");
390 let mounted_drive = MountedDrive { uri: "memory://".to_owned(), drive };
391 drives.insert(key.clone(), mounted_drive);
392 Self { factories, drives, current: key }
393 }
394}
395
396impl Storage {
397 pub fn register_scheme(&mut self, scheme: &str, factory: Box<dyn DriveFactory>) {
400 assert_eq!(scheme.to_lowercase(), scheme);
401 let previous = self.factories.insert(scheme.to_owned(), factory);
402 assert!(previous.is_none(), "Tried to register {} twice", scheme);
403 }
404
405 pub fn has_scheme(&self, scheme: &str) -> bool {
407 self.factories.contains_key(scheme)
408 }
409
410 pub fn make_canonical(&self, raw_location: &str) -> io::Result<String> {
412 let mut location = Location::new(raw_location)?;
413 if location.drive.is_none() {
414 location.drive = Some(self.current.clone());
415 }
416 Ok(location.to_string())
417 }
418
419 pub fn make_canonical_with_extension(
424 &self,
425 raw_location: &str,
426 extension: &str,
427 ) -> io::Result<String> {
428 let mut location = Location::new(raw_location)?;
429 if location.drive.is_none() {
430 location.drive = Some(self.current.clone());
431 }
432 if location.leaf_name().is_none() {
433 return Err(io::Error::new(
434 io::ErrorKind::NotFound,
435 format!("Missing file name in path '{}'", raw_location),
436 ));
437 }
438 location.set_extension(extension);
439 Ok(location.to_string())
440 }
441
442 fn attach(&mut self, name: &str, uri: &str, drive: Box<dyn Drive>) -> io::Result<()> {
446 let key = DriveKey::new(name)?;
447 if self.drives.contains_key(&key) {
448 return Err(io::Error::new(
449 io::ErrorKind::AlreadyExists,
450 format!("Drive '{}' is already mounted", name),
451 ));
452 }
453 let mounted_drive = MountedDrive { uri: uri.to_owned(), drive };
454 self.drives.insert(DriveKey::new(name)?, mounted_drive);
455 Ok(())
456 }
457
458 pub fn mount(&mut self, name: &str, uri: &str) -> io::Result<()> {
462 let (scheme, path) = split_uri(uri)?;
463 let drive = match self.factories.get(&scheme.to_lowercase()) {
464 Some(factory) => factory.create(path)?,
465 None => {
466 return Err(io::Error::new(
467 io::ErrorKind::InvalidInput,
468 format!("Unknown mount scheme '{}'", scheme),
469 ));
470 }
471 };
472 self.attach(name, uri, drive)
473 }
474
475 pub fn unmount(&mut self, name: &str) -> io::Result<()> {
480 let key = DriveKey::new(name)?;
481 if !self.drives.contains_key(&key) {
482 return Err(io::Error::new(
483 io::ErrorKind::NotFound,
484 format!("Drive '{}' is not mounted", name),
485 ));
486 }
487 if self.current == key {
488 return Err(io::Error::new(
489 io::ErrorKind::AlreadyExists,
490 format!("Cannot unmount the current drive '{}'", name),
491 ));
492 }
493 assert!(
494 self.drives.len() > 1,
495 "There must be more than one drive if the current drive is not the given name"
496 );
497 self.drives.remove(&key).expect("Drive presence in map checked above");
498 Ok(())
499 }
500
501 pub fn mounted(&self) -> BTreeMap<&str, &str> {
504 let mut info = BTreeMap::new();
505 for (name, mounted_drive) in &self.drives {
506 info.insert(name.0.as_str(), mounted_drive.uri.as_str());
507 }
508 info
509 }
510
511 pub fn cd(&mut self, location: &str) -> io::Result<()> {
516 let location = Location::new(location)?;
517 if location.leaf_name().is_some() {
518 return Err(io::Error::new(io::ErrorKind::InvalidInput, "Cannot cd to a file"));
519 }
520
521 match location.drive {
522 Some(drive) => {
523 if !self.drives.contains_key(&drive) {
524 return Err(io::Error::new(
525 io::ErrorKind::AlreadyExists,
526 format!("Drive '{}' is not mounted", drive),
527 ));
528 }
529 self.current = drive;
530 Ok(())
531 }
532 None => Ok(()),
533 }
534 }
535
536 pub fn cwd(&self) -> String {
538 Location::with_drive_root(self.current.clone()).to_string()
539 }
540
541 fn get_drive(&self, location: &Location) -> io::Result<&dyn Drive> {
543 match &location.drive {
544 Some(key) => match self.drives.get(key) {
545 Some(mounted_drive) => Ok(mounted_drive.drive.as_ref()),
546 None => Err(io::Error::new(
547 io::ErrorKind::NotFound,
548 format!("Drive '{}' is not mounted", key),
549 )),
550 },
551 None => Ok(self
552 .drives
553 .get(&self.current)
554 .expect("Current drive out of sync")
555 .drive
556 .as_ref()),
557 }
558 }
559
560 fn get_drive_mut(&mut self, location: &Location) -> io::Result<&mut dyn Drive> {
562 match &location.drive {
563 Some(key) => match self.drives.get_mut(key) {
564 Some(mounted_drive) => Ok(mounted_drive.drive.as_mut()),
565 None => Err(io::Error::new(
566 io::ErrorKind::NotFound,
567 format!("Drive '{}' is not mounted", key),
568 )),
569 },
570 None => Ok(self
571 .drives
572 .get_mut(&self.current)
573 .expect("Current drive out of sync")
574 .drive
575 .as_mut()),
576 }
577 }
578
579 pub async fn delete(&mut self, raw_location: &str) -> io::Result<()> {
581 let location = Location::new(raw_location)?;
582 match location.leaf_name() {
583 Some(name) => self.get_drive_mut(&location)?.delete(name).await,
584 None => Err(io::Error::new(
585 io::ErrorKind::NotFound,
586 format!("Missing file name in path '{}'", raw_location),
587 )),
588 }
589 }
590
591 pub async fn enumerate(&self, raw_location: &str) -> io::Result<DriveFiles> {
593 let location = Location::new(raw_location)?;
594 match location.leaf_name() {
595 Some(_) => Err(io::Error::new(
596 io::ErrorKind::NotFound,
597 format!("Location '{}' is not a directory", raw_location),
598 )),
599 None => self.get_drive(&location)?.enumerate().await,
600 }
601 }
602
603 async fn get_location(&self, raw_location: &str, location: &Location) -> io::Result<Vec<u8>> {
606 match location.leaf_name() {
607 Some(name) => self.get_drive(location)?.get(name).await,
608 None => Err(io::Error::new(
609 io::ErrorKind::NotFound,
610 format!("Missing file name in path '{}'", raw_location),
611 )),
612 }
613 }
614
615 pub async fn get(&self, raw_location: &str) -> io::Result<Vec<u8>> {
617 let location = Location::new(raw_location)?;
618 self.get_location(raw_location, &location).await
619 }
620
621 pub async fn get_acls(&self, raw_location: &str) -> io::Result<FileAcls> {
623 let location = Location::new(raw_location)?;
624 match location.leaf_name() {
625 Some(name) => self.get_drive(&location)?.get_acls(name).await,
626 None => Err(io::Error::new(
627 io::ErrorKind::NotFound,
628 format!("Missing file name in path '{}'", raw_location),
629 )),
630 }
631 }
632
633 async fn put_location(
636 &mut self,
637 raw_location: &str,
638 location: &Location,
639 content: &[u8],
640 ) -> io::Result<()> {
641 match location.leaf_name() {
642 Some(name) => self.get_drive_mut(location)?.put(name, content).await,
643 None => Err(io::Error::new(
644 io::ErrorKind::NotFound,
645 format!("Missing file name in path '{}'", raw_location),
646 )),
647 }
648 }
649
650 pub async fn put(&mut self, raw_location: &str, content: &[u8]) -> io::Result<()> {
652 let location = Location::new(raw_location)?;
653 self.put_location(raw_location, &location, content).await
654 }
655
656 pub async fn update_acls(
659 &mut self,
660 raw_location: &str,
661 add: &FileAcls,
662 remove: &FileAcls,
663 ) -> io::Result<()> {
664 let location = Location::new(raw_location)?;
665 match location.leaf_name() {
666 Some(name) => self.get_drive_mut(&location)?.update_acls(name, add, remove).await,
667 None => Err(io::Error::new(
668 io::ErrorKind::NotFound,
669 format!("Missing file name in path '{}'", raw_location),
670 )),
671 }
672 }
673
674 pub fn system_path(&self, raw_location: &str) -> io::Result<Option<PathBuf>> {
676 let location = Location::new(raw_location)?;
677 match location.leaf_name() {
678 Some(name) => Ok(self.get_drive(&location)?.system_path(name)),
679 None => Ok(self.get_drive(&location)?.system_path("")),
680 }
681 }
682
683 pub async fn copy(&mut self, raw_src: &str, raw_dest: &str) -> io::Result<()> {
685 let src = Location::new(raw_src)?;
686 let src_name = match src.leaf_name() {
687 Some(name) => name,
688 None => {
689 return Err(io::Error::new(
690 io::ErrorKind::NotFound,
691 format!("Missing file name in copy source path '{}'", raw_src),
692 ));
693 }
694 };
695
696 let mut dest = Location::new(raw_dest)?;
697 if dest.leaf_name().is_none() {
698 dest.set_leaf_name(src_name);
699 }
700
701 let content = self.get_location(raw_src, &src).await?;
702 self.put_location(raw_dest, &dest, &content).await
703 }
704}
705
706#[cfg(test)]
707mod tests {
708 use super::*;
709 use futures_lite::future::block_on;
710
711 #[test]
712 fn test_split_uri_ok() {
713 assert_eq!(("the-scheme", ""), split_uri("the-scheme://").unwrap());
714 assert_eq!(("foo", "/some:/target"), split_uri("foo:///some:/target").unwrap());
715 assert_eq!(("foo", "bar://baz"), split_uri("foo://bar://baz").unwrap());
716 }
717
718 #[test]
719 fn test_split_uri_errors() {
720 for uri in &["", "://abc", "foo:/", "bar//", "/baz/"] {
721 assert_eq!(
722 "Mount URI must be of the form scheme://path",
723 format!("{}", split_uri(uri).unwrap_err())
724 );
725 }
726 }
727
728 #[test]
729 fn test_drivekey_new() {
730 assert_eq!(DriveKey("FOO".to_owned()), DriveKey::new("foo").unwrap());
731 assert_eq!(DriveKey("A".to_owned()), DriveKey::new("A".to_owned()).unwrap());
732 }
733
734 #[test]
735 fn test_drivekey_errors() {
736 assert_eq!("Invalid drive name ''", format!("{}", DriveKey::new("").unwrap_err()));
737 assert_eq!("Invalid drive name 'a:b'", format!("{}", DriveKey::new("a:b").unwrap_err()));
738 assert_eq!("Invalid drive name 'a\\b'", format!("{}", DriveKey::new("a\\b").unwrap_err()));
739 assert_eq!("Invalid drive name 'a/b'", format!("{}", DriveKey::new("a/b").unwrap_err()));
740 assert_eq!("Invalid drive name 'a.b'", format!("{}", DriveKey::new("a.b").unwrap_err()));
741 }
742
743 #[test]
744 fn test_location_new_ok() {
745 fn check(exp_drive: Option<&str>, exp_path: &str, input: &str) {
746 let rp = Location::new(input).unwrap();
747 assert_eq!(exp_drive.map(|s| DriveKey::new(s).unwrap()), rp.drive);
748 assert_eq!(exp_path, rp.path);
749 }
750
751 check(None, "/", "/");
752 check(None, "/foo.bas", "/foo.bas");
753
754 check(Some("A"), "/", "a:");
755 check(Some("ABC"), "/foo.bas", "abc:/foo.bas");
756 check(Some("ABC"), "Foo.Bas", "abc:Foo.Bas");
757 }
758
759 #[test]
760 fn test_location_new_errors() {
761 fn check(exp_error: &str, input: &str) {
762 let e = Location::new(input).unwrap_err();
763 assert_eq!(io::ErrorKind::InvalidInput, e.kind());
764 assert_eq!(exp_error, format!("{}", e));
765 }
766
767 check("Too many : separators in path 'a:b:c'", "a:b:c");
768
769 check("Invalid drive name ''", ":");
770 check("Invalid drive name 'a\\b'", "a\\b:");
771 check("Invalid drive name 'a/b'", "a/b:");
772 check("Invalid drive name 'a.b'", "a.b:");
773
774 check("Invalid path 'a:\\'", "a:\\");
775 check("Invalid path 'a:.'", "a:.");
776 check("Invalid path 'a:..'", "a:..");
777 check("Invalid path 'a:/.'", "a:/.");
778 check("Invalid path 'a:/..'", "a:/..");
779 check("Invalid path '.'", ".");
780 check("Invalid path '..'", "..");
781 check("Invalid path '/.'", "/.");
782 check("Invalid path '/..'", "/..");
783
784 check("Too many / separators in path 'a://.'", "a://.");
785 check("Too many / separators in path 'a:../'", "a:../");
786 check("Too many / separators in path 'a:b/c'", "a:b/c");
787 check("Too many / separators in path 'a:/b/c'", "a:/b/c");
788 }
789
790 #[test]
791 fn test_location_leaf_name() {
792 assert_eq!(None, Location::new("drv:/").unwrap().leaf_name());
793 assert_eq!(None, Location::new("drv:").unwrap().leaf_name());
794
795 assert_eq!(Some("abc.txt"), Location::new("a:/abc.txt").unwrap().leaf_name());
796 assert_eq!(Some("abc.txt"), Location::new("a:abc.txt").unwrap().leaf_name());
797 assert_eq!(Some("abc.txt"), Location::new("abc.txt").unwrap().leaf_name());
798 }
799
800 #[test]
801 fn test_location_set_leaf_name() {
802 let mut location = Location::new("drv:/").unwrap();
803 location.set_leaf_name("foo");
804 assert_eq!("DRV:/foo", format!("{}", location));
805
806 let mut location = Location::new("drv:").unwrap();
807 location.set_leaf_name("foo");
808 assert_eq!("DRV:/foo", format!("{}", location));
809
810 let mut location = Location::new("/bar").unwrap();
811 location.set_leaf_name("foo");
812 assert_eq!("/foo", format!("{}", location));
813
814 let mut location = Location::new("bar").unwrap();
815 location.set_leaf_name("foo");
816 assert_eq!("foo", format!("{}", location));
817 }
818
819 #[test]
820 fn test_location_set_extension() {
821 for (exp_location, raw_location, extension) in [
822 ("DRV:/", "drv:", "bas"),
823 ("DRV:/", "drv:/", "bas"),
824 ("foo.bas", "foo", "bas"),
825 ("foo.bas", "foo.bas", "bas"),
826 ("Foo.bas", "Foo", "bas"),
827 ("FOO.BAS", "FOO", "bas"),
828 ("foo.", "foo.", "bas"),
829 ("foo.other", "foo.other", "bas"),
830 ] {
831 let mut location = Location::new(raw_location).unwrap();
832 location.set_extension(extension);
833 assert_eq!(exp_location, location.to_string());
834 }
835 }
836
837 fn drive_names(storage: &Storage) -> Vec<String> {
839 let mut names = storage.drives.keys().map(DriveKey::to_string).collect::<Vec<String>>();
840 names.sort();
841 names
842 }
843
844 #[test]
845 fn test_storage_default() {
846 let storage = Storage::default();
847 assert_eq!("MEMORY:/", storage.cwd());
848 }
849
850 #[test]
851 fn test_storage_make_canonical_ok() {
852 let mut storage = Storage::default();
853 storage.mount("some", "memory://").unwrap();
854
855 assert_eq!("MEMORY:/", storage.make_canonical("memory:").unwrap());
856
857 assert_eq!("MEMORY:foo.bar", storage.make_canonical("foo.bar").unwrap());
858 assert_eq!("MEMORY:/foo.bar", storage.make_canonical("/foo.bar").unwrap());
859 storage.cd("some:/").unwrap();
860 assert_eq!("SOME:foo.bar", storage.make_canonical("foo.bar").unwrap());
861 assert_eq!("SOME:/foo.bar", storage.make_canonical("/foo.bar").unwrap());
862
863 assert_eq!("MEMORY:a", storage.make_canonical("memory:a").unwrap());
864 assert_eq!("MEMORY:/Abc", storage.make_canonical("memory:/Abc").unwrap());
865 assert_eq!("OTHER:a", storage.make_canonical("Other:a").unwrap());
866 assert_eq!("OTHER:/Abc", storage.make_canonical("Other:/Abc").unwrap());
867 }
868
869 #[test]
870 fn test_storage_make_canonical_errors() {
871 let storage = Storage::default();
872 assert_eq!(
873 "Invalid drive name 'a\\b'",
874 format!("{}", storage.make_canonical("a\\b:c").unwrap_err())
875 );
876 }
877
878 #[test]
879 fn test_storage_make_canonical_with_extension_ok() {
880 let mut storage = Storage::default();
881 storage.mount("some", "memory://").unwrap();
882
883 assert_eq!("MEMORY:foo.bas", storage.make_canonical_with_extension("foo", "bas").unwrap());
884 assert_eq!(
885 "MEMORY:/foo.bar",
886 storage.make_canonical_with_extension("/foo.bar", "bas").unwrap()
887 );
888
889 assert_eq!(
890 "MEMORY:a.bas",
891 storage.make_canonical_with_extension("memory:a", "bas").unwrap()
892 );
893 assert_eq!(
894 "MEMORY:/a.bas",
895 storage.make_canonical_with_extension("memory:/a.bas", "bas").unwrap()
896 );
897 }
898
899 #[test]
900 fn test_storage_make_canonical_with_extension_errors() {
901 let storage = Storage::default();
902
903 assert_eq!(
904 "Missing file name in path 'memory:'",
905 format!("{}", storage.make_canonical_with_extension("memory:", "bas").unwrap_err()),
906 );
907
908 assert_eq!(
909 "Invalid drive name 'a\\b'",
910 format!("{}", storage.make_canonical_with_extension("a\\b:c", "bas").unwrap_err())
911 );
912 }
913
914 #[test]
915 fn test_storage_attach_ok() {
916 let mut storage = Storage::default();
917 storage.attach("zzz1", "z://", Box::from(InMemoryDrive::default())).unwrap();
918 storage.attach("A4", "z://", Box::from(InMemoryDrive::default())).unwrap();
919
920 assert_eq!("MEMORY:/", storage.cwd());
921 assert_eq!(["A4", "MEMORY", "ZZZ1"], drive_names(&storage).as_slice());
922 }
923
924 #[test]
925 fn test_storage_attach_invalid_name() {
926 let mut storage = Storage::default();
927 assert_eq!(
928 "Invalid drive name 'a:b'",
929 format!(
930 "{}",
931 storage.attach("a:b", "z://", Box::from(InMemoryDrive::default())).unwrap_err()
932 )
933 );
934 }
935
936 #[test]
937 fn test_storage_attach_already_attached() {
938 let mut storage = Storage::default();
939 assert_eq!(
940 "Drive 'memory' is already mounted",
941 format!(
942 "{}",
943 storage.attach("memory", "z://", Box::from(InMemoryDrive::default())).unwrap_err()
944 )
945 );
946
947 storage.attach("new", "z://", Box::from(InMemoryDrive::default())).unwrap();
948 assert_eq!(
949 "Drive 'New' is already mounted",
950 format!(
951 "{}",
952 storage.attach("New", "z://", Box::from(InMemoryDrive::default())).unwrap_err()
953 )
954 );
955 }
956
957 #[test]
958 fn test_storage_has_scheme() {
959 let mut storage = Storage::default();
960 storage.register_scheme("fake", Box::from(InMemoryDriveFactory::default()));
961 assert!(storage.has_scheme("fake"));
962 assert!(!storage.has_scheme("fakes"));
963 }
964
965 #[test]
966 fn test_storage_mount_ok() {
967 let mut storage = Storage::default();
968 storage.register_scheme("fake", Box::from(InMemoryDriveFactory::default()));
969 storage.mount("a", "memory://").unwrap();
970 storage.mount("z", "fAkE://").unwrap();
971
972 assert_eq!(["A", "MEMORY", "Z"], drive_names(&storage).as_slice());
973 }
974
975 #[test]
976 fn test_storage_mount_path_redirection() {
977 let root = tempfile::tempdir().unwrap();
978 let dir1 = root.path().join("first");
979 let dir2 = root.path().join("second");
980
981 let mut storage = Storage::default();
982 storage.register_scheme("file", Box::from(DirectoryDriveFactory::default()));
983 storage.mount("c", &format!("file://{}", dir1.display())).unwrap();
984 storage.mount("d", &format!("file://{}", dir2.display())).unwrap();
985
986 block_on(storage.put("c:file1.txt", b"hi")).unwrap();
987 block_on(storage.put("d:file2.txt", b"bye")).unwrap();
988
989 assert!(dir1.join("file1.txt").exists());
990 assert!(!dir1.join("file2.txt").exists());
991 assert!(!dir2.join("file1.txt").exists());
992 assert!(dir2.join("file2.txt").exists());
993 }
994
995 #[test]
996 fn test_storage_mount_unknown_scheme() {
997 let mut storage = Storage::default();
998 assert_eq!(
999 "Unknown mount scheme 'fake'",
1000 format!("{}", storage.mount("a", "fake://abc").unwrap_err())
1001 );
1002 }
1003
1004 #[test]
1005 fn test_storage_mount_invalid_path_for_scheme() {
1006 let mut storage = Storage::default();
1007 assert_eq!(
1008 "Cannot specify a path to mount an in-memory drive",
1009 format!("{}", storage.mount("a", "memory://abc").unwrap_err())
1010 );
1011 }
1012
1013 #[test]
1014 fn test_storage_unmount_ok() {
1015 let mut storage = Storage::default();
1016 storage.mount("other", "memory://").unwrap();
1017 assert_eq!("MEMORY:/", storage.cwd());
1018 assert_eq!(["MEMORY", "OTHER"], drive_names(&storage).as_slice());
1019
1020 storage.unmount("other").unwrap();
1021 assert_eq!("MEMORY:/", storage.cwd());
1022 assert_eq!(["MEMORY"], drive_names(&storage).as_slice());
1023 }
1024
1025 #[test]
1026 fn test_storage_unmount_not_mounted_error() {
1027 let mut storage = Storage::default();
1028 assert_eq!(
1029 "Drive 'foo' is not mounted",
1030 format!("{}", storage.unmount("foo").unwrap_err())
1031 );
1032 }
1033
1034 #[test]
1035 fn test_storage_unmount_current_drive_error() {
1036 let mut storage = Storage::default();
1037 storage.mount("other", "memory://").unwrap();
1038 assert_eq!(
1039 "Cannot unmount the current drive 'memory'",
1040 format!("{}", storage.unmount("memory").unwrap_err())
1041 );
1042 storage.cd("other:").unwrap();
1043 storage.unmount("memory").unwrap();
1044 assert_eq!("OTHER:/", storage.cwd());
1045 assert_eq!(["OTHER"], drive_names(&storage).as_slice());
1046 }
1047
1048 #[test]
1049 fn test_storage_mounted() {
1050 let mut storage = Storage::default();
1051 storage.register_scheme("fake", Box::from(InMemoryDriveFactory::default()));
1052 storage.mount("z", "fAkE://").unwrap();
1053
1054 let mut exp_info = BTreeMap::default();
1055 exp_info.insert("MEMORY", "memory://");
1056 exp_info.insert("Z", "fAkE://");
1057 assert_eq!(exp_info, storage.mounted());
1058 }
1059
1060 #[test]
1061 fn test_storage_cd_and_cwd_ok() {
1062 let mut storage = Storage::default();
1063 storage.mount("other", "memory://").unwrap();
1064 assert_eq!("MEMORY:/", storage.cwd());
1065 storage.cd("other:/").unwrap();
1066 assert_eq!("OTHER:/", storage.cwd());
1067 storage.cd("memory:").unwrap();
1068 assert_eq!("MEMORY:/", storage.cwd());
1069 }
1070
1071 #[test]
1072 fn test_storage_cd_errors() {
1073 let mut storage = Storage::default();
1074 assert_eq!("Invalid drive name ''", format!("{}", storage.cd(":foo").unwrap_err()));
1075 assert_eq!("Invalid path 'a:b\\c'", format!("{}", storage.cd("a:b\\c").unwrap_err()));
1076 assert_eq!("Cannot cd to a file", format!("{}", storage.cd("foo:bar.bas").unwrap_err()));
1077 assert_eq!("Drive 'A' is not mounted", format!("{}", storage.cd("a:").unwrap_err()));
1078 }
1079
1080 #[test]
1081 fn test_storage_file_ops_with_absolute_paths() {
1082 let mut storage = Storage::default();
1083 storage.mount("other", "memory://").unwrap();
1084
1085 block_on(storage.put("other:/f1", b"some text")).unwrap();
1086 block_on(storage.put("other:f2", b"other text")).unwrap();
1087 {
1088 let memory_drive = storage.drives.get(&DriveKey::new("memory").unwrap()).unwrap();
1090 assert_eq!(0, block_on(memory_drive.drive.enumerate()).unwrap().dirents().len());
1091 let other_drive = storage.drives.get(&DriveKey::new("other").unwrap()).unwrap();
1092 assert_eq!(2, block_on(other_drive.drive.enumerate()).unwrap().dirents().len());
1093 }
1094
1095 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
1096 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
1097 assert_eq!(2, block_on(storage.enumerate("other:/")).unwrap().dirents().len());
1098 assert_eq!(2, block_on(storage.enumerate("other:/")).unwrap().dirents().len());
1099
1100 assert_eq!(b"some text", block_on(storage.get("OTHER:f1")).unwrap().as_slice());
1101 assert_eq!(b"other text", block_on(storage.get("OTHER:/f2")).unwrap().as_slice());
1102
1103 block_on(storage.delete("other:/f2")).unwrap();
1104 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
1105 assert_eq!(1, block_on(storage.enumerate("other:")).unwrap().dirents().len());
1106 block_on(storage.delete("other:f1")).unwrap();
1107 assert_eq!(0, block_on(storage.enumerate("memory:")).unwrap().dirents().len());
1108 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
1109 }
1110
1111 #[test]
1112 fn test_storage_file_ops_with_relative_paths() {
1113 let mut storage = Storage::default();
1114 storage.mount("other", "memory://").unwrap();
1115
1116 block_on(storage.put("/f1", b"some text")).unwrap();
1117 block_on(storage.put("f2", b"other text")).unwrap();
1118 {
1119 let memory_drive = storage.drives.get(&DriveKey::new("memory").unwrap()).unwrap();
1121 assert_eq!(2, block_on(memory_drive.drive.enumerate()).unwrap().dirents().len());
1122 let other_drive = storage.drives.get(&DriveKey::new("other").unwrap()).unwrap();
1123 assert_eq!(0, block_on(other_drive.drive.enumerate()).unwrap().dirents().len());
1124 }
1125
1126 assert_eq!(2, block_on(storage.enumerate("")).unwrap().dirents().len());
1127 assert_eq!(2, block_on(storage.enumerate("/")).unwrap().dirents().len());
1128 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
1129 assert_eq!(0, block_on(storage.enumerate("other:/")).unwrap().dirents().len());
1130
1131 assert_eq!(b"some text", block_on(storage.get("f1")).unwrap().as_slice());
1132 assert_eq!(b"other text", block_on(storage.get("/f2")).unwrap().as_slice());
1133
1134 block_on(storage.delete("/f2")).unwrap();
1135 assert_eq!(1, block_on(storage.enumerate("")).unwrap().dirents().len());
1136 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
1137 block_on(storage.delete("f1")).unwrap();
1138 assert_eq!(0, block_on(storage.enumerate("")).unwrap().dirents().len());
1139 assert_eq!(0, block_on(storage.enumerate("other:")).unwrap().dirents().len());
1140 }
1141
1142 #[test]
1143 fn test_storage_delete_errors() {
1144 let mut storage = Storage::default();
1145 assert_eq!(
1146 "Invalid drive name ''",
1147 format!("{}", block_on(storage.delete(":foo")).unwrap_err())
1148 );
1149 assert_eq!(
1150 "Invalid path 'a:b\\c'",
1151 format!("{}", block_on(storage.delete("a:b\\c")).unwrap_err())
1152 );
1153 assert_eq!(
1154 "Missing file name in path 'a:'",
1155 format!("{}", block_on(storage.delete("a:")).unwrap_err())
1156 );
1157 }
1158
1159 #[test]
1160 fn test_storage_enumerate_errors() {
1161 let storage = Storage::default();
1162 assert_eq!(
1163 "Invalid drive name ''",
1164 format!("{}", block_on(storage.enumerate(":foo")).unwrap_err())
1165 );
1166 assert_eq!(
1167 "Invalid path 'a:b\\c'",
1168 format!("{}", block_on(storage.enumerate("a:b\\c")).unwrap_err())
1169 );
1170 assert_eq!(
1171 "Location 'a:/foo' is not a directory",
1172 format!("{}", block_on(storage.enumerate("a:/foo")).unwrap_err())
1173 );
1174 }
1175
1176 #[test]
1177 fn test_storage_get_errors() {
1178 let storage = Storage::default();
1179 assert_eq!(
1180 "Invalid drive name ''",
1181 format!("{}", block_on(storage.get(":foo")).unwrap_err())
1182 );
1183 assert_eq!(
1184 "Invalid path 'a:b\\c'",
1185 format!("{}", block_on(storage.get("a:b\\c")).unwrap_err())
1186 );
1187 assert_eq!(
1188 "Missing file name in path 'a:'",
1189 format!("{}", block_on(storage.get("a:")).unwrap_err())
1190 );
1191 }
1192
1193 #[test]
1194 fn test_storage_put_errors() {
1195 let mut storage = Storage::default();
1196 assert_eq!(
1197 "Invalid drive name ''",
1198 format!("{}", block_on(storage.put(":foo", b"")).unwrap_err())
1199 );
1200 assert_eq!(
1201 "Invalid path 'a:b\\c'",
1202 format!("{}", block_on(storage.put("a:b\\c", b"")).unwrap_err())
1203 );
1204 assert_eq!(
1205 "Missing file name in path 'a:'",
1206 format!("{}", block_on(storage.put("a:", b"")).unwrap_err())
1207 );
1208 }
1209
1210 #[test]
1211 fn test_storage_system_path_ok() {
1212 let dir = tempfile::tempdir().unwrap();
1213 let dir = dir.path().canonicalize().unwrap();
1214
1215 let mut storage = Storage::default();
1216 storage
1217 .attach(
1218 "c",
1219 &format!("file://{}", dir.display()),
1220 Box::from(DirectoryDrive::new(dir.clone()).unwrap()),
1221 )
1222 .unwrap();
1223
1224 assert!(storage.system_path("memory:/foo").unwrap().is_none());
1225 assert_eq!(dir.join("some name"), storage.system_path("c:/some name").unwrap().unwrap());
1226 assert_eq!(dir.join("xyz"), storage.system_path("c:xyz").unwrap().unwrap());
1227 }
1228
1229 #[test]
1230 fn test_storage_system_path_of_cwd() {
1231 let dir = tempfile::tempdir().unwrap();
1232 let dir = dir.path().canonicalize().unwrap();
1233
1234 let mut storage = Storage::default();
1235 storage
1236 .attach(
1237 "c",
1238 &format!("file://{}", dir.display()),
1239 Box::from(DirectoryDrive::new(dir.clone()).unwrap()),
1240 )
1241 .unwrap();
1242
1243 assert!(storage.system_path(&storage.cwd()).unwrap().is_none());
1244
1245 storage.cd("c:/").unwrap();
1246 assert_eq!(dir, storage.system_path(&storage.cwd()).unwrap().unwrap());
1247 }
1248
1249 #[test]
1250 fn test_storage_system_path_errors() {
1251 let dir = tempfile::tempdir().unwrap();
1252 let dir = dir.path();
1253
1254 let mut storage = Storage::default();
1255 storage
1256 .attach(
1257 "c",
1258 &format!("file://{}", dir.display()),
1259 Box::from(DirectoryDrive::new(dir).unwrap()),
1260 )
1261 .unwrap();
1262
1263 assert_eq!(
1264 "Too many / separators in path 'c:a/b'",
1265 format!("{}", storage.system_path("c:a/b").unwrap_err())
1266 );
1267 assert_eq!("Invalid path 'c:..'", format!("{}", storage.system_path("c:..").unwrap_err()));
1268 }
1269}