use camino::{Utf8Path, Utf8PathBuf};
use std::borrow::Borrow;
use std::fmt::Formatter;
use std::ops::Deref;
use std::path::{Path, PathBuf, StripPrefixError};
#[repr(transparent)]
#[derive(Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct SystemPath(Utf8Path);
impl SystemPath {
pub fn new(path: &(impl AsRef<Utf8Path> + ?Sized)) -> &Self {
let path = path.as_ref();
unsafe { &*(path as *const Utf8Path as *const SystemPath) }
}
#[inline]
pub fn simplified(&self) -> &SystemPath {
SystemPath::from_std_path(dunce::simplified(self.as_std_path())).unwrap()
}
#[inline]
#[must_use]
pub fn is_absolute(&self) -> bool {
self.0.is_absolute()
}
#[inline]
#[must_use]
pub fn extension(&self) -> Option<&str> {
self.0.extension()
}
#[inline]
#[must_use]
pub fn starts_with(&self, base: impl AsRef<SystemPath>) -> bool {
self.0.starts_with(base.as_ref())
}
#[inline]
#[must_use]
pub fn ends_with(&self, child: impl AsRef<SystemPath>) -> bool {
self.0.ends_with(child.as_ref())
}
#[inline]
#[must_use]
pub fn parent(&self) -> Option<&SystemPath> {
self.0.parent().map(SystemPath::new)
}
#[inline]
pub fn ancestors(&self) -> impl Iterator<Item = &SystemPath> {
self.0.ancestors().map(SystemPath::new)
}
#[inline]
pub fn components(&self) -> camino::Utf8Components<'_> {
self.0.components()
}
#[inline]
#[must_use]
pub fn file_name(&self) -> Option<&str> {
self.0.file_name()
}
#[inline]
#[must_use]
pub fn file_stem(&self) -> Option<&str> {
self.0.file_stem()
}
#[inline]
pub fn strip_prefix(
&self,
base: impl AsRef<SystemPath>,
) -> std::result::Result<&SystemPath, StripPrefixError> {
self.0.strip_prefix(base.as_ref()).map(SystemPath::new)
}
#[inline]
#[must_use]
pub fn join(&self, path: impl AsRef<SystemPath>) -> SystemPathBuf {
SystemPathBuf::from_utf8_path_buf(self.0.join(&path.as_ref().0))
}
#[inline]
pub fn with_extension(&self, extension: &str) -> SystemPathBuf {
SystemPathBuf::from_utf8_path_buf(self.0.with_extension(extension))
}
pub fn to_path_buf(&self) -> SystemPathBuf {
SystemPathBuf(self.0.to_path_buf())
}
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[inline]
pub fn as_std_path(&self) -> &Path {
self.0.as_std_path()
}
#[inline]
pub fn as_utf8_path(&self) -> &Utf8Path {
&self.0
}
pub fn from_std_path(path: &Path) -> Option<&SystemPath> {
Some(SystemPath::new(Utf8Path::from_path(path)?))
}
pub fn absolute(path: impl AsRef<SystemPath>, cwd: impl AsRef<SystemPath>) -> SystemPathBuf {
fn absolute(path: &SystemPath, cwd: &SystemPath) -> SystemPathBuf {
let path = &path.0;
let mut components = path.components().peekable();
let mut ret = if let Some(
c @ (camino::Utf8Component::Prefix(..) | camino::Utf8Component::RootDir),
) = components.peek().cloned()
{
components.next();
Utf8PathBuf::from(c.as_str())
} else {
cwd.0.to_path_buf()
};
for component in components {
match component {
camino::Utf8Component::Prefix(..) => unreachable!(),
camino::Utf8Component::RootDir => {
ret.push(component);
}
camino::Utf8Component::CurDir => {}
camino::Utf8Component::ParentDir => {
ret.pop();
}
camino::Utf8Component::Normal(c) => {
ret.push(c);
}
}
}
SystemPathBuf::from_utf8_path_buf(ret)
}
absolute(path.as_ref(), cwd.as_ref())
}
}
impl ToOwned for SystemPath {
type Owned = SystemPathBuf;
fn to_owned(&self) -> Self::Owned {
self.to_path_buf()
}
}
#[repr(transparent)]
#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct SystemPathBuf(#[cfg_attr(feature = "schemars", schemars(with = "String"))] Utf8PathBuf);
impl get_size2::GetSize for SystemPathBuf {
fn get_heap_size_with_tracker<T: get_size2::GetSizeTracker>(&self, tracker: T) -> (usize, T) {
(self.0.capacity(), tracker)
}
}
impl SystemPathBuf {
pub fn new() -> Self {
Self(Utf8PathBuf::new())
}
pub fn from_utf8_path_buf(path: Utf8PathBuf) -> Self {
Self(path)
}
pub fn from_path_buf(
path: std::path::PathBuf,
) -> std::result::Result<Self, std::path::PathBuf> {
Utf8PathBuf::from_path_buf(path).map(Self)
}
pub fn from_path_buf_lossy(path: std::path::PathBuf) -> Self {
Self::from_path_buf(path)
.unwrap_or_else(|path| Self::from(path.to_string_lossy().to_string()))
}
pub fn push(&mut self, path: impl AsRef<SystemPath>) {
self.0.push(&path.as_ref().0);
}
pub fn into_utf8_path_buf(self) -> Utf8PathBuf {
self.0
}
pub fn into_std_path_buf(self) -> PathBuf {
self.0.into_std_path_buf()
}
pub fn into_string(self) -> String {
self.0.into_string()
}
#[inline]
pub fn as_path(&self) -> &SystemPath {
SystemPath::new(&self.0)
}
}
impl From<&SystemPath> for Box<SystemPath> {
fn from(path: &SystemPath) -> Self {
Box::from(path.to_path_buf())
}
}
impl From<SystemPathBuf> for Box<SystemPath> {
fn from(path: SystemPathBuf) -> Self {
let path = Box::into_raw(path.0.into_boxed_path()) as *mut SystemPath;
unsafe { Box::from_raw(path) }
}
}
impl Clone for Box<SystemPath> {
fn clone(&self) -> Self {
Box::from(&**self)
}
}
impl get_size2::GetSize for Box<SystemPath> {
fn get_heap_size_with_tracker<T: get_size2::GetSizeTracker>(&self, tracker: T) -> (usize, T) {
(std::mem::size_of_val(&**self), tracker)
}
}
impl Borrow<SystemPath> for SystemPathBuf {
fn borrow(&self) -> &SystemPath {
self.as_path()
}
}
impl From<&str> for SystemPathBuf {
fn from(value: &str) -> Self {
SystemPathBuf::from_utf8_path_buf(Utf8PathBuf::from(value))
}
}
impl From<String> for SystemPathBuf {
fn from(value: String) -> Self {
SystemPathBuf::from_utf8_path_buf(Utf8PathBuf::from(value))
}
}
impl Default for SystemPathBuf {
fn default() -> Self {
Self::new()
}
}
impl AsRef<SystemPath> for SystemPathBuf {
#[inline]
fn as_ref(&self) -> &SystemPath {
self.as_path()
}
}
impl AsRef<SystemPath> for SystemPath {
#[inline]
fn as_ref(&self) -> &SystemPath {
self
}
}
impl AsRef<SystemPath> for Utf8Path {
#[inline]
fn as_ref(&self) -> &SystemPath {
SystemPath::new(self)
}
}
impl AsRef<SystemPath> for Utf8PathBuf {
#[inline]
fn as_ref(&self) -> &SystemPath {
SystemPath::new(self.as_path())
}
}
impl AsRef<SystemPath> for camino::Utf8Component<'_> {
#[inline]
fn as_ref(&self) -> &SystemPath {
SystemPath::new(self.as_str())
}
}
impl AsRef<SystemPath> for str {
#[inline]
fn as_ref(&self) -> &SystemPath {
SystemPath::new(self)
}
}
impl AsRef<SystemPath> for String {
#[inline]
fn as_ref(&self) -> &SystemPath {
SystemPath::new(self)
}
}
impl AsRef<Path> for SystemPath {
#[inline]
fn as_ref(&self) -> &Path {
self.0.as_std_path()
}
}
impl Deref for SystemPathBuf {
type Target = SystemPath;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_path()
}
}
impl AsRef<Path> for SystemPathBuf {
#[inline]
fn as_ref(&self) -> &Path {
self.0.as_std_path()
}
}
impl<P: AsRef<SystemPath>> FromIterator<P> for SystemPathBuf {
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
let mut buf = SystemPathBuf::new();
buf.extend(iter);
buf
}
}
impl<P: AsRef<SystemPath>> Extend<P> for SystemPathBuf {
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
for path in iter {
self.push(path);
}
}
}
impl std::fmt::Debug for SystemPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for SystemPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Debug for SystemPathBuf {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for SystemPathBuf {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "cache")]
impl ruff_cache::CacheKey for SystemPath {
fn cache_key(&self, hasher: &mut ruff_cache::CacheKeyHasher) {
self.0.as_str().cache_key(hasher);
}
}
#[cfg(feature = "cache")]
impl ruff_cache::CacheKey for SystemPathBuf {
fn cache_key(&self, hasher: &mut ruff_cache::CacheKeyHasher) {
self.as_path().cache_key(hasher);
}
}
#[repr(transparent)]
#[derive(Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct SystemVirtualPath(str);
impl SystemVirtualPath {
pub const fn new(path: &str) -> &SystemVirtualPath {
unsafe { &*(path as *const str as *const SystemVirtualPath) }
}
pub fn to_path_buf(&self) -> SystemVirtualPathBuf {
SystemVirtualPathBuf(self.0.to_string())
}
pub fn extension(&self) -> Option<&str> {
Path::new(&self.0).extension().and_then(|ext| ext.to_str())
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord, get_size2::GetSize)]
pub struct SystemVirtualPathBuf(String);
impl SystemVirtualPathBuf {
#[inline]
pub const fn as_path(&self) -> &SystemVirtualPath {
SystemVirtualPath::new(self.0.as_str())
}
}
impl From<&SystemVirtualPath> for Box<SystemVirtualPath> {
fn from(path: &SystemVirtualPath) -> Self {
Box::from(path.to_path_buf())
}
}
impl From<SystemVirtualPathBuf> for Box<SystemVirtualPath> {
fn from(path: SystemVirtualPathBuf) -> Self {
let path = Box::into_raw(path.0.into_boxed_str()) as *mut SystemVirtualPath;
unsafe { Box::from_raw(path) }
}
}
impl Clone for Box<SystemVirtualPath> {
fn clone(&self) -> Self {
Box::from(&**self)
}
}
impl get_size2::GetSize for Box<SystemVirtualPath> {
fn get_heap_size_with_tracker<T: get_size2::GetSizeTracker>(&self, tracker: T) -> (usize, T) {
(std::mem::size_of_val(&**self), tracker)
}
}
impl From<String> for SystemVirtualPathBuf {
fn from(value: String) -> Self {
SystemVirtualPathBuf(value)
}
}
impl AsRef<SystemVirtualPath> for SystemVirtualPathBuf {
#[inline]
fn as_ref(&self) -> &SystemVirtualPath {
self.as_path()
}
}
impl AsRef<SystemVirtualPath> for SystemVirtualPath {
#[inline]
fn as_ref(&self) -> &SystemVirtualPath {
self
}
}
impl AsRef<SystemVirtualPath> for str {
#[inline]
fn as_ref(&self) -> &SystemVirtualPath {
SystemVirtualPath::new(self)
}
}
impl AsRef<SystemVirtualPath> for String {
#[inline]
fn as_ref(&self) -> &SystemVirtualPath {
SystemVirtualPath::new(self)
}
}
impl Deref for SystemVirtualPathBuf {
type Target = SystemVirtualPath;
fn deref(&self) -> &Self::Target {
self.as_path()
}
}
impl std::fmt::Debug for SystemVirtualPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for SystemVirtualPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Debug for SystemVirtualPathBuf {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for SystemVirtualPathBuf {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "cache")]
impl ruff_cache::CacheKey for SystemVirtualPath {
fn cache_key(&self, hasher: &mut ruff_cache::CacheKeyHasher) {
self.as_str().cache_key(hasher);
}
}
#[cfg(feature = "cache")]
impl ruff_cache::CacheKey for SystemVirtualPathBuf {
fn cache_key(&self, hasher: &mut ruff_cache::CacheKeyHasher) {
self.as_path().cache_key(hasher);
}
}
impl Borrow<SystemVirtualPath> for SystemVirtualPathBuf {
fn borrow(&self) -> &SystemVirtualPath {
self.as_path()
}
}
pub fn deduplicate_nested_paths<P, I>(paths: I) -> DeduplicatedNestedPathsIter<P>
where
I: IntoIterator<Item = P>,
P: AsRef<SystemPath>,
{
DeduplicatedNestedPathsIter::new(paths)
}
pub struct DeduplicatedNestedPathsIter<P> {
inner: std::vec::IntoIter<P>,
next: Option<P>,
}
impl<P> DeduplicatedNestedPathsIter<P>
where
P: AsRef<SystemPath>,
{
fn new<I>(paths: I) -> Self
where
I: IntoIterator<Item = P>,
{
let mut paths = paths.into_iter().collect::<Vec<_>>();
paths.sort_unstable_by(|left, right| left.as_ref().cmp(right.as_ref()));
let mut iter = paths.into_iter();
Self {
next: iter.next(),
inner: iter,
}
}
}
impl<P> Iterator for DeduplicatedNestedPathsIter<P>
where
P: AsRef<SystemPath>,
{
type Item = P;
fn next(&mut self) -> Option<Self::Item> {
let current = self.next.take()?;
for next in self.inner.by_ref() {
if !next.as_ref().starts_with(current.as_ref()) {
self.next = Some(next);
break;
}
}
Some(current)
}
}