use super::{DetectionResult, ProjectDetector, ProjectType};
use crate::error::{cargo_toml_error, Result};
use cargo_toml::Manifest;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
pub struct RustDetector;
impl ProjectDetector for RustDetector {
fn detect(&self, path: &Path) -> Result<Option<DetectionResult>> {
let cargo_toml_path = path.join("Cargo.toml");
if !cargo_toml_path.exists() {
return Ok(None);
}
let contents = fs::read_to_string(&cargo_toml_path)?;
let manifest = Manifest::from_str(&contents)
.map_err(|e| cargo_toml_error(e.to_string()))?;
let mut metadata = HashMap::new();
if let Some(workspace) = &manifest.workspace {
metadata.insert("type".to_string(), "workspace".to_string());
let members = &workspace.members;
metadata.insert("members".to_string(), members.join(", "));
metadata.insert("member_count".to_string(), members.len().to_string());
return Ok(Some(DetectionResult {
project_type: ProjectType::RustWorkspace,
language_version: extract_rust_version(&manifest),
metadata,
}));
}
let project_type = if has_library(&manifest) {
metadata.insert("type".to_string(), "library".to_string());
ProjectType::RustLibrary
} else if has_binary(&manifest) {
metadata.insert("type".to_string(), "binary".to_string());
ProjectType::RustBinary
} else {
metadata.insert("type".to_string(), "binary (assumed)".to_string());
ProjectType::RustBinary
};
if let Some(package) = &manifest.package {
metadata.insert("name".to_string(), package.name().to_string());
}
Ok(Some(DetectionResult {
project_type,
language_version: extract_rust_version(&manifest),
metadata,
}))
}
fn name(&self) -> &str {
"Rust"
}
}
fn has_library(manifest: &Manifest) -> bool {
manifest.lib.is_some()
}
fn has_binary(manifest: &Manifest) -> bool {
!manifest.bin.is_empty() || manifest.package.as_ref()
.and_then(|p| p.default_run.as_ref())
.is_some()
}
fn extract_rust_version(_manifest: &Manifest) -> Option<String> {
Some("stable".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_detect_rust_library() {
let dir = tempdir().unwrap();
let cargo_toml = dir.path().join("Cargo.toml");
fs::write(&cargo_toml, r#"
[package]
name = "test-lib"
version = "0.1.0"
edition = "2021"
[lib]
name = "test_lib"
"#).unwrap();
let detector = RustDetector;
let result = detector.detect(dir.path()).unwrap().unwrap();
assert_eq!(result.project_type, ProjectType::RustLibrary);
assert_eq!(result.metadata.get("name").unwrap(), "test-lib");
}
#[test]
fn test_detect_rust_binary() {
let dir = tempdir().unwrap();
let cargo_toml = dir.path().join("Cargo.toml");
fs::write(&cargo_toml, r#"
[package]
name = "test-bin"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "test-bin"
"#).unwrap();
let detector = RustDetector;
let result = detector.detect(dir.path()).unwrap().unwrap();
assert_eq!(result.project_type, ProjectType::RustBinary);
}
#[test]
fn test_detect_rust_workspace() {
let dir = tempdir().unwrap();
let cargo_toml = dir.path().join("Cargo.toml");
fs::write(&cargo_toml, r#"
[workspace]
members = ["crate1", "crate2"]
"#).unwrap();
let detector = RustDetector;
let result = detector.detect(dir.path()).unwrap().unwrap();
assert_eq!(result.project_type, ProjectType::RustWorkspace);
assert_eq!(result.metadata.get("member_count").unwrap(), "2");
}
#[test]
fn test_no_cargo_toml() {
let dir = tempdir().unwrap();
let detector = RustDetector;
let result = detector.detect(dir.path()).unwrap();
assert!(result.is_none());
}
}