use std::path::{Path, PathBuf};
use std::fs::{File, read_dir};
use std::io::{BufReader, BufRead};
use anyhow::Result;
use regex::Regex;
pub const PYTHON_VERSION_RE: &str = r#"VERSION\s*=\s*["'](?P<version>[0-9.^"']+)["']"#;
#[derive(Debug, PartialEq, Eq)]
pub enum ProjectType {
Rust(PathBuf),
Python(PathBuf),
Javascript(PathBuf),
Nofile,
}
fn is_rust<T: AsRef<Path>>(dir: T) -> Option<PathBuf> {
let cargo_toml = dir.as_ref().join("Cargo.toml");
cargo_toml.is_file().then_some(cargo_toml)
}
fn declares_version_python(file_path: &Path) -> Result<bool> {
if !file_path.is_file() {
return Ok(false);
}
let file = File::open(file_path)?;
let buf_reader = BufReader::new(file);
let version_re = Regex::new(PYTHON_VERSION_RE).unwrap();
for line in buf_reader.lines().filter_map(|l| l.ok()) {
if version_re.is_match(&line) {
return Ok(true);
}
}
Ok(false)
}
fn is_python<T: AsRef<Path>>(dir: T) -> Result<Option<PathBuf>> {
let pyproject = dir.as_ref().join("pyproject.toml");
if !pyproject.is_file() {
return Ok(None);
}
for entry in read_dir(dir)?.filter_map(|e| e.ok()).map(|e| e.path()).filter(|p| p.is_dir()) {
let version_txt = entry.join("__init__.py");
if declares_version_python(&version_txt)? {
return Ok(Some(version_txt));
}
}
Ok(None)
}
fn is_javascript<T: AsRef<Path>>(dir: T) -> Option<PathBuf> {
let package_json = dir.as_ref().join("package.json");
package_json.is_file().then_some(package_json)
}
pub fn detect_project_type<T: AsRef<Path>>(dir: T) -> anyhow::Result<ProjectType> {
if let Some(cargo_toml) = is_rust(dir.as_ref()) {
return Ok(ProjectType::Rust(cargo_toml));
}
if let Some(version_file) = is_python(dir.as_ref())? {
return Ok(ProjectType::Python(version_file));
}
if let Some(package_json) = is_javascript(dir.as_ref()) {
return Ok(ProjectType::Javascript(package_json));
}
Ok(ProjectType::Nofile)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn directories_are_properly_detected() {
assert_eq!(detect_project_type("./assets/test/rust_dir").unwrap(), ProjectType::Rust("./assets/test/rust_dir/Cargo.toml".into()));
assert_eq!(detect_project_type("./assets/test/python_dir").unwrap(), ProjectType::Python("./assets/test/python_dir/module/__init__.py".into()));
assert_eq!(detect_project_type("./assets/test/javascript_dir").unwrap(), ProjectType::Javascript("./assets/test/javascript_dir/package.json".into()));
}
}