use std::path::{Path, PathBuf};
fn config_path(path: &Path) -> Option<PathBuf> {
let config = path.join("config");
if config.exists() {
return Some(config);
}
let config = path.join("config.toml");
if config.exists() {
return Some(config);
}
None
}
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Walk<'a> {
ancestors: std::path::Ancestors<'a>,
cargo_home: Option<PathBuf>,
}
impl<'a> Walk<'a> {
pub fn new(current_dir: &'a Path) -> Self {
Self::with_cargo_home(current_dir, home::cargo_home_with_cwd(current_dir).ok())
}
pub fn with_cargo_home(current_dir: &'a Path, cargo_home: Option<PathBuf>) -> Self {
Self { ancestors: current_dir.ancestors(), cargo_home }
}
}
impl Iterator for Walk<'_> {
type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> {
for p in self.ancestors.by_ref() {
let p = p.join(".cargo");
if self.cargo_home.as_ref() == Some(&p) {
self.cargo_home = None;
}
if let Some(p) = config_path(&p) {
return Some(p);
}
}
config_path(&self.cargo_home.take()?)
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use fs_err as fs;
use super::*;
#[test]
fn walk() -> Result<()> {
let tmp = tempfile::tempdir()?;
let p = tmp.path();
let home = &p.join("a/.cargo");
let cwd = &p.join("a/b/c");
fs::create_dir_all(home)?;
fs::write(p.join("a/.cargo/config"), "")?;
fs::create_dir_all(p.join("a/b/.cargo"))?;
fs::write(p.join("a/b/.cargo/config"), "")?;
fs::write(p.join("a/b/.cargo/config.toml"), "")?;
fs::create_dir_all(p.join("a/b/c/.cargo"))?;
fs::write(p.join("a/b/c/.cargo/config.toml"), "")?;
let mut w = Walk::with_cargo_home(cwd, Some(home.clone()));
assert_eq!(w.next(), Some(p.join("a/b/c/.cargo/config.toml")));
assert_eq!(w.next(), Some(p.join("a/b/.cargo/config")));
assert_eq!(w.next(), Some(p.join("a/.cargo/config")));
assert_eq!(w.next(), None);
Ok(())
}
}