#![allow(clippy::unwrap_or_default)]
#![allow(clippy::useless_vec)]
use execute::Execute;
use std::{
env, path,
process::{Command, Stdio},
};
pub fn get_project_root_path() -> Option<String> {
let current_dir = env::current_dir().unwrap();
let mut dir = walk_reverse_dir(current_dir.as_path());
if dir.is_none() {
let mut command = Command::new("git");
command.arg("rev-parse").arg("--show-toplevel");
command.current_dir(current_dir.clone());
command.stdout(Stdio::piped());
command.stderr(Stdio::piped());
let output = command.execute_output().unwrap();
if !output.status.success() {
dir = Some(current_dir.to_str().unwrap().to_string());
} else {
let output = String::from_utf8(output.stdout).unwrap();
dir = Some(output.trim().to_string());
}
}
dir
}
fn walk_reverse_dir(path: &path::Path) -> Option<String> {
let current_path = path.to_path_buf();
let map_files = vec![
("package-lock.json", "npm"),
("npm-shrinkwrap.json", "npm"),
("yarn.lock", "yarn"),
("pnpm-lock.yaml", "pnpm"),
("bun.lockb", "bun"),
];
for (file, _) in map_files.iter() {
let lock_file = current_path.join(file);
if lock_file.exists() {
return Some(current_path.to_str().unwrap().to_string());
}
}
if let Some(parent) = path.parent() {
return walk_reverse_dir(parent);
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::{remove_file, File};
use std::path::Path;
fn create_agent_file(path: &Path) -> File {
File::create(path).expect("File not created")
}
fn delete_agent_file(path: &Path) {
remove_file(path).expect("File not deleted");
}
#[test]
fn npm_root_project() {
let path = std::env::current_dir().expect("Current user home directory");
let npm_lock = path.join("package-lock.json");
create_agent_file(&npm_lock);
let project_root = get_project_root_path();
assert_eq!(project_root, Some(path.to_str().unwrap().to_string()));
delete_agent_file(&npm_lock);
}
#[test]
fn yarn_root_project() {
let path = std::env::current_dir().expect("Current user home directory");
let yarn_lock = path.join("yarn.lock");
create_agent_file(&yarn_lock);
let project_root = get_project_root_path();
assert_eq!(project_root, Some(path.to_str().unwrap().to_string()));
delete_agent_file(&yarn_lock);
}
#[test]
fn pnpm_root_project() {
let path = std::env::current_dir().expect("Current user home directory");
let pnpm_lock = path.join("pnpm-lock.yaml");
create_agent_file(&pnpm_lock);
let project_root = get_project_root_path();
assert_eq!(project_root, Some(path.to_str().unwrap().to_string()));
delete_agent_file(&pnpm_lock);
}
#[test]
fn bun_root_project() {
let path = std::env::current_dir().expect("Current user home directory");
let bun_lock = path.join("bun.lockb");
create_agent_file(&bun_lock);
let project_root = get_project_root_path();
assert_eq!(project_root, Some(path.to_str().unwrap().to_string()));
delete_agent_file(&bun_lock);
}
#[test]
fn git_root_project() {
let path = std::env::current_dir().expect("Current user home directory");
let project_root = get_project_root_path();
assert_eq!(project_root, Some(path.to_str().unwrap().to_string()));
}
}