naga/
path_like.rs

1//! [`PathLike`] and its supporting items, such as [`PathLikeRef`] and [`PathLikeOwned`].
2//! This trait and these types provide a common denominator API for `Path`-like
3//! types and operations in `std` and `no_std` contexts.
4//!
5//! # Usage
6//!
7//! - Store a [`PathLikeRef<'a>`] instead of a `&'a Path` in structs and enums.
8//! - Store a [`PathLikeOwned`] instead of a `PathBuf` in structs and enums.
9//! - Accept `impl PathLike` instead of `impl AsRef<Path>` for methods which directly
10//!   work with `Path`-like values.
11//! - Accept `Into<PathLikeRef<'_>>` and/or `Into<PathLikeOwned>` in methods which
12//!   will store a `Path`-like value.
13
14use alloc::{borrow::Cow, string::String};
15use core::fmt;
16
17mod sealed {
18    /// Seal for [`PathLike`](super::PathLike).
19    pub trait Sealed {}
20}
21
22/// A trait that abstracts over types accepted for conversion to the most
23/// featureful path representation possible; that is:
24///
25/// - When `no_std` is active, this is implemented for:
26///   - [`str`],
27///   - [`String`],
28///   - [`Cow<'_, str>`], and
29///   - [`PathLikeRef`]
30/// - Otherwise, types that implement `AsRef<Path>` (to extract a `&Path`).
31///
32/// This type is used as the type bounds for various diagnostic rendering methods, i.e.,
33/// [`WithSpan::emit_to_string_with_path`](crate::span::WithSpan::emit_to_string_with_path).
34pub trait PathLike: sealed::Sealed {
35    fn to_string_lossy(&self) -> Cow<'_, str>;
36}
37
38/// Abstraction over `Path` which falls back to [`str`] for `no_std` compatibility.
39///
40/// This type should be used for _storing_ a reference to a [`PathLike`].
41/// Functions which accept a `Path` should prefer to use `impl PathLike`
42/// or `impl Into<PathLikeRef<'_>>`.
43#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct PathLikeRef<'a>(&'a path_like_impls::PathInner);
45
46impl fmt::Debug for PathLikeRef<'_> {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        fmt::Debug::fmt(self.0, f)
49    }
50}
51
52impl<'a> From<&'a str> for PathLikeRef<'a> {
53    fn from(value: &'a str) -> Self {
54        cfg_if::cfg_if! {
55            if #[cfg(std)] {
56                Self(std::path::Path::new(value))
57            } else {
58                Self(value)
59            }
60        }
61    }
62}
63
64/// Abstraction over `PathBuf` which falls back to [`String`] for `no_std` compatibility.
65///
66/// This type should be used for _storing_ an owned [`PathLike`].
67/// Functions which accept a `PathBuf` should prefer to use `impl PathLike`
68/// or `impl Into<PathLikeOwned>`.
69#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
70pub struct PathLikeOwned(<path_like_impls::PathInner as alloc::borrow::ToOwned>::Owned);
71
72impl fmt::Debug for PathLikeOwned {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        fmt::Debug::fmt(&self.0, f)
75    }
76}
77
78impl From<String> for PathLikeOwned {
79    fn from(value: String) -> Self {
80        cfg_if::cfg_if! {
81            if #[cfg(std)] {
82                Self(value.into())
83            } else {
84                Self(value)
85            }
86        }
87    }
88}
89
90#[cfg(std)]
91mod path_like_impls {
92    //! Implementations of [`PathLike`] within an `std` context.
93    //!
94    //! Since `std` is available, we blanket implement [`PathLike`] for all types
95    //! implementing [`AsRef<Path>`].
96
97    use alloc::borrow::Cow;
98    use std::path::Path;
99
100    use super::{sealed, PathLike};
101
102    pub(super) type PathInner = Path;
103
104    impl<T: AsRef<Path> + ?Sized> PathLike for T {
105        fn to_string_lossy(&self) -> Cow<'_, str> {
106            self.as_ref().to_string_lossy()
107        }
108    }
109
110    impl<T: AsRef<Path> + ?Sized> sealed::Sealed for T {}
111}
112
113#[cfg(no_std)]
114mod path_like_impls {
115    //! Implementations of [`PathLike`] within a `no_std` context.
116    //!
117    //! Without `std`, we cannot blanket implement on [`AsRef<Path>`].
118    //! Instead, we manually implement for a subset of types which are known
119    //! to implement [`AsRef<Path>`] when `std` is available.
120    //!
121    //! Implementing [`PathLike`] for a type which does _not_ implement [`AsRef<Path>`]
122    //! with `std` enabled breaks the additive requirement of Cargo features.
123
124    use alloc::{borrow::Cow, string::String};
125    use core::borrow::Borrow;
126
127    use super::{sealed, PathLike, PathLikeOwned, PathLikeRef};
128
129    pub(super) type PathInner = str;
130
131    impl PathLike for String {
132        fn to_string_lossy(&self) -> Cow<'_, str> {
133            Cow::Borrowed(self.as_str())
134        }
135    }
136
137    impl sealed::Sealed for String {}
138
139    impl PathLike for str {
140        fn to_string_lossy(&self) -> Cow<'_, str> {
141            Cow::Borrowed(self)
142        }
143    }
144
145    impl sealed::Sealed for str {}
146
147    impl PathLike for Cow<'_, str> {
148        fn to_string_lossy(&self) -> Cow<'_, str> {
149            Cow::Borrowed(self.borrow())
150        }
151    }
152
153    impl sealed::Sealed for Cow<'_, str> {}
154
155    impl<T: PathLike + ?Sized> PathLike for &T {
156        fn to_string_lossy(&self) -> Cow<'_, str> {
157            (*self).to_string_lossy()
158        }
159    }
160
161    impl<T: PathLike + ?Sized> sealed::Sealed for &T {}
162
163    impl PathLike for PathLikeRef<'_> {
164        fn to_string_lossy(&self) -> Cow<'_, str> {
165            Cow::Borrowed(self.0)
166        }
167    }
168
169    impl sealed::Sealed for PathLikeRef<'_> {}
170
171    impl PathLike for PathLikeOwned {
172        fn to_string_lossy(&self) -> Cow<'_, str> {
173            Cow::Borrowed(self.0.borrow())
174        }
175    }
176
177    impl sealed::Sealed for PathLikeOwned {}
178}
179
180#[cfg(std)]
181mod path_like_owned_std_impls {
182    //! Traits which can only be implemented for [`PathLikeOwned`] with `std`.
183
184    use std::path::{Path, PathBuf};
185
186    use super::PathLikeOwned;
187
188    impl AsRef<Path> for PathLikeOwned {
189        fn as_ref(&self) -> &Path {
190            self.0.as_ref()
191        }
192    }
193
194    impl From<PathBuf> for PathLikeOwned {
195        fn from(value: PathBuf) -> Self {
196            Self(value)
197        }
198    }
199
200    impl From<PathLikeOwned> for PathBuf {
201        fn from(value: PathLikeOwned) -> Self {
202            value.0
203        }
204    }
205
206    impl AsRef<PathBuf> for PathLikeOwned {
207        fn as_ref(&self) -> &PathBuf {
208            &self.0
209        }
210    }
211}
212
213#[cfg(std)]
214mod path_like_ref_std_impls {
215    //! Traits which can only be implemented for [`PathLikeRef`] with `std`.
216
217    use std::path::Path;
218
219    use super::PathLikeRef;
220
221    impl AsRef<Path> for PathLikeRef<'_> {
222        fn as_ref(&self) -> &Path {
223            self.0
224        }
225    }
226
227    impl<'a> From<&'a Path> for PathLikeRef<'a> {
228        fn from(value: &'a Path) -> Self {
229            Self(value)
230        }
231    }
232
233    impl<'a> From<PathLikeRef<'a>> for &'a Path {
234        fn from(value: PathLikeRef<'a>) -> Self {
235            value.0
236        }
237    }
238}