use std::env;
use std::process::Command;
pub fn get_project() -> String {
if let Ok(output) = Command::new("git")
.args(["rev-parse", "--show-toplevel"])
.output()
&& output.status.success()
{
return String::from_utf8_lossy(&output.stdout).trim().to_string();
}
env::current_dir()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| "unknown".to_string())
}
pub fn extract_slug_from_url(url: &str) -> Option<String> {
let path = if let Some(rest) = url.strip_prefix("git@") {
rest.split(':').nth(1)?
} else if let Some(rest) = url.strip_prefix("ssh://git@") {
rest.split_once('/')?.1
} else if url.starts_with("https://") || url.starts_with("http://") {
let without_scheme = url.splitn(3, '/').nth(2)?;
without_scheme.split_once('/')?.1
} else {
return None;
};
let slug = path.strip_suffix(".git").unwrap_or(path);
if slug.contains('/') { Some(slug.to_string()) } else { None }
}
pub fn normalize_project(project: &str) -> String {
let trimmed = project.trim().trim_end_matches('/');
if let Some(pos) = trimmed.to_lowercase().find("github.com/") {
let after = &trimmed[pos + "github.com/".len()..];
let parts: Vec<&str> = after.split('/').collect();
if parts.len() >= 2 {
return format!("{}/{}", parts[0], parts[1]).to_lowercase();
}
}
trimmed.to_lowercase()
}
pub fn short_project(path: &str) -> &str {
let trimmed = path.trim_end_matches('/');
match trimmed.rfind('/') {
Some(slash) => match trimmed[..slash].rfind('/') {
Some(prev) => &trimmed[prev + 1..],
None => trimmed,
},
None => trimmed,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_project_two_segments() {
assert_eq!(
short_project("/Users/sakasegawa/src/github.com/nyosegawa/agent-task"),
"nyosegawa/agent-task"
);
}
#[test]
fn short_project_deep_path() {
assert_eq!(short_project("/a/b/c/d/e"), "d/e");
}
#[test]
fn short_project_single_segment() {
assert_eq!(short_project("repo"), "repo");
}
#[test]
fn short_project_trailing_slash() {
assert_eq!(short_project("/Users/x/owner/repo/"), "owner/repo");
}
#[test]
fn slug_from_ssh() {
assert_eq!(
extract_slug_from_url("git@github.com:nyosegawa/agent-task-web.git"),
Some("nyosegawa/agent-task-web".into())
);
}
#[test]
fn slug_from_ssh_scheme() {
assert_eq!(
extract_slug_from_url("ssh://git@github.com/nyosegawa/agent-task.git"),
Some("nyosegawa/agent-task".into())
);
}
#[test]
fn slug_from_https() {
assert_eq!(
extract_slug_from_url("https://github.com/owner/repo.git"),
Some("owner/repo".into())
);
}
#[test]
fn slug_from_https_no_suffix() {
assert_eq!(
extract_slug_from_url("https://github.com/owner/repo"),
Some("owner/repo".into())
);
}
#[test]
fn slug_invalid() {
assert_eq!(extract_slug_from_url("not-a-url"), None);
assert_eq!(extract_slug_from_url(""), None);
}
#[test]
fn normalize_macos_path() {
assert_eq!(
normalize_project("/Users/sakasegawa/src/github.com/nyosegawa/agent-task-web"),
"nyosegawa/agent-task-web"
);
}
#[test]
fn normalize_linux_path() {
assert_eq!(
normalize_project("/home/sakasegawa/src/github.com/nyosegawa/agent-task-web"),
"nyosegawa/agent-task-web"
);
}
#[test]
fn normalize_slug_passthrough() {
assert_eq!(
normalize_project("nyosegawa/agent-task-web"),
"nyosegawa/agent-task-web"
);
}
#[test]
fn normalize_macos_and_linux_match() {
let mac = normalize_project("/Users/sakasegawa/src/github.com/nyosegawa/agent-task-web");
let linux = normalize_project("/home/sakasegawa/src/github.com/nyosegawa/agent-task-web");
assert_eq!(mac, linux);
}
#[test]
fn normalize_non_github_path() {
assert_eq!(
normalize_project("/home/user/projects/my-app"),
"/home/user/projects/my-app"
);
}
}