path_abs/
ty.rs

1/* Copyright (c) 2018 Garrett Berg, vitiral@gmail.com
2 *
3 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 * http://opensource.org/licenses/MIT>, at your option. This file may not be
6 * copied, modified, or distributed except according to those terms.
7 */
8use std::ffi;
9use std_prelude::*;
10
11use super::Result;
12use super::{PathAbs, PathDir, PathFile, PathInfo, PathOps};
13
14#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
15#[cfg_attr(
16    feature = "serialize",
17    serde(tag = "type", content = "path", rename_all = "lowercase")
18)]
19#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
20/// An an enum containing either a file or a directory.
21///
22/// This is used primarily for:
23/// - The items returned from `PathDir::list`
24/// - Serializing paths of different types.
25///
26/// Note that for symlinks, this returns the underlying file type.
27pub enum PathType {
28    File(PathFile),
29    Dir(PathDir),
30}
31
32impl PathType {
33    /// Resolves and returns the `PathType` of the given path.
34    ///
35    /// > If the path exists but is not a file or a directory (i.e. is a symlink), then
36    /// > `io::ErrorKind::InvalidInput` is returned.
37    ///
38    /// # Examples
39    /// ```rust
40    /// # extern crate path_abs;
41    /// use path_abs::PathType;
42    ///
43    /// # fn try_main() -> ::std::io::Result<()> {
44    /// let src = PathType::new("src")?;
45    /// # Ok(()) } fn main() { try_main().unwrap() }
46    pub fn new<P: AsRef<Path>>(path: P) -> Result<PathType> {
47        let abs = PathAbs::new(&path)?;
48        PathType::try_from(abs)
49    }
50
51    /// Consume the `PathAbs` returning the `PathType`.
52    pub fn try_from<P: Into<PathAbs>>(path: P) -> Result<PathType> {
53        let abs = path.into();
54        let ty = abs.metadata()?.file_type();
55        if ty.is_file() {
56            Ok(PathType::File(PathFile(abs)))
57        } else if ty.is_dir() {
58            Ok(PathType::Dir(PathDir(abs)))
59        } else {
60            unreachable!("rust docs: The fs::metadata function follows symbolic links")
61        }
62    }
63
64    /// Unwrap the `PathType` as a `PathFile`.
65    ///
66    /// # Examples
67    /// ```rust
68    /// # extern crate path_abs;
69    /// use path_abs::PathType;
70    ///
71    /// # fn try_main() -> ::std::io::Result<()> {
72    /// let lib = PathType::new("src/lib.rs")?.unwrap_file();
73    /// # Ok(()) } fn main() { try_main().unwrap() }
74    pub fn unwrap_file(self) -> PathFile {
75        match self {
76            PathType::File(f) => f,
77            PathType::Dir(d) => {
78                panic!("unwrap_file called on {}, which is not a file", d.display())
79            }
80        }
81    }
82
83    /// Unwrap the `PathType` as a `PathDir`.
84    ///
85    /// # Examples
86    /// ```rust
87    /// # extern crate path_abs;
88    /// use path_abs::PathType;
89    ///
90    /// # fn try_main() -> ::std::io::Result<()> {
91    /// let src = PathType::new("src")?.unwrap_dir();
92    /// # Ok(()) } fn main() { try_main().unwrap() }
93    pub fn unwrap_dir(self) -> PathDir {
94        match self {
95            PathType::Dir(d) => d,
96            PathType::File(f) => panic!(
97                "unwrap_dir called on {}, which is not a directory",
98                f.display()
99            ),
100        }
101    }
102
103    /// Return whether this variant is `PathType::Dir`.
104    pub fn is_dir(&self) -> bool {
105        if let PathType::Dir(_) = *self {
106            true
107        } else {
108            false
109        }
110    }
111
112    /// Return whether this variant is `PathType::File`.
113    pub fn is_file(&self) -> bool {
114        if let PathType::File(_) = *self {
115            true
116        } else {
117            false
118        }
119    }
120}
121
122impl AsRef<ffi::OsStr> for PathType {
123    fn as_ref(&self) -> &std::ffi::OsStr {
124        self.as_path().as_ref()
125    }
126}
127
128impl AsRef<PathAbs> for PathType {
129    fn as_ref(&self) -> &PathAbs {
130        match *self {
131            PathType::File(ref file) => file.as_ref(),
132            PathType::Dir(ref dir) => dir.as_ref(),
133        }
134    }
135}
136
137impl AsRef<Path> for PathType {
138    fn as_ref(&self) -> &Path {
139        let r: &PathAbs = self.as_ref();
140        r.as_ref()
141    }
142}
143
144impl AsRef<PathBuf> for PathType {
145    fn as_ref(&self) -> &PathBuf {
146        let r: &PathAbs = self.as_ref();
147        r.as_ref()
148    }
149}
150
151impl Borrow<PathAbs> for PathType {
152    fn borrow(&self) -> &PathAbs {
153        self.as_ref()
154    }
155}
156
157impl Borrow<Path> for PathType {
158    fn borrow(&self) -> &Path {
159        self.as_ref()
160    }
161}
162
163impl Borrow<PathBuf> for PathType {
164    fn borrow(&self) -> &PathBuf {
165        self.as_ref()
166    }
167}
168
169impl<'a> Borrow<PathAbs> for &'a PathType {
170    fn borrow(&self) -> &PathAbs {
171        self.as_ref()
172    }
173}
174
175impl<'a> Borrow<Path> for &'a PathType {
176    fn borrow(&self) -> &Path {
177        self.as_ref()
178    }
179}
180
181impl<'a> Borrow<PathBuf> for &'a PathType {
182    fn borrow(&self) -> &PathBuf {
183        self.as_ref()
184    }
185}
186
187impl From<PathType> for PathAbs {
188    fn from(path: PathType) -> PathAbs {
189        match path {
190            PathType::File(p) => p.into(),
191            PathType::Dir(p) => p.into(),
192        }
193    }
194}
195
196impl From<PathType> for Arc<PathBuf> {
197    fn from(path: PathType) -> Arc<PathBuf> {
198        let abs: PathAbs = path.into();
199        abs.into()
200    }
201}
202
203impl From<PathType> for PathBuf {
204    fn from(path: PathType) -> PathBuf {
205        let abs: PathAbs = path.into();
206        abs.into()
207    }
208}
209
210impl PathOps for PathType {
211    type Output = PathAbs;
212
213    fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output> {
214        match self {
215            PathType::File(p) => p.concat(path),
216            PathType::Dir(p) => p.concat(path),
217        }
218    }
219
220    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
221        let buf = Path::join(self.as_path(), path);
222        Self::Output::new_unchecked(buf)
223    }
224
225    fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
226        match self {
227            PathType::File(p) => p.with_file_name(file_name),
228            PathType::Dir(p) => p.with_file_name(file_name),
229        }
230    }
231
232    fn with_extension<S: AsRef<ffi::OsStr>>(&self, extension: S) -> Self::Output {
233        match self {
234            PathType::File(p) => p.with_extension(extension),
235            PathType::Dir(p) => p.with_extension(extension),
236        }
237    }
238}