paline 0.4.2

一个用 Rust 编写的代码行数统计命令行工具。递归扫描目录,按编程语言统计行数,并以彩色横向条形图在终端中可视化展示。
use crate::color::Color;
use std::path::Path;

/// 支持的编程语言 / 标记语言枚举。
///
/// `#[repr(u8)]` 保证各变体拥有连续下标(0..COUNT),可用固定数组累加统计;
/// 具体语言与扩展名的对应关系见 [`Languages::from_path`]。
/// 具体语言与颜色的对应关系见 [`Languages::to_rgb`]
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] //满足HashMap的键要求
pub enum Languages {
    Cpp,
    C,
    Cmake,
    Make,
    Ninja,
    Gradle,
    Maven,
    Python,
    Java,
    Javascript,
    Typescript,
    Go,
    Rust,
    Ruby,
    Php,
    Shell,
    Lua,
    R,
    Swift,
    Kotlin,
    Fsharp,
    FsharpScript,
    Csharp,
    VisualBasic,
    Assembly,
    Json,
    Yaml,
    Toml,
    Xml,
}

impl Languages {
    /// 按声明顺序排列的全部语言,用于遍历统计结果。
    pub const ALL: [Languages; 29] = [
        Self::Cpp,
        Self::C,
        Self::Cmake,
        Self::Make,
        Self::Ninja,
        Self::Gradle,
        Self::Maven,
        Self::Python,
        Self::Java,
        Self::Javascript,
        Self::Typescript,
        Self::Go,
        Self::Rust,
        Self::Ruby,
        Self::Php,
        Self::Shell,
        Self::Lua,
        Self::R,
        Self::Swift,
        Self::Kotlin,
        Self::Fsharp,
        Self::FsharpScript,
        Self::Csharp,
        Self::VisualBasic,
        Self::Assembly,
        Self::Json,
        Self::Yaml,
        Self::Toml,
        Self::Xml,
    ];

    /// 语言种类总数,也是固定数组累加器的长度。
    pub const COUNT: usize = Self::ALL.len();

    /// 通过文件名精确匹配的语言表(不依赖扩展名)。
    ///
    /// 例如 `Makefile` 应识别为 Make 语言,`pom.xml` 应识别为 Maven。
    const EXACT_FILENAMES: &[(&str, Self)] = &[
        ("CMakeLists.txt", Self::Cmake),
        ("Makefile", Self::Make),
        ("pom.xml", Self::Maven),
        ("build.gradle", Self::Gradle),
    ];

    /// 根据文件路径识别其所属语言。
    ///
    /// 先尝试文件名精确匹配(如 `Makefile`),失败后再按扩展名匹配,
    /// 均无法识别时返回 [`None`]。
    pub fn from_path(path: &Path) -> Option<Self> {
        let file_name = path.file_name()?.to_str()?;
        for (name, lang) in Self::EXACT_FILENAMES {
            if file_name == *name {
                return Some(*lang);
            }
        }
        let ext = path.extension()?.to_str()?;

        Some(match ext {
            "c" | "h" => Self::C,
            "cpp" | "hpp" | "cxx" | "cc" | "hxx" | "hh" | "inl" => Self::Cpp,
            "cmake" => Self::Cmake,
            "mk" => Self::Make,
            "ninja" => Self::Ninja,
            "gradle" => Self::Gradle,
            "py" | "pyw" | "pyi" => Self::Python,
            "java" => Self::Java,
            "js" | "mjs" | "cjs" => Self::Javascript,
            "ts" | "tsx" => Self::Typescript,
            "go" => Self::Go,
            "rs" => Self::Rust,
            "rb" | "rake" | "gemspec" => Self::Ruby,
            "php" | "phtml" => Self::Php,
            "sh" | "bash" | "zsh" => Self::Shell,
            "lua" => Self::Lua,
            "R" | "r" => Self::R,
            "swift" => Self::Swift,
            "kt" | "kts" => Self::Kotlin,
            "fs" | "fsi" => Self::Fsharp,
            "fsx" => Self::FsharpScript,
            "cs" => Self::Csharp,
            "vb" => Self::VisualBasic,
            "s" | "asm" => Self::Assembly,
            "json" => Self::Json,
            "yaml" | "yml" => Self::Yaml,
            "toml" => Self::Toml,
            "xml" => Self::Xml,
            _ => return None,
        })
    }

    /// 返回该语言在数组累加器中的下标。
    pub const fn idx(self) -> usize {
        self as usize
    }

    /// 返回该语言用于展示的名称
    pub const fn to_str(self) -> &'static str {
        match self {
            Languages::Cpp => "Cpp",
            Languages::C => "C",
            Languages::Cmake => "CMake",
            Languages::Make => "Make",
            Languages::Ninja => "Ninja",
            Languages::Gradle => "Gradle",
            Languages::Maven => "Maven",
            Languages::Python => "Python",
            Languages::Java => "Java",
            Languages::Javascript => "JavaScript",
            Languages::Typescript => "TypeScript",
            Languages::Go => "Go",
            Languages::Rust => "Rust",
            Languages::Ruby => "Ruby",
            Languages::Php => "PHP",
            Languages::Shell => "Shell",
            Languages::Lua => "Lua",
            Languages::R => "R",
            Languages::Swift => "Swift",
            Languages::Kotlin => "Kotlin",
            Languages::Fsharp => "FSharp",
            Languages::FsharpScript => "FSharpScript",
            Languages::Csharp => "CSharp",
            Languages::VisualBasic => "VisualBasic",
            Languages::Assembly => "Assembly",
            Languages::Json => "JSON",
            Languages::Yaml => "YAML",
            Languages::Toml => "TOML",
            Languages::Xml => "XML",
        }
    }

    /// 返回某语言在展示中使用的标识色(RGB)。
    pub const fn to_rgb(self) -> Color {
        match self {
            Languages::C => Color::new(85, 85, 85),
            Languages::Cpp => Color::new(243, 75, 125),
            Languages::Cmake => Color::new(218, 52, 52),
            Languages::Make => Color::new(66, 120, 25),
            Languages::Ninja => Color::new(128, 0, 255),
            Languages::Gradle => Color::new(2, 48, 58),
            Languages::Maven => Color::new(199, 26, 54),
            Languages::Python => Color::new(53, 114, 165),
            Languages::Java => Color::new(176, 114, 25),
            Languages::Javascript => Color::new(241, 224, 90),
            Languages::Typescript => Color::new(49, 120, 198),
            Languages::Go => Color::new(0, 173, 216),
            Languages::Rust => Color::new(222, 165, 132),
            Languages::Ruby => Color::new(112, 21, 22),
            Languages::Php => Color::new(79, 93, 149),
            Languages::Shell => Color::new(137, 224, 81),
            Languages::Lua => Color::new(0, 0, 128),
            Languages::R => Color::new(25, 140, 231),
            Languages::Swift => Color::new(240, 81, 56),
            Languages::Kotlin => Color::new(169, 123, 255),
            Languages::Fsharp => Color::new(184, 69, 252),
            Languages::FsharpScript => Color::new(184, 69, 252),
            Languages::Csharp => Color::new(23, 134, 0),
            Languages::VisualBasic => Color::new(148, 93, 183),
            Languages::Assembly => Color::new(110, 76, 19),
            Languages::Json => Color::new(139, 148, 158),
            Languages::Yaml => Color::new(203, 23, 30),
            Languages::Toml => Color::new(156, 66, 33),
            Languages::Xml => Color::new(0, 96, 172),
        }
    }
}