1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
mod components;

use std::fmt;
use std::hash::Hasher;

pub use components::*;

use crate::typed::{Utf8TypedPath, Utf8TypedPathBuf};
use crate::{private, Encoding, UnixEncoding, Utf8Encoding, Utf8Path, Utf8PathBuf};

/// Represents a Unix-specific [`Utf8Path`]
pub type Utf8UnixPath = Utf8Path<Utf8UnixEncoding>;

/// Represents a Unix-specific [`Utf8PathBuf`]
pub type Utf8UnixPathBuf = Utf8PathBuf<Utf8UnixEncoding>;

/// Represents a Unix-specific [`Utf8Encoding`]
#[derive(Copy, Clone)]
pub struct Utf8UnixEncoding;

impl private::Sealed for Utf8UnixEncoding {}

impl<'a> Utf8Encoding<'a> for Utf8UnixEncoding {
    type Components = Utf8UnixComponents<'a>;

    fn label() -> &'static str {
        "unix"
    }

    fn components(path: &'a str) -> Self::Components {
        Utf8UnixComponents::new(path)
    }

    fn hash<H: Hasher>(path: &str, h: &mut H) {
        UnixEncoding::hash(path.as_bytes(), h);
    }

    fn push(current_path: &mut String, path: &str) {
        unsafe {
            UnixEncoding::push(current_path.as_mut_vec(), path.as_bytes());
        }
    }
}

impl fmt::Debug for Utf8UnixEncoding {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Utf8UnixEncoding").finish()
    }
}

impl fmt::Display for Utf8UnixEncoding {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Utf8UnixEncoding")
    }
}

impl<T> Utf8Path<T>
where
    T: for<'enc> Utf8Encoding<'enc>,
{
    /// Returns true if the encoding for the path is for Unix.
    ///
    /// # Examples
    ///
    /// ```
    /// use typed_path::{Utf8UnixPath, Utf8WindowsPath};
    ///
    /// assert!(Utf8UnixPath::new("/some/path").has_unix_encoding());
    /// assert!(!Utf8WindowsPath::new(r"\some\path").has_unix_encoding());
    /// ```
    pub fn has_unix_encoding(&self) -> bool {
        T::label() == Utf8UnixEncoding::label()
    }

    /// Creates an owned [`Utf8PathBuf`] like `self` but using [`Utf8UnixEncoding`].
    ///
    /// See [`Utf8Path::with_encoding`] for more information.
    pub fn with_unix_encoding(&self) -> Utf8PathBuf<Utf8UnixEncoding> {
        self.with_encoding()
    }
}

impl Utf8UnixPath {
    pub fn to_typed_path(&self) -> Utf8TypedPath {
        Utf8TypedPath::unix(self)
    }

    pub fn to_typed_path_buf(&self) -> Utf8TypedPathBuf {
        Utf8TypedPathBuf::from_unix(self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn push_should_replace_current_path_with_provided_path_if_provided_path_is_absolute() {
        // Empty current path will just become the provided path
        let mut current_path = String::new();
        Utf8UnixEncoding::push(&mut current_path, "/abc");
        assert_eq!(current_path, "/abc");

        // Non-empty relative current path will be replaced with the provided path
        let mut current_path = String::from("some/path");
        Utf8UnixEncoding::push(&mut current_path, "/abc");
        assert_eq!(current_path, "/abc");

        // Non-empty absolute current path will be replaced with the provided path
        let mut current_path = String::from("/some/path/");
        Utf8UnixEncoding::push(&mut current_path, "/abc");
        assert_eq!(current_path, "/abc");
    }

    #[test]
    fn push_should_append_path_to_current_path_with_a_separator_if_provided_path_is_relative() {
        // Empty current path will just become the provided path
        let mut current_path = String::new();
        Utf8UnixEncoding::push(&mut current_path, "abc");
        assert_eq!(current_path, "abc");

        // Non-empty current path will have provided path appended
        let mut current_path = String::from("some/path");
        Utf8UnixEncoding::push(&mut current_path, "abc");
        assert_eq!(current_path, "some/path/abc");

        // Non-empty current path ending in separator will have provided path appended without sep
        let mut current_path = String::from("some/path/");
        Utf8UnixEncoding::push(&mut current_path, "abc");
        assert_eq!(current_path, "some/path/abc");
    }
}