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}