use std::path::{Path, PathBuf}; #[derive(Debug, PartialEq)] pub enum ProjectType { Maven, Npm, Cargo } impl ProjectType { fn detect>(path: P) -> Option { let project_path: PathBuf = path.as_ref().into(); if project_path.join("pom.xml").is_file() { return Some(ProjectType::Maven); } else if project_path.join("package.json").is_file() { return Some(ProjectType::Npm); } else if project_path.join("Cargo.toml").is_file() { return Some(ProjectType::Cargo); } None } } #[derive(Debug)] pub struct Project { pub project_type: ProjectType, path: PathBuf } impl Project { pub fn path(&self) -> &Path { &self.path } pub fn info(&self) -> ProjectInfo { match self.project_type { ProjectType::Maven => ProjectInfo { name: "placeholder".into(), version: "placeholder".into() }, ProjectType::Npm => ProjectInfo { name: "placeholder".into(), version: "placeholder".into() }, ProjectType::Cargo => self.read_cargo_metadata().into() } } fn read_cargo_metadata(&self) -> cargo_metadata::Metadata { cargo_metadata::MetadataCommand::new() .current_dir(&self.path) .exec() .expect("metadata call failed") } } #[derive(Debug)] pub struct ProjectInfo { pub name: String, pub version: String } impl From for ProjectInfo { fn from(metadata: cargo_metadata::Metadata) -> Self { let root_package_id = metadata.resolve .expect("non-existent resolve field") .root .expect("non-existent root package id"); let package = metadata.packages.into_iter() .find(|package| package.id == root_package_id) .expect("failed to find package in metadata"); ProjectInfo { name: package.name.into(), version: format!("{}", package.version) } } } pub struct Scouter { search_path: PathBuf } impl Scouter { pub fn new>(path: P) -> Scouter { let mut search_path: PathBuf = path.as_ref().into(); if !search_path.is_dir() { search_path.pop(); } Scouter { search_path: search_path } } } impl Iterator for Scouter { type Item = Project; fn next(&mut self) -> Option { loop { let project_type = ProjectType::detect(&self.search_path); if project_type.is_some() { return project_type.map(|project_type| Project { project_type: project_type, path: self.search_path.to_owned() }); } if !self.search_path.pop() { return None; } } } } #[cfg(test)] mod test { use super::{ProjectType, Scouter}; #[test] fn test_scouter() { let mut scouter = Scouter::new(file!()); let project = scouter.next().unwrap(); let project_info = project.info(); assert_eq!(ProjectType::Cargo, project.project_type); assert_eq!(env!("CARGO_PKG_NAME"), project_info.name); assert_eq!(env!("CARGO_PKG_VERSION"), project_info.version); } }