ferrous_opencc/
config.rs

1use crate::error::{OpenCCError, Result};
2use serde::{Deserialize, Serialize};
3use std::fs::File;
4use std::io::BufReader;
5use std::path::{Path, PathBuf};
6use std::str::FromStr;
7
8#[cfg(feature = "wasm")]
9use wasm_bindgen::prelude::*;
10
11/// 顶层的 JSON 配置结构
12#[derive(Serialize, Deserialize, Debug)]
13pub struct Config {
14    /// 转换配置的名称
15    pub name: String,
16    /// 转换步骤链
17    pub conversion_chain: Vec<ConversionNodeConfig>,
18
19    /// 配置文件所在的目录
20    #[serde(skip)]
21    config_directory: PathBuf,
22}
23
24/// 所有内置的 OpenCC 配置
25#[repr(i32)]
26#[cfg_attr(feature = "wasm", wasm_bindgen)]
27#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
28pub enum BuiltinConfig {
29    /// 简体到繁体
30    S2t = 0,
31    /// 繁体到简体
32    T2s = 1,
33    /// 简体到台湾正体
34    S2tw = 2,
35    /// 台湾正体到简体
36    Tw2s = 3,
37    /// 简体到香港繁体
38    S2hk = 4,
39    /// 香港繁体到简体
40    Hk2s = 5,
41    /// 简体到台湾正体(包含词汇转换)
42    S2twp = 6,
43    /// 台湾正体(包含词汇转换)到简体
44    Tw2sp = 7,
45    /// 繁体到台湾正体
46    T2tw = 8,
47    /// 台湾正体到繁体
48    Tw2t = 9,
49    /// 繁体到香港繁体
50    T2hk = 10,
51    /// 香港繁体到繁体
52    Hk2t = 11,
53    /// 日语新字体到繁体
54    Jp2t = 12,
55    /// 繁体到日语新字体
56    T2jp = 13,
57}
58
59impl BuiltinConfig {
60    /// 将枚举成员转换为对应的文件名字符串
61    pub fn to_filename(&self) -> &'static str {
62        match self {
63            BuiltinConfig::S2t => "s2t.json",
64            BuiltinConfig::T2s => "t2s.json",
65            BuiltinConfig::S2tw => "s2tw.json",
66            BuiltinConfig::Tw2s => "tw2s.json",
67            BuiltinConfig::S2hk => "s2hk.json",
68            BuiltinConfig::Hk2s => "hk2s.json",
69            BuiltinConfig::S2twp => "s2twp.json",
70            BuiltinConfig::Tw2sp => "tw2sp.json",
71            BuiltinConfig::T2tw => "t2tw.json",
72            BuiltinConfig::Tw2t => "tw2t.json",
73            BuiltinConfig::T2hk => "t2hk.json",
74            BuiltinConfig::Hk2t => "hk2t.json",
75            BuiltinConfig::Jp2t => "jp2t.json",
76            BuiltinConfig::T2jp => "t2jp.json",
77        }
78    }
79
80    /// 从文件名字符串转换为对应的枚举成员
81    pub fn from_filename(filename: &str) -> Result<Self> {
82        match filename {
83            "s2t.json" => Ok(BuiltinConfig::S2t),
84            "t2s.json" => Ok(BuiltinConfig::T2s),
85            "s2tw.json" => Ok(BuiltinConfig::S2tw),
86            "tw2s.json" => Ok(BuiltinConfig::Tw2s),
87            "s2hk.json" => Ok(BuiltinConfig::S2hk),
88            "hk2s.json" => Ok(BuiltinConfig::Hk2s),
89            "s2twp.json" => Ok(BuiltinConfig::S2twp),
90            "tw2sp.json" => Ok(BuiltinConfig::Tw2sp),
91            "t2tw.json" => Ok(BuiltinConfig::T2tw),
92            "tw2t.json" => Ok(BuiltinConfig::Tw2t),
93            "t2hk.json" => Ok(BuiltinConfig::T2hk),
94            "hk2t.json" => Ok(BuiltinConfig::Hk2t),
95            "jp2t.json" => Ok(BuiltinConfig::Jp2t),
96            "t2jp.json" => Ok(BuiltinConfig::T2jp),
97            _ => Err(OpenCCError::ConfigNotFound(filename.to_string())),
98        }
99    }
100}
101
102/// 转换链中的一个节点
103/// 每个节点对应一个基于词典的转换步骤
104#[derive(Serialize, Deserialize, Debug)]
105pub struct ConversionNodeConfig {
106    /// 此转换步骤要使用的词典
107    pub dict: DictConfig,
108}
109
110/// 代表一个词典配置,可以是一个单独的词典文件,也可以是一组词典
111#[derive(Serialize, Deserialize, Debug)]
112pub struct DictConfig {
113    /// 词典的类型,例如 "text" 或 "group"
114    #[serde(rename = "type")]
115    pub dict_type: String,
116    /// 词典文件名 (用于 `type: "text"`)
117    pub file: Option<String>,
118    /// 子词典列表 (用于 `type: "group"`)。
119    pub dicts: Option<Vec<DictConfig>>,
120}
121
122impl Config {
123    /// 从 JSON 文件加载并解析配置
124    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
125        let path = path.as_ref();
126        let file = File::open(path)
127            .map_err(|e| OpenCCError::FileNotFound(format!("{}: {}", path.display(), e)))?;
128        let reader = BufReader::new(file);
129        let mut config: Config = serde_json::from_reader(reader)?;
130
131        // 保存配置文件的父目录
132        config.config_directory = path.parent().unwrap_or_else(|| Path::new("")).to_path_buf();
133
134        Ok(config)
135    }
136
137    /// 获取配置文件所在的目录
138    pub fn get_config_directory(&self) -> &Path {
139        &self.config_directory
140    }
141}
142
143impl FromStr for Config {
144    type Err = OpenCCError;
145
146    fn from_str(s: &str) -> Result<Self> {
147        let config: Config = serde_json::from_str(s)?;
148        Ok(config)
149    }
150}