simpleicons 0.1.0

A Rust library for loading and querying simple-icons data from a JSON file, providing convenient access to icon metadata by name.
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;

#[derive(Debug, Deserialize, Clone)]
pub struct IconData {
    pub title: String,
    pub hex: String,
    pub source: String,
    pub aliases: Option<Aliases>,
    pub guidelines: Option<String>,
    pub license: Option<License>,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Aliases {
    pub aka: Option<Vec<String>>,
}

#[derive(Debug, Deserialize, Clone)]
pub struct License {
    #[serde(rename = "type")]
    pub license_type: Option<String>,
}

pub struct Icon {
    icons: Vec<IconData>,
    name_map: HashMap<String, usize>,
}

impl Icon {
    /// 加载 data/simple-icons.json,初始化 Icon 实例
    pub fn new() -> Self {
        let file = File::open("data/simple-icons.json").expect("无法打开 simple-icons.json");
        let reader = BufReader::new(file);
        let icons: Vec<IconData> = serde_json::from_reader(reader).expect("JSON 解析失败");

        // 构建名称到索引的映射(title 和 aka)
        let mut name_map = HashMap::new();
        for (i, icon) in icons.iter().enumerate() {
            // title 原样和小写
            name_map.insert(icon.title.clone(), i);
            name_map.insert(icon.title.to_lowercase(), i);
            if let Some(aliases) = &icon.aliases {
                if let Some(aka_vec) = &aliases.aka {
                    for aka in aka_vec {
                        name_map.insert(aka.clone(), i);
                        name_map.insert(aka.to_lowercase(), i);
                    }
                }
            }
        }

        Icon { icons, name_map }
    }

    /// 通过名称(title 或 aka)获取 icon 数据对象
    pub fn get(&self, name: &str) -> Option<&IconData> {
        self.name_map
            .get(&name.to_lowercase())
            .and_then(|&i| self.icons.get(i))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_icon() {
        let icon_lib = Icon::new();
        let icon = icon_lib.get(".env").expect("未找到 .env");
        assert_eq!(icon.title, ".ENV");
        let icon2 = icon_lib.get("Dotenv").expect("未找到 Dotenv aka");
        assert_eq!(icon2.title, ".ENV");
        let icon3 = icon_lib.get("不存在的名称");
        assert!(icon3.is_none());
    }
}