ergo_fs/lib.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 */
8//! Methods and types for making working with the filesystem ergonomic, therefore fun.
9//!
10//! ## Purpose
11//!
12//! This crate provides a minimal set of common types and methods for working with the filesystem.
13//! These types aim to provide:
14//!
15//! - Descriptive error messages
16//! - Good performance, but not necessarily at _all_ costs.
17//! - As much type safety as is possible when dealing with the filesystem
18//!
19//! The crates it wraps/rexports are:
20//!
21//! - [`glob`](https://github.com/rust-lang-nursery/glob): Support for matching file paths against
22//! Unix shell style patterns.
23//! - [`path_abs`](https://github.com/vitiral/path_abs): Ergonomic paths and files in rust.
24//! - [`shellexpand`](https://github.com/netvl/shellexpand): A library for shell-like expansions of
25//! variables in strings.
26//! - [`tar-rs`](https://github.com/alexcrichton/tar-rs): A library for reading and writing TAR
27//! archives.
28//! - [`tempdir`](https://github.com/rust-lang-nursery/tempdir): Temporary directories
29//! of files.
30//! - [`walkdir`](https://github.com/BurntSushi/walkdir): Provides an efficient and cross platform
31//! implementation of recursive directory traversal.
32//!
33//! Consider supporting their development individually and starring them on github.
34//!
35//! ## How to Use
36//! ergo_fs is intended to be a "standard library" of filesystem types. Therefore you should
37//! use like so:
38//!
39//! ```
40//! extern crate ergo_fs;
41//! use ergo_fs::*;
42//! # fn try_main() -> ::std::io::Result<()> {
43//! # Ok(()) } fn main() { try_main().unwrap() }
44//! ```
45//!
46//! # Types
47//! This library provides several kinds of types which improve and expand on `std::fs` and
48//! `std::path`, as well as provide new functionality like temporary files and tar archives.
49//!
50//! ## Path, Dir and File Types
51//! These types provide improved error messages and type safety when working with paths and files.
52//!
53//! - [`PathArc`](struct.PathArc.html): a reference counted `PathBuf` with methods reimplemented
54//! with better error messages. Use this for a generic serializable path that may or may
55//! not exist.
56//! - [`PathAbs`](struct.PathAbs.html): a reference counted absolute (canonicalized) path that is
57//! guaranteed (on initialization) to exist.
58//! - [`PathFile`](struct.PathFile.html): a `PathAbs` that is guaranteed to be a file, with
59//! associated methods.
60//! - [`PathDir`](struct.PathDir.html): a `PathAbs` that is guaranteed to be a directory, with
61//! associated methods.
62//! - [`PathType`](struct.PathType.html): an enum containing either a PathFile or a PathDir.
63//! Returned by [`PathDir::list`][dir_list]
64//! - [`PathTmp`](struct.PathTmp.html): a `PathDir` that is deleted when it goes out of scope.
65//! This is a wrapper around the crate `tempdir::TempDir` with methods that mimick the `Path`
66//! types in this crate.
67//! - [`FileRead`](struct.FileRead.html): a read-only file handle with `path()` attached and
68//! improved error messages. Contains only the methods and trait implementations which are
69//! allowed by a read-only file.
70//! - [`FileWrite`](struct.FileWrite.html): a write-only file handle with `path()` attached and
71//! improved error messages. Contains only the methods and trait implementations which are
72//! allowed by a write-only file.
73//! - [`FileEdit`](struct.FileEdit.html): a read/write file handle with `path()` attached and
74//! improved error messages. Contains methods and trait implements for both readable _and_
75//! writeable files.
76//! - [`WalkDir`](struct.WalkDir.html): used for recursively walking directories _quickly_.
77//! See the **Walkdir** section below.
78//!
79//! In addition, it exports the following from [`std_prelude`](../std_prelude/index.html)
80//!
81//! - traits: `Read, IoWrite`
82//! - types: `Path, PathBuf`
83//!
84//!
85//! # Methods
86//! The following methods are exported.
87//!
88//! - [`expand`](fn.expand.html): does shell expansion on both tilde (`~` = home dir) and
89//! environment variables with the user's home directory + env variables. Also see the
90//! exported [`shellexpand`](shellexpand/index.html) crate itself. Consider using with
91//! `glob` (see below).
92//! - [`glob`](fn.glob.html): a lightweight wrapper around [`glob::glob`](../glob/fn.glob.html) that
93//! returns `PathType` objects.
94//! - [`glob_with`](fn.glob_with.html): a lightweight wrapper around
95//! [`glob::glob_with`](../glob/fn.glob_with.html) that returns `PathType` objects.
96//!
97//! # Details
98//! Bellow are some additional details about imported types.
99//!
100//! ## Walkdir
101//!
102//! Use `PathDir::walk` to walk a directory. This returns the [`Walkdir`](struct.WalkDir.html)
103//! iterator, which is a direct export from the [`walkdir`](../walkdir/index.html) crate. The
104//! crate already has excellent error messages, and although it returns the regular
105//! `std::path::PathBuf` type, you can convert to a `PathType` using `PathType::from_entry`.
106//!
107//! > TODO: although the WalkDir error can be auto-converted to std::io::Error, it
108//! > does not preserve the pretty output. See
109//! > [this ticket](https://github.com/BurntSushi/walkdir/pull/92)
110//!
111//! ### Examples
112//! ```rust
113//! # extern crate ergo_fs;
114//! use ergo_fs::*;
115//!
116//! # fn try_main() -> ::std::io::Result<()> {
117//! let dir = PathDir::new("src")?;
118//! for entry in dir.walk().max_depth(1) {
119//! match PathType::from_entry(entry?)? {
120//! PathType::File(file) => println!("got file {}", file.display()),
121//! PathType::Dir(dir) => println!("got dir {}", dir.display()),
122//! }
123//! }
124//! # Ok(()) } fn main() { try_main().unwrap() }
125//! ```
126//!
127//! ## Tar Files
128//! Similarly to walkdir, this is a direct export of the `tar` crate. It is recommended that you
129//! use the `FileWrite` and `FileRead` types when interacting with this crate so that
130//! reading/writing have context. This library already has pretty errors for every other operation.
131//!
132//! ```rust
133//! # extern crate ergo_fs;
134//! use ergo_fs::*;
135//! use ergo_fs::tar::Builder;
136//!
137//! # fn try_main() -> ::std::io::Result<()> {
138//! // We are going to tar the source code of this library
139//!
140//! let tmp = PathTmp::create("tmp")?;
141//! let mut tarfile = FileWrite::create(tmp.join("src.tar"))?;
142//!
143//! // tar the source directory
144//! let mut tar = Builder::new(tarfile);
145//! tar.append_dir_all("src", ".")?;
146//! tar.finish();
147//! let tarfile = tar.into_inner()?;
148//!
149//! // A tarfile now exists, do whatever you would like with it.
150//! # Ok(()) } fn main() { try_main().unwrap() }
151//! ```
152
153pub extern crate glob as glob_crate;
154pub extern crate path_abs;
155pub extern crate shellexpand;
156pub extern crate std_prelude;
157pub extern crate tar;
158pub extern crate tempdir;
159pub extern crate walkdir;
160
161// -------------------------------
162// External Crate Exports
163
164use std::borrow::Cow; // FIXME: remove this
165use std_prelude::*;
166pub use path_abs::{FileEdit, FileRead, FileWrite, PathAbs, PathArc, PathDir, PathFile, PathType};
167pub use walkdir::{Error as WalkError, WalkDir};
168pub use std_prelude::{Read, IoWrite, Path, PathBuf};
169
170// -------------------------------
171// Local Modules and Exports
172
173mod tmp;
174mod glob_wrapper;
175
176pub use glob_wrapper::{
177 // functions
178 glob, glob_with,
179 // renamed types
180 GlobOptions, GlobPatternError,
181 // new iterators
182 GlobPathDirs, GlobPathFiles, GlobPathTypes,
183};
184pub use tmp::PathTmp;
185
186/// Extension method on the `Path` type.
187pub trait PathDirExt
188where
189 Self: AsRef<Path>,
190{
191 /// Walk the `PathDir`, returning the `WalkDir` builder.
192 ///
193 /// # Examples
194 /// ```rust
195 /// # extern crate ergo_fs;
196 /// use ergo_fs::*;
197 ///
198 /// # fn try_main() -> ::std::io::Result<()> {
199 /// let dir = PathDir::new("src")?;
200 /// for entry in dir.walk().max_depth(1) {
201 /// match PathType::from_entry(entry?)? {
202 /// PathType::File(file) => println!("got file {}", file.display()),
203 /// PathType::Dir(dir) => println!("got dir {}", dir.display()),
204 /// }
205 /// }
206 /// # Ok(()) } fn main() { try_main().unwrap() }
207 /// ```
208 fn walk(&self) -> walkdir::WalkDir {
209 walkdir::WalkDir::new(&self)
210 }
211}
212
213/// Extended methods for `PathType`
214pub trait PathTypeExt {
215 /// Create a `PathType` from a `walkdir::DirEntry` using fewer syscalls.
216 ///
217 /// See [`PathDir::walk`]
218 ///
219 /// [`PathDir::walk`]: trait.PathDirExt.html#method.walk
220 fn from_entry(entry: walkdir::DirEntry) -> path_abs::Result<PathType> {
221 let abs = PathAbs::new(entry.path())?;
222 let ty = entry.file_type();
223 if ty.is_file() {
224 Ok(PathType::File(PathFile::from_abs_unchecked(abs)))
225 } else if ty.is_dir() {
226 Ok(PathType::Dir(PathDir::from_abs_unchecked(abs)))
227 } else {
228 // it is a symlink and we _must_ use a syscall to resolve the type.
229 PathType::from_abs(abs)
230 }
231 }
232}
233
234impl PathDirExt for PathDir {}
235impl PathTypeExt for PathType {}
236
237// ---------------------------------------
238// ----------- SHELL EXPANSION -----------
239
240/// Renamed [`shellexpand::LookupError`](../shellexpand/struct.LookupError.html) for better
241/// ergonomics.
242pub type ExpandError = shellexpand::LookupError<::std::env::VarError>;
243
244/// Performs both tilde and environment shell expansions in the default system context. This is
245/// the same as [`shellexpand::full`](../shellexpand/fn.full.html) and is the "typical use case"
246/// for expanding strings.
247///
248/// Note that non-existant variables will result in an `Err` (in `sh` they are silently replaced
249/// with an empty string). Also, environment lookup is only done _as needed_ so this function is
250/// performant on strings that do not expand.
251///
252/// For more options and information, see the exported [`shellexpand`](../shellexpand/index.html).
253///
254/// # Examples
255/// ```
256/// # extern crate ergo_fs;
257/// use std::env;
258/// use ergo_fs::*;
259///
260/// # fn try_main() -> Result<(), ExpandError> {
261/// env::set_var("A", "a value");
262/// env::set_var("B", "b value");
263///
264/// let home_dir = env::home_dir()
265/// .map(|p| p.display().to_string())
266/// .unwrap_or_else(|| "~".to_owned());
267///
268/// // Performs both tilde and environment expansions using the system contexts
269/// assert_eq!(
270/// expand("~/$A/${B}s")?,
271/// format!("{}/a value/b values", home_dir)
272/// );
273///
274/// // Unknown variables cause expansion errors
275/// assert_eq!(
276/// expand("~/$UNKNOWN/$B"),
277/// Err(ExpandError {
278/// var_name: "UNKNOWN".into(),
279/// cause: env::VarError::NotPresent
280/// })
281/// );
282/// # Ok(()) } fn main() { try_main().unwrap() }
283/// ```
284pub fn expand<SI: ?Sized>(input: &SI) -> Result<Cow<str>, ExpandError>
285where
286 SI: AsRef<str>,
287{
288 shellexpand::full(input)
289}