use crate::{
windows::{Utf8WindowsComponents, WindowsPrefix, WindowsPrefixComponent},
ParseError,
};
use std::{
cmp,
convert::TryFrom,
hash::{Hash, Hasher},
str::Utf8Error,
};
#[derive(Copy, Clone, Debug, Eq)]
pub struct Utf8WindowsPrefixComponent<'a> {
pub(crate) raw: &'a str,
pub(crate) parsed: Utf8WindowsPrefix<'a>,
}
impl<'a> Utf8WindowsPrefixComponent<'a> {
pub fn kind(&self) -> Utf8WindowsPrefix<'a> {
self.parsed
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.raw.len()
}
pub fn as_str(&self) -> &'a str {
self.raw
}
pub fn from_utf8(component: &WindowsPrefixComponent<'a>) -> Result<Self, Utf8Error> {
Ok(Self {
raw: std::str::from_utf8(component.raw)?,
parsed: Utf8WindowsPrefix::from_utf8(&component.parsed)?,
})
}
pub unsafe fn from_utf8_unchecked(component: &WindowsPrefixComponent<'a>) -> Self {
Self {
raw: std::str::from_utf8_unchecked(component.raw),
parsed: Utf8WindowsPrefix::from_utf8_unchecked(&component.parsed),
}
}
}
impl<'a> TryFrom<&'a str> for Utf8WindowsPrefixComponent<'a> {
type Error = ParseError;
fn try_from(path: &'a str) -> Result<Self, Self::Error> {
let mut components = Utf8WindowsComponents::new(path);
let prefix = components
.next()
.and_then(|c| c.prefix())
.ok_or("not a prefix")?;
if components.next().is_some() {
return Err("contains more than prefix");
}
Ok(prefix)
}
}
impl<'a> cmp::PartialEq for Utf8WindowsPrefixComponent<'a> {
#[inline]
fn eq(&self, other: &Utf8WindowsPrefixComponent<'a>) -> bool {
cmp::PartialEq::eq(&self.parsed, &other.parsed)
}
}
impl<'a> cmp::PartialOrd for Utf8WindowsPrefixComponent<'a> {
#[inline]
fn partial_cmp(&self, other: &Utf8WindowsPrefixComponent<'a>) -> Option<cmp::Ordering> {
cmp::PartialOrd::partial_cmp(&self.parsed, &other.parsed)
}
}
impl cmp::Ord for Utf8WindowsPrefixComponent<'_> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
cmp::Ord::cmp(&self.parsed, &other.parsed)
}
}
impl Hash for Utf8WindowsPrefixComponent<'_> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.parsed.hash(h);
}
}
#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum Utf8WindowsPrefix<'a> {
Verbatim(&'a str),
VerbatimUNC(&'a str, &'a str),
VerbatimDisk(char),
DeviceNS(&'a str),
UNC(&'a str, &'a str),
Disk(char),
}
impl<'a> TryFrom<&'a str> for Utf8WindowsPrefix<'a> {
type Error = ParseError;
fn try_from(path: &'a str) -> Result<Self, Self::Error> {
Ok(Utf8WindowsPrefixComponent::try_from(path)?.kind())
}
}
impl<'a> Utf8WindowsPrefix<'a> {
#[inline]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
use self::Utf8WindowsPrefix::*;
match *self {
Verbatim(x) => 4 + x.len(),
VerbatimUNC(x, y) => 8 + x.len() + if !y.is_empty() { 1 + y.len() } else { 0 },
VerbatimDisk(_) => 6,
UNC(x, y) => 2 + x.len() + if !y.is_empty() { 1 + y.len() } else { 0 },
DeviceNS(x) => 4 + x.len(),
Disk(_) => 2,
}
}
#[inline]
pub fn is_verbatim(&self) -> bool {
use self::Utf8WindowsPrefix::*;
matches!(*self, Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..))
}
pub fn from_utf8(prefix: &WindowsPrefix<'a>) -> Result<Self, Utf8Error> {
Ok(match prefix {
WindowsPrefix::Verbatim(x) => Self::Verbatim(std::str::from_utf8(x)?),
WindowsPrefix::VerbatimUNC(x, y) => {
Self::VerbatimUNC(std::str::from_utf8(x)?, std::str::from_utf8(y)?)
}
WindowsPrefix::VerbatimDisk(x) => Self::VerbatimDisk(*x as char),
WindowsPrefix::UNC(x, y) => Self::UNC(std::str::from_utf8(x)?, std::str::from_utf8(y)?),
WindowsPrefix::DeviceNS(x) => Self::DeviceNS(std::str::from_utf8(x)?),
WindowsPrefix::Disk(x) => Self::Disk(*x as char),
})
}
pub unsafe fn from_utf8_unchecked(prefix: &WindowsPrefix<'a>) -> Self {
match prefix {
WindowsPrefix::Verbatim(x) => Self::Verbatim(std::str::from_utf8_unchecked(x)),
WindowsPrefix::VerbatimUNC(x, y) => Self::VerbatimUNC(
std::str::from_utf8_unchecked(x),
std::str::from_utf8_unchecked(y),
),
WindowsPrefix::VerbatimDisk(x) => Self::VerbatimDisk(*x as char),
WindowsPrefix::UNC(x, y) => Self::UNC(
std::str::from_utf8_unchecked(x),
std::str::from_utf8_unchecked(y),
),
WindowsPrefix::DeviceNS(x) => Self::DeviceNS(std::str::from_utf8_unchecked(x)),
WindowsPrefix::Disk(x) => Self::Disk(*x as char),
}
}
}