zoxide/src/util.rs

144 lines
4.8 KiB
Rust
Raw Normal View History

use std::env;
use std::path::{Component, Path, PathBuf};
2020-03-05 13:09:32 +00:00
use std::time::SystemTime;
2021-09-13 08:01:58 +00:00
use anyhow::{bail, Context, Result};
use crate::db::Epoch;
2020-10-18 09:22:13 +00:00
pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
2021-09-12 21:36:21 +00:00
dunce::canonicalize(path).with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
2020-10-18 09:22:13 +00:00
}
pub fn current_dir() -> Result<PathBuf> {
env::current_dir().context("could not get current directory")
}
2020-10-18 09:22:13 +00:00
pub fn current_time() -> Result<Epoch> {
2021-09-12 21:36:21 +00:00
let current_time =
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).context("system clock set to invalid time")?.as_secs();
2020-03-05 13:09:32 +00:00
2020-10-20 06:16:39 +00:00
Ok(current_time)
2020-03-05 13:09:32 +00:00
}
2020-10-18 09:22:13 +00:00
pub fn path_to_str<P: AsRef<Path>>(path: &P) -> Result<&str> {
let path = path.as_ref();
path.to_str().with_context(|| format!("invalid unicode in path: {}", path.display()))
2020-05-24 19:09:27 +00:00
}
2021-09-13 08:01:58 +00:00
/// Returns the absolute version of a path. Like [`std::path::Path::canonicalize`], but doesn't
/// resolve symlinks.
2020-10-18 09:22:13 +00:00
pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
let path = path.as_ref();
let base_path;
let mut components = path.components().peekable();
let mut stack = Vec::new();
// initialize root
if cfg!(windows) {
use std::path::Prefix;
fn get_drive_letter<P: AsRef<Path>>(path: P) -> Option<u8> {
let path = path.as_ref();
let mut components = path.components();
match components.next() {
Some(Component::Prefix(prefix)) => match prefix.kind() {
2021-09-12 21:36:21 +00:00
Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => Some(drive_letter),
_ => None,
},
_ => None,
}
}
fn get_drive_path(drive_letter: u8) -> PathBuf {
format!(r"{}:\", drive_letter as char).into()
}
fn get_drive_relative(drive_letter: u8) -> Result<PathBuf> {
2020-10-18 09:22:13 +00:00
let path = current_dir()?;
if Some(drive_letter) == get_drive_letter(&path) {
return Ok(path);
}
if let Some(path) = env::var_os(format!("={}:", drive_letter as char)) {
return Ok(path.into());
}
let path = get_drive_path(drive_letter);
Ok(path)
}
match components.peek() {
Some(Component::Prefix(prefix)) => match prefix.kind() {
Prefix::Disk(drive_letter) => {
let disk = components.next().unwrap();
2021-08-31 06:24:39 +00:00
if components.peek() == Some(&Component::RootDir) {
let root = components.next().unwrap();
stack.push(disk);
stack.push(root);
} else {
base_path = get_drive_relative(drive_letter)?;
stack.extend(base_path.components());
}
}
Prefix::VerbatimDisk(drive_letter) => {
components.next();
if components.peek() == Some(&Component::RootDir) {
components.next();
}
base_path = get_drive_path(drive_letter);
stack.extend(base_path.components());
}
_ => bail!("invalid path: {}", path.display()),
},
Some(Component::RootDir) => {
components.next();
let current_dir = env::current_dir()?;
2021-09-12 21:36:21 +00:00
let drive_letter = get_drive_letter(&current_dir)
.with_context(|| format!("could not get drive letter: {}", current_dir.display()))?;
base_path = get_drive_path(drive_letter);
stack.extend(base_path.components());
}
_ => {
2020-10-18 09:22:13 +00:00
base_path = current_dir()?;
stack.extend(base_path.components());
}
}
2021-08-31 06:24:39 +00:00
} else if components.peek() == Some(&Component::RootDir) {
let root = components.next().unwrap();
stack.push(root);
} else {
2021-08-31 06:24:39 +00:00
base_path = current_dir()?;
stack.extend(base_path.components());
}
for component in components {
match component {
Component::Normal(_) => stack.push(component),
Component::CurDir => (),
Component::ParentDir => {
if stack.last() != Some(&Component::RootDir) {
stack.pop();
}
}
Component::Prefix(_) | Component::RootDir => unreachable!(),
}
}
Ok(stack.iter().collect())
}
2021-05-05 23:01:57 +00:00
2021-09-13 08:01:58 +00:00
/// Convert a string to lowercase, with a fast path for ASCII strings.
2021-05-05 23:01:57 +00:00
pub fn to_lowercase<S: AsRef<str>>(s: S) -> String {
let s = s.as_ref();
if s.is_ascii() {
s.to_ascii_lowercase()
} else {
s.to_lowercase()
}
}