mod components;
use std::fmt;
use std::hash::Hasher;
pub use components::*;
use super::constants::*;
use crate::{private, Components, Encoding, Path, PathBuf};
pub type UnixPath = Path<UnixEncoding>;
pub type UnixPathBuf = PathBuf<UnixEncoding>;
pub struct UnixEncoding;
impl private::Sealed for UnixEncoding {}
impl<'a> Encoding<'a> for UnixEncoding {
type Components = UnixComponents<'a>;
fn label() -> &'static str {
"unix"
}
fn components(path: &'a [u8]) -> Self::Components {
UnixComponents::new(path)
}
fn hash<H: Hasher>(path: &[u8], h: &mut H) {
let mut component_start = 0;
let mut bytes_hashed = 0;
for i in 0..path.len() {
let is_sep = path[i] == SEPARATOR as u8;
if is_sep {
if i > component_start {
let to_hash = &path[component_start..i];
h.write(to_hash);
bytes_hashed += to_hash.len();
}
component_start = i + 1;
let tail = &path[component_start..];
component_start += match tail {
[b'.'] => 1,
[b'.', sep, ..] if *sep == SEPARATOR as u8 => 1,
_ => 0,
};
}
}
if component_start < path.len() {
let to_hash = &path[component_start..];
h.write(to_hash);
bytes_hashed += to_hash.len();
}
h.write_usize(bytes_hashed);
}
fn push(current_path: &mut Vec<u8>, path: &[u8]) {
if path.is_empty() {
return;
}
if Self::components(path).is_absolute() {
current_path.clear();
} else if !current_path.is_empty() && !current_path.ends_with(&[SEPARATOR as u8]) {
current_path.push(SEPARATOR as u8);
}
current_path.extend_from_slice(path);
}
}
impl fmt::Debug for UnixEncoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UnixEncoding").finish()
}
}
impl fmt::Display for UnixEncoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UnixEncoding")
}
}
impl<T> Path<T>
where
T: for<'enc> Encoding<'enc>,
{
pub fn has_unix_encoding(&self) -> bool {
T::label() == UnixEncoding::label()
}
pub fn with_unix_encoding(&self) -> PathBuf<UnixEncoding> {
self.with_encoding()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_should_replace_current_path_with_provided_path_if_provided_path_is_absolute() {
let mut current_path = vec![];
UnixEncoding::push(&mut current_path, b"/abc");
assert_eq!(current_path, b"/abc");
let mut current_path = b"some/path".to_vec();
UnixEncoding::push(&mut current_path, b"/abc");
assert_eq!(current_path, b"/abc");
let mut current_path = b"/some/path/".to_vec();
UnixEncoding::push(&mut current_path, b"/abc");
assert_eq!(current_path, b"/abc");
}
#[test]
fn push_should_append_path_to_current_path_with_a_separator_if_provided_path_is_relative() {
let mut current_path = vec![];
UnixEncoding::push(&mut current_path, b"abc");
assert_eq!(current_path, b"abc");
let mut current_path = b"some/path".to_vec();
UnixEncoding::push(&mut current_path, b"abc");
assert_eq!(current_path, b"some/path/abc");
let mut current_path = b"some/path/".to_vec();
UnixEncoding::push(&mut current_path, b"abc");
assert_eq!(current_path, b"some/path/abc");
}
}