///|
/// Filesystem path helpers and the shrc directory layout.
///
/// shrc keeps its own tiny path layer rather than pulling in a platform path
/// library: every file it manages is a POSIX-style dotfile path.
///|
/// Return the directory part of `path` (everything before the last `/`).
/// Returns `"."` when there is no separator.
pub fn path_dirname(path : String) -> String {
let view = path.view()
match view.rev_find("/") {
Some(index) =>
if index == 0 {
"/"
} else {
view.view(start_offset=0, end_offset=index).to_owned()
}
None => "."
}
}
///|
/// Return the final component of `path`.
pub fn path_basename(path : String) -> String {
let view = path.view()
match view.rev_find("/") {
Some(index) =>
view.view(start_offset=index + 1, end_offset=view.length()).to_owned()
None => path
}
}
///|
/// Join two path segments with a single `/`.
pub fn path_join(left : String, right : String) -> String {
if left.length() == 0 {
return right
}
if right.length() == 0 {
return left
}
if left.view().has_suffix("/") {
"\{left}\{right}"
} else {
"\{left}/\{right}"
}
}
///|
/// The user's home directory. On Windows `HOME` is often unset, so fall back
/// to `USERPROFILE` and then to `HOMEDRIVE`+`HOMEPATH`; `/` is the last resort
/// so the pure path helpers stay total.
pub fn home_dir() -> String {
match @env.get_env_var("HOME") {
Some(h) if h.length() > 0 => h
_ =>
match @env.get_env_var("USERPROFILE") {
Some(h) if h.length() > 0 => h
_ =>
match (@env.get_env_var("HOMEDRIVE"), @env.get_env_var("HOMEPATH")) {
(Some(drive), Some(path)) if drive.length() > 0 && path.length() > 0 =>
drive + path
_ => "/"
}
}
}
}
///|
/// `$XDG_CONFIG_HOME`, or `$HOME/.config` when it is unset.
pub fn xdg_config_home() -> String {
match @env.get_env_var("XDG_CONFIG_HOME") {
Some(p) if p.length() > 0 => p
_ => path_join(home_dir(), ".config")
}
}
///|
/// The directory zsh keeps its dotfiles in (`$ZDOTDIR`), or `$HOME`.
pub fn zdotdir() -> String {
match @env.get_env_var("ZDOTDIR") {
Some(p) if p.length() > 0 => p
_ => home_dir()
}
}
///|
/// The shrc home directory. Honours `SHRC_HOME`, otherwise `$HOME/.shrc`.
pub fn shrc_home() -> String {
match @env.get_env_var("SHRC_HOME") {
Some(p) if p.length() > 0 => p
_ => path_join(home_dir(), ".shrc")
}
}
///|
/// Path of the manifest that records every managed entry.
pub fn manifest_path() -> String {
path_join(shrc_home(), "entries")
}
///|
/// Create `dir` and any missing parents. `@fs.create_dir` only makes the last
/// component, and `SHRC_HOME` is user-supplied, so walk the path.
pub fn ensure_dir(dir : String) -> Result[Unit, String] {
if dir.length() == 0 || dir == "/" || dir == "." || @fs.path_exists(dir) {
return Ok(())
}
let parent = path_dirname(dir)
if parent != dir && parent.length() > 0 {
match ensure_dir(parent) {
Ok(_) => ()
Err(msg) => return Err(msg)
}
}
@fs.create_dir(dir) catch {
_ =>
// A concurrent creator, or a directory that appeared meanwhile, is fine.
if !@fs.path_exists(dir) {
return Err("cannot create directory '\{dir}'")
}
}
Ok(())
}