use std::fs;
use std::path::Path;
pub fn detect_crate_name(project_root: &Path, _file_path: &Path) -> String {
let cargo_toml = project_root.join("Cargo.toml");
if let Ok(content) = fs::read_to_string(&cargo_toml) {
if let Some(name) = parse_cargo_toml_name(&content) {
return name;
}
} else {
}
if let Some(dir_name) = project_root.file_name() {
if let Some(name) = dir_name.to_str() {
return name.to_string();
}
}
"unknown".to_string()
}
fn parse_cargo_toml_name(content: &str) -> Option<String> {
let mut in_package_section = false;
for line in content.lines() {
let trimmed = line.trim();
if trimmed == "[package]" {
in_package_section = true;
continue;
}
if trimmed.starts_with('[') {
if in_package_section && trimmed != "[package]" {
break;
}
continue;
}
if in_package_section && trimmed.starts_with("name") && trimmed.contains('=') {
return extract_quoted_value(trimmed);
}
}
None
}
fn extract_quoted_value(line: &str) -> Option<String> {
let eq_pos = line.find('=')?;
let after_eq = &line[eq_pos + 1..];
let trimmed = after_eq.trim_start();
let quote_char = trimmed.chars().next()?;
if quote_char != '"' && quote_char != '\'' {
return None;
}
let quote_offset = trimmed.find(quote_char)?;
let start = quote_offset + 1;
let remaining = &trimmed[start..];
let end = remaining.find(quote_char)?;
Some(remaining[..end].to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_quoted_value_double_quotes() {
assert_eq!(
extract_quoted_value(r#"name = "my-crate""#),
Some("my-crate".to_string())
);
}
#[test]
fn test_extract_quoted_value_single_quotes() {
assert_eq!(
extract_quoted_value(r#"name = 'my-crate'"#),
Some("my-crate".to_string())
);
}
#[test]
fn test_extract_quoted_value_no_quotes() {
assert_eq!(extract_quoted_value(r#"name = my-crate"#), None);
}
#[test]
fn test_extract_quoted_value_empty() {
assert_eq!(extract_quoted_value(r#"name = """#), Some("".to_string()));
}
#[test]
fn test_parse_cargo_toml_name_valid() {
let content = r#"
[package]
name = "test-crate"
version = "0.1.0"
[dependencies]
"#;
assert_eq!(
parse_cargo_toml_name(content),
Some("test-crate".to_string())
);
}
#[test]
fn test_parse_cargo_toml_name_with_spaces() {
let content = r#"
[package]
name = "test-crate-with-spaces"
"#;
assert_eq!(
parse_cargo_toml_name(content),
Some("test-crate-with-spaces".to_string())
);
}
#[test]
fn test_parse_cargo_toml_name_empty() {
assert_eq!(parse_cargo_toml_name(""), None);
}
#[test]
fn test_parse_cargo_toml_name_no_package_section() {
let content = r#"
[dependencies]
serde = "1.0"
"#;
assert_eq!(parse_cargo_toml_name(content), None);
}
#[test]
fn test_parse_cargo_toml_name_no_name_field() {
let content = r#"
[package]
version = "0.1.0"
"#;
assert_eq!(parse_cargo_toml_name(content), None);
}
#[test]
fn test_parse_cargo_toml_name_stops_at_next_section() {
let content = r#"
[package]
version = "0.1.0"
[package.metadata]
name = "should-not-pick-this"
[dependencies]
"#;
assert_eq!(parse_cargo_toml_name(content), None);
}
#[test]
fn test_detect_from_cargo_toml() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let cargo_toml = temp_dir.path().join("Cargo.toml");
fs::write(
&cargo_toml,
r#"
[package]
name = "test-crate"
version = "0.1.0"
"#,
)
.unwrap();
let file_path = temp_dir.path().join("src/lib.rs");
let result = detect_crate_name(temp_dir.path(), &file_path);
assert_eq!(result, "test-crate");
}
#[test]
fn test_fallback_to_directory_name() {
use tempfile::TempDir;
let temp_dir = TempDir::with_prefix("my-test-crate-").unwrap();
let file_path = temp_dir.path().join("src/lib.rs");
let result = detect_crate_name(temp_dir.path(), &file_path);
assert!(result.starts_with("my-test-crate-"));
}
#[test]
fn test_unknown_fallback_empty_string_name() {
let file_path = std::path::PathBuf::from("/tmp/file.rs");
let result = detect_crate_name(std::path::Path::new("/"), &file_path);
assert_eq!(result, "unknown");
}
#[test]
fn test_malformed_cargo_toml() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let cargo_toml = temp_dir.path().join("Cargo.toml");
fs::write(&cargo_toml, "not valid toml at all!!!").unwrap();
let file_path = temp_dir.path().join("src/lib.rs");
let result = detect_crate_name(temp_dir.path(), &file_path);
assert!(!result.is_empty());
assert_ne!(result, "unknown"); }
#[test]
fn test_missing_name_field() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let cargo_toml = temp_dir.path().join("Cargo.toml");
fs::write(
&cargo_toml,
r#"
[package]
version = "0.1.0"
"#,
)
.unwrap();
let file_path = temp_dir.path().join("src/lib.rs");
let result = detect_crate_name(temp_dir.path(), &file_path);
assert!(!result.is_empty());
assert_ne!(result, "unknown"); }
#[test]
fn test_cargo_toml_with_special_characters() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let cargo_toml = temp_dir.path().join("Cargo.toml");
fs::write(
&cargo_toml,
r#"
[package]
name = "my-awesome-crate-v2"
version = "0.1.0"
"#,
)
.unwrap();
let file_path = temp_dir.path().join("src/lib.rs");
let result = detect_crate_name(temp_dir.path(), &file_path);
assert_eq!(result, "my-awesome-crate-v2");
}
}