file_storage/path/storage_path.rs
1/// A file or folder path.
2///
3/// # Base & Extension
4/// Storage paths are divided into a base and an extension. The base is effectively immutable, and
5/// the extension is a sequence of segments divided by file-separators.
6///
7/// Unix Example:
8/// Path: /the/path
9/// Base: /
10/// Extension: the/path
11///
12/// Windows Example:
13/// Path: C:\The\Path
14/// Base: C:\
15/// Extension: The\Path
16///
17/// URL Example:
18/// Path: https://example.com/the/path
19/// Base: https://example.com/
20/// Extension: the/path
21///
22/// # Files & Folders
23/// A storage path can be a file path or a folder path. These are mutually exclusive and
24/// exhaustive. A storage path is a folder path if it is equal to its base or ends with a
25/// file-separator, otherwise it is a file path. This invariant can be broken with `unsafe`.
26#[derive(Clone, Debug)]
27pub struct StoragePath {
28 path: String,
29 base_len: usize,
30 file_separator: char,
31}
32
33impl StoragePath {
34 //! Construction
35
36 /// Creates a new storage path.
37 ///
38 /// # Safety
39 /// The `base_len` must be a valid char boundary in the `path`.
40 pub unsafe fn new<S>(path: S, base_len: usize, file_separator: char) -> Self
41 where
42 S: Into<String>,
43 {
44 let path: String = path.into();
45
46 debug_assert!(path.is_char_boundary(base_len));
47
48 Self {
49 path,
50 base_len,
51 file_separator,
52 }
53 }
54}
55
56impl StoragePath {
57 //! Properties
58
59 /// Gets the path string.
60 pub fn path(&self) -> &str {
61 self.path.as_str()
62 }
63
64 /// Gets the mutable path string.
65 ///
66 /// # Safety
67 /// The path string must remain valid for the path.
68 pub unsafe fn path_mut(&mut self) -> &mut String {
69 &mut self.path
70 }
71
72 /// Gets the base length. (in bytes)
73 pub fn base_len(&self) -> usize {
74 self.base_len
75 }
76
77 /// Gets the file-separator.
78 pub fn file_separator(&self) -> char {
79 self.file_separator
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use crate::StoragePath;
86
87 #[test]
88 fn properties() {
89 let mut path: StoragePath = unsafe { StoragePath::new("/the/path", 1, '/') };
90 assert_eq!(path.path(), "/the/path");
91 unsafe {
92 assert_eq!(path.path_mut(), "/the/path");
93 }
94 assert_eq!(path.base_len(), 1);
95 assert_eq!(path.file_separator(), '/');
96 }
97}