protobuf_parse/
proto_path.rs#![doc(hidden)]
use std::borrow::Borrow;
use std::fmt;
use std::hash::Hash;
use std::ops::Deref;
use std::path::Component;
use std::path::Path;
use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("path is empty")]
Empty,
#[error("backslashes in path: {0:?}")]
Backslashes(String),
#[error("path contains empty components: {0:?}")]
EmptyComponent(String),
#[error("dot in path: {0:?}")]
Dot(String),
#[error("dot-dot in path: {0:?}")]
DotDot(String),
#[error("path is absolute: `{}`", _0.display())]
Absolute(PathBuf),
#[error("non-UTF-8 component in path: `{}`", _0.display())]
NotUtf8(PathBuf),
}
#[repr(transparent)]
#[derive(Eq, PartialEq, Hash, Debug)]
pub struct ProtoPath {
path: str,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ProtoPathBuf {
path: String,
}
impl Hash for ProtoPathBuf {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_path().hash(state);
}
}
impl Borrow<ProtoPath> for ProtoPathBuf {
fn borrow(&self) -> &ProtoPath {
self.as_path()
}
}
impl Deref for ProtoPathBuf {
type Target = ProtoPath;
fn deref(&self) -> &ProtoPath {
self.as_path()
}
}
impl fmt::Display for ProtoPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.path)
}
}
impl fmt::Display for ProtoPathBuf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.path)
}
}
impl PartialEq<str> for ProtoPath {
fn eq(&self, other: &str) -> bool {
&self.path == other
}
}
impl PartialEq<str> for ProtoPathBuf {
fn eq(&self, other: &str) -> bool {
&self.path == other
}
}
impl ProtoPath {
fn unchecked_new(path: &str) -> &ProtoPath {
unsafe { &*(path as *const str as *const ProtoPath) }
}
pub fn new(path: &str) -> anyhow::Result<&ProtoPath> {
if path.is_empty() {
return Err(Error::Empty.into());
}
if path.contains('\\') {
return Err(Error::Backslashes(path.to_owned()).into());
}
for component in path.split('/') {
if component.is_empty() {
return Err(Error::EmptyComponent(path.to_owned()).into());
}
if component == "." {
return Err(Error::Dot(path.to_owned()).into());
}
if component == ".." {
return Err(Error::DotDot(path.to_owned()).into());
}
}
Ok(Self::unchecked_new(path))
}
pub fn to_str(&self) -> &str {
&self.path
}
pub fn to_path(&self) -> &Path {
Path::new(&self.path)
}
pub fn to_proto_path_buf(&self) -> ProtoPathBuf {
ProtoPathBuf {
path: self.path.to_owned(),
}
}
}
impl ProtoPathBuf {
pub fn as_path(&self) -> &ProtoPath {
ProtoPath::unchecked_new(&self.path)
}
pub fn new(path: String) -> anyhow::Result<ProtoPathBuf> {
ProtoPath::new(&path)?;
Ok(ProtoPathBuf { path })
}
pub fn from_path(path: &Path) -> anyhow::Result<ProtoPathBuf> {
let mut path_str = String::new();
for component in path.components() {
match component {
Component::Prefix(..) => return Err(Error::Absolute(path.to_owned()).into()),
Component::RootDir => return Err(Error::Absolute(path.to_owned()).into()),
Component::CurDir if path_str.is_empty() => {}
Component::CurDir => return Err(Error::Dot(path.display().to_string()).into()),
Component::ParentDir => {
return Err(Error::DotDot(path.display().to_string()).into())
}
Component::Normal(c) => {
if !path_str.is_empty() {
path_str.push('/');
}
path_str.push_str(c.to_str().ok_or_else(|| Error::NotUtf8(path.to_owned()))?);
}
}
}
Ok(ProtoPathBuf { path: path_str })
}
}