actr-cli 0.2.1

Command line tool for Actor-RTC framework projects
Documentation
use std::{fmt, path::Path};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DetectedProjectLanguage {
    Rust,
    Swift,
    Kotlin,
    Python,
    TypeScript,
    Ambiguous,
    Unknown,
}

impl DetectedProjectLanguage {
    pub fn detect(root: &Path) -> Self {
        let has_swift = root.join("Package.swift").exists() || root.join("project.yml").exists();
        let has_kotlin =
            root.join("build.gradle.kts").exists() || root.join("build.gradle").exists();
        let has_python =
            root.join("pyproject.toml").exists() || root.join("requirements.txt").exists();
        let has_typescript =
            root.join("tsconfig.json").exists() || root.join("package.json").exists();
        let has_rust = root.join("Cargo.toml").exists();

        let matched_count = [has_swift, has_kotlin, has_python, has_typescript, has_rust]
            .into_iter()
            .filter(|matched| *matched)
            .count();

        match matched_count {
            0 => Self::Unknown,
            1 => {
                if has_swift {
                    Self::Swift
                } else if has_kotlin {
                    Self::Kotlin
                } else if has_python {
                    Self::Python
                } else if has_typescript {
                    Self::TypeScript
                } else {
                    Self::Rust
                }
            }
            _ => Self::Ambiguous,
        }
    }

    pub fn cli_name(self) -> &'static str {
        match self {
            Self::Rust => "rust",
            Self::Swift => "swift",
            Self::Kotlin => "kotlin",
            Self::Python => "python",
            Self::TypeScript => "typescript",
            Self::Ambiguous => "ambiguous",
            Self::Unknown => "unknown",
        }
    }
}

impl fmt::Display for DetectedProjectLanguage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.cli_name())
    }
}

#[cfg(test)]
mod tests {
    use super::DetectedProjectLanguage;
    use std::fs;
    use tempfile::TempDir;

    #[test]
    fn detects_rust_from_cargo_toml() {
        let dir = TempDir::new().unwrap();
        fs::write(
            dir.path().join("Cargo.toml"),
            "[package]\nname = \"demo\"\n",
        )
        .unwrap();

        assert_eq!(
            DetectedProjectLanguage::detect(dir.path()),
            DetectedProjectLanguage::Rust
        );
    }

    #[test]
    fn returns_unknown_without_language_markers() {
        let dir = TempDir::new().unwrap();

        assert_eq!(
            DetectedProjectLanguage::detect(dir.path()),
            DetectedProjectLanguage::Unknown
        );
    }

    #[test]
    fn returns_ambiguous_with_multiple_language_markers() {
        let dir = TempDir::new().unwrap();
        fs::write(
            dir.path().join("Cargo.toml"),
            "[package]\nname = \"demo\"\n",
        )
        .unwrap();
        fs::write(
            dir.path().join("package.json"),
            "{\n  \"name\": \"demo\"\n}\n",
        )
        .unwrap();

        assert_eq!(
            DetectedProjectLanguage::detect(dir.path()),
            DetectedProjectLanguage::Ambiguous
        );
    }
}