use std::path::Path;
pub fn detect_name(root: &Path) -> String {
root.file_name()
.map(|n| n.to_string_lossy().into_owned())
.unwrap_or_else(|| "project".to_string())
}
pub fn detect_remote(root: &Path) -> Option<String> {
let config = git_config(root)?;
let mut in_origin = false;
for line in config.lines() {
let line = line.trim();
if line.starts_with('[') {
in_origin = line == "[remote \"origin\"]";
continue;
}
if !in_origin {
continue;
}
if let Some((key, value)) = line.split_once('=')
&& key.trim() == "url"
{
return Some(value.trim().to_string());
}
}
None
}
fn git_config(root: &Path) -> Option<String> {
let dot_git = root.join(".git");
let git_dir = if dot_git.is_dir() {
dot_git
} else {
let pointer = std::fs::read_to_string(&dot_git).ok()?;
let target = pointer.trim().strip_prefix("gitdir:")?.trim();
let target = Path::new(target);
if target.is_absolute() { target.to_path_buf() } else { root.join(target) }
};
std::fs::read_to_string(git_dir.join("config")).ok()
}
pub fn detect_about(root: &Path) -> Option<String> {
if let Ok(text) = std::fs::read_to_string(root.join("Cargo.toml")) {
if let Some(about) = package_description(&text) {
return Some(about);
}
if let Some(about) = workspace_description(root, &text) {
return Some(about);
}
}
if let Ok(text) = std::fs::read_to_string(root.join("package.json"))
&& let Ok(json) = serde_json::from_str::<serde_json::Value>(&text)
&& let Some(about) = json.get("description").and_then(|d| d.as_str())
&& !about.trim().is_empty()
{
return Some(about.trim().to_string());
}
None
}
fn package_description(text: &str) -> Option<String> {
let mut in_package = false;
for line in text.lines() {
let line = line.trim();
if line.starts_with('[') {
in_package = line == "[package]";
continue;
}
if in_package
&& let Some((key, value)) = line.split_once('=')
&& key.trim() == "description"
{
let value = value.trim().trim_matches('"').trim();
if !value.is_empty() {
return Some(value.to_string());
}
}
}
None
}
fn workspace_description(root: &Path, text: &str) -> Option<String> {
let members = workspace_members(text);
if members.is_empty() {
return None;
}
let name = root.file_name()?.to_string_lossy().to_string();
let described = |member: &String| -> Option<String> {
let text = std::fs::read_to_string(root.join(member).join("Cargo.toml")).ok()?;
package_description(&text)
};
let basename = |member: &String| member.rsplit(['/', '\\']).next().unwrap_or(member).to_string();
for wanted in [name.clone(), format!("{name}-cli")] {
if let Some(about) = members.iter().find(|m| basename(m) == wanted).and_then(&described) {
return Some(about);
}
}
members.iter().find_map(&described)
}
fn workspace_members(text: &str) -> Vec<String> {
let mut members = Vec::new();
let mut in_workspace = false;
let mut in_list = false;
for line in text.lines() {
let line = line.trim();
if line.starts_with('[') && !in_list {
in_workspace = line == "[workspace]";
continue;
}
if !in_workspace {
continue;
}
let rest = match line.split_once('=') {
Some((key, rest)) if key.trim() == "members" => {
in_list = true;
rest.trim()
}
_ if in_list => line,
_ => continue,
};
for piece in rest.trim_start_matches('[').trim_end_matches(']').split(',') {
let piece = piece.trim().trim_matches('"').trim();
if !piece.is_empty() {
members.push(piece.to_string());
}
}
if rest.contains(']') {
in_list = false;
}
}
members
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_workspace_is_described_by_the_member_that_is_the_product() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("nooma");
std::fs::create_dir_all(root.join("crates").join("nooma")).unwrap();
std::fs::create_dir_all(root.join("crates").join("nooma-core")).unwrap();
std::fs::write(
root.join("Cargo.toml"),
"[workspace]\nmembers = [\"crates/nooma\", \"crates/nooma-core\"]\nresolver = \"3\"\n\n[workspace.package]\nversion = \"0.2.0\"\n",
)
.unwrap();
std::fs::write(
root.join("crates").join("nooma").join("Cargo.toml"),
"[package]\nname = \"nooma\"\ndescription = \"Local semantic search\"\n",
)
.unwrap();
std::fs::write(
root.join("crates").join("nooma-core").join("Cargo.toml"),
"[package]\nname = \"nooma-core\"\ndescription = \"The library behind it\"\n",
)
.unwrap();
assert_eq!(detect_about(&root).as_deref(), Some("Local semantic search"));
}
#[test]
fn the_crate_that_ships_wins_over_the_library_it_sits_on() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("sefy");
for member in ["sefy-core", "sefy-cli"] {
std::fs::create_dir_all(root.join("crates").join(member)).unwrap();
}
std::fs::write(
root.join("Cargo.toml"),
"[workspace]\nmembers = [\n \"crates/sefy-core\",\n \"crates/sefy-cli\",\n]\nresolver = \"2\"\n",
)
.unwrap();
std::fs::write(
root.join("crates").join("sefy-core").join("Cargo.toml"),
"[package]\nname = \"sefy-core\"\ndescription = \"Core library for sefy\"\n",
)
.unwrap();
std::fs::write(
root.join("crates").join("sefy-cli").join("Cargo.toml"),
"[package]\nname = \"sefy-cli\"\ndescription = \"An inconspicuous encrypted store\"\n",
)
.unwrap();
assert_eq!(detect_about(&root).as_deref(), Some("An inconspicuous encrypted store"));
let named = dir.path().join("nooma");
for member in ["nooma-cli", "nooma"] {
std::fs::create_dir_all(named.join("crates").join(member)).unwrap();
}
std::fs::write(named.join("Cargo.toml"), "[workspace]\nmembers = [\"crates/nooma-cli\", \"crates/nooma\"]\n").unwrap();
std::fs::write(
named.join("crates").join("nooma-cli").join("Cargo.toml"),
"[package]\nname = \"nooma-cli\"\ndescription = \"The wrapper\"\n",
)
.unwrap();
std::fs::write(
named.join("crates").join("nooma").join("Cargo.toml"),
"[package]\nname = \"nooma\"\ndescription = \"The product\"\n",
)
.unwrap();
assert_eq!(detect_about(&named).as_deref(), Some("The product"));
}
#[test]
fn a_workspace_with_one_described_member_means_that_one() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("austeris");
std::fs::create_dir_all(root.join("crates").join("api")).unwrap();
std::fs::write(root.join("Cargo.toml"), "[workspace]\nmembers = [\"crates/api\"]\n").unwrap();
std::fs::write(
root.join("crates").join("api").join("Cargo.toml"),
"[package]\nname = \"api\"\ndescription = \"Household bookkeeping\"\n",
)
.unwrap();
assert_eq!(detect_about(&root).as_deref(), Some("Household bookkeeping"));
let bare = dir.path().join("bare");
std::fs::create_dir_all(bare.join("crates").join("x")).unwrap();
std::fs::write(bare.join("Cargo.toml"), "[workspace]\nmembers = [\"crates/x\"]\n").unwrap();
std::fs::write(bare.join("crates").join("x").join("Cargo.toml"), "[package]\nname = \"x\"\n").unwrap();
assert_eq!(detect_about(&bare), None);
}
#[test]
fn about_comes_from_the_package_section_only() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Cargo.toml"),
"[package]\nname = \"x\"\ndescription = \"A thing\"\n[dependencies]\ndescription = \"not this\"\n",
)
.unwrap();
assert_eq!(detect_about(dir.path()).as_deref(), Some("A thing"));
}
#[test]
fn about_falls_back_to_package_json_then_to_nothing() {
let dir = tempfile::tempdir().unwrap();
assert_eq!(detect_about(dir.path()), None);
std::fs::write(dir.path().join("package.json"), "{\"name\":\"x\",\"description\":\"From npm\"}").unwrap();
assert_eq!(detect_about(dir.path()).as_deref(), Some("From npm"));
}
#[test]
fn the_name_is_the_directory_name_whatever_the_manifest_says() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("product");
std::fs::create_dir(&root).unwrap();
std::fs::write(root.join("Cargo.toml"), "[package]\nname = \"product-cli\"\n").unwrap();
assert_eq!(detect_name(&root), "product");
}
#[test]
fn remote_is_read_from_the_origin_section_only() {
let dir = tempfile::tempdir().unwrap();
let git = dir.path().join(".git");
std::fs::create_dir(&git).unwrap();
std::fs::write(
git.join("config"),
"[core]\n\tbare = false\n[remote \"upstream\"]\n\turl = https://example.com/upstream.git\n[remote \"origin\"]\n\turl = https://example.com/origin.git\n",
)
.unwrap();
assert_eq!(detect_remote(dir.path()).as_deref(), Some("https://example.com/origin.git"));
}
#[test]
fn a_worktree_pointer_file_is_followed() {
let dir = tempfile::tempdir().unwrap();
let real = dir.path().join("real-git");
std::fs::create_dir(&real).unwrap();
std::fs::write(real.join("config"), "[remote \"origin\"]\n\turl = git@example.com:o/r.git\n").unwrap();
let tree = dir.path().join("tree");
std::fs::create_dir(&tree).unwrap();
std::fs::write(tree.join(".git"), format!("gitdir: {}\n", real.display())).unwrap();
assert_eq!(detect_remote(&tree).as_deref(), Some("git@example.com:o/r.git"));
}
#[test]
fn no_git_means_no_remote() {
let dir = tempfile::tempdir().unwrap();
assert_eq!(detect_remote(dir.path()), None);
}
}