1use crate::path;
10use crate::prelude::*;
11use std::io;
12use std::path::{Path, PathBuf};
13
14#[derive(Debug, Error)]
17pub enum VarError {
18 #[error("Environment variable not present.")]
20 NotPresent,
21 #[error("Environment variable contains non-Unicode characters.")]
23 NotUnicode,
24}
25
26#[derive(Debug, Error)]
28pub enum WorkingPathError {
29 #[error("The current working directory was not found.")]
31 NotFound,
32 #[error("Permission denied reading the current working directory.")]
34 PermissionDenied,
35 #[error("The current working directory `{}` is not unicode.", .0.display())]
37 NotUnicode(PathBuf),
38}
39
40static EXE_PATH: Lazy<Result<(String, String), String>> = Lazy::new(|| {
42 let mut path: String = std::env::current_exe()
43 .map_err(|err| format!("IO error. {}.", err))?
44 .to_str()
45 .ok_or("non-unicode path name.")?
46 .into();
47
48 let file = path::pop(&mut path).unwrap_or_default();
49
50 Ok((path, file))
51});
52
53pub fn exe_name() -> &'static str {
55 &EXE_PATH.as_ref().expect("Failed to determine path to current executable").1
56}
57
58pub fn exe_path() -> &'static str {
61 &EXE_PATH.as_ref().expect("Failed to determine path to current executable").0
62}
63
64pub fn project_path() -> Option<&'static str> {
70 static PATH: Lazy<Option<&'static str>> = Lazy::new(|| {
71 let value = var("CARGO_MANIFEST_DIR").ok()?;
72
73 Some(Box::leak(value.into_boxed_str()))
74 });
75
76 *PATH
77}
78
79pub fn var(name: &str) -> Result<String, VarError> {
81 std::env::var(name).map_err(|err| match err {
82 std::env::VarError::NotPresent => VarError::NotPresent,
83 std::env::VarError::NotUnicode(_) => VarError::NotUnicode,
84 })
85}
86
87pub fn working_path() -> Result<String, WorkingPathError> {
89 let path = std::env::current_dir().map_err(|err| match err.kind() {
90 io::ErrorKind::NotFound => WorkingPathError::NotFound,
91 io::ErrorKind::PermissionDenied => WorkingPathError::PermissionDenied,
92 _ => panic!("{}", err),
93 })?;
94
95 Ok(path.to_str().map(String::from).ok_or(WorkingPathError::NotUnicode(path))?)
96}
97
98pub fn workspace_path() -> Option<&'static str> {
104 static PATH: Lazy<Option<&'static str>> = Lazy::new(|| {
105 let project_path = project_path()?;
108 let mut workspace_path: String = project_path.into();
109
110 loop {
111 path::append(&mut workspace_path, "Cargo.lock");
112
113 let found = AsRef::<Path>::as_ref(&workspace_path).exists();
114
115 path::pop(&mut workspace_path);
116
117 if found {
118 break;
119 }
120
121 if path::pop(&mut workspace_path).is_none() {
125 workspace_path.replace_range(.., project_path);
126 break;
127 }
128 }
129
130 Some(Box::leak(workspace_path.into_boxed_str()))
131 });
132
133 *PATH
134}