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#[derive(Serialize, Deserialize, Debug)]
10pub struct Config {
11 pub name: String,
13 pub conversion_chain: Vec<ConversionNodeConfig>,
15
16 #[serde(skip)]
18 directory: PathBuf,
19}
20
21#[repr(i32)]
23#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
24pub enum BuiltinConfig {
25 #[cfg(feature = "s2t-conversion")]
27 S2t = 0,
28 #[cfg(feature = "t2s-conversion")]
30 T2s = 1,
31 #[cfg(feature = "s2t-conversion")]
33 S2tw = 2,
34 #[cfg(feature = "t2s-conversion")]
36 Tw2s = 3,
37 #[cfg(feature = "s2t-conversion")]
39 S2hk = 4,
40 #[cfg(feature = "t2s-conversion")]
42 Hk2s = 5,
43 #[cfg(feature = "s2t-conversion")]
45 S2twp = 6,
46 #[cfg(feature = "t2s-conversion")]
48 Tw2sp = 7,
49 #[cfg(feature = "t2s-conversion")]
51 T2tw = 8,
52 #[cfg(feature = "s2t-conversion")]
54 Tw2t = 9,
55 #[cfg(feature = "s2t-conversion")]
57 T2hk = 10,
58 #[cfg(feature = "t2s-conversion")]
60 Hk2t = 11,
61 #[cfg(feature = "japanese-conversion")]
63 Jp2t = 12,
64 #[cfg(feature = "japanese-conversion")]
66 T2jp = 13,
67}
68
69impl BuiltinConfig {
70 #[must_use]
72 pub const fn to_filename(&self) -> &'static str {
73 match self {
74 #[cfg(feature = "s2t-conversion")]
75 Self::S2t => "s2t.json",
76 #[cfg(feature = "t2s-conversion")]
77 Self::T2s => "t2s.json",
78 #[cfg(feature = "s2t-conversion")]
79 Self::S2tw => "s2tw.json",
80 #[cfg(feature = "t2s-conversion")]
81 Self::Tw2s => "tw2s.json",
82 #[cfg(feature = "s2t-conversion")]
83 Self::S2hk => "s2hk.json",
84 #[cfg(feature = "t2s-conversion")]
85 Self::Hk2s => "hk2s.json",
86 #[cfg(feature = "s2t-conversion")]
87 Self::S2twp => "s2twp.json",
88 #[cfg(feature = "t2s-conversion")]
89 Self::Tw2sp => "tw2sp.json",
90 #[cfg(feature = "t2s-conversion")]
91 Self::T2tw => "t2tw.json",
92 #[cfg(feature = "s2t-conversion")]
93 Self::Tw2t => "tw2t.json",
94 #[cfg(feature = "s2t-conversion")]
95 Self::T2hk => "t2hk.json",
96 #[cfg(feature = "t2s-conversion")]
97 Self::Hk2t => "hk2t.json",
98 #[cfg(feature = "japanese-conversion")]
99 Self::Jp2t => "jp2t.json",
100 #[cfg(feature = "japanese-conversion")]
101 Self::T2jp => "t2jp.json",
102 }
103 }
104
105 pub fn from_filename(filename: &str) -> Result<Self> {
107 match filename {
108 #[cfg(feature = "s2t-conversion")]
109 "s2t.json" => Ok(Self::S2t),
110 #[cfg(feature = "t2s-conversion")]
111 "t2s.json" => Ok(Self::T2s),
112 #[cfg(feature = "s2t-conversion")]
113 "s2tw.json" => Ok(Self::S2tw),
114 #[cfg(feature = "t2s-conversion")]
115 "tw2s.json" => Ok(Self::Tw2s),
116 #[cfg(feature = "s2t-conversion")]
117 "s2hk.json" => Ok(Self::S2hk),
118 #[cfg(feature = "t2s-conversion")]
119 "hk2s.json" => Ok(Self::Hk2s),
120 #[cfg(feature = "s2t-conversion")]
121 "s2twp.json" => Ok(Self::S2twp),
122 #[cfg(feature = "t2s-conversion")]
123 "tw2sp.json" => Ok(Self::Tw2sp),
124 #[cfg(feature = "t2s-conversion")]
125 "t2tw.json" => Ok(Self::T2tw),
126 #[cfg(feature = "s2t-conversion")]
127 "tw2t.json" => Ok(Self::Tw2t),
128 #[cfg(feature = "s2t-conversion")]
129 "t2hk.json" => Ok(Self::T2hk),
130 #[cfg(feature = "t2s-conversion")]
131 "hk2t.json" => Ok(Self::Hk2t),
132 #[cfg(feature = "japanese-conversion")]
133 "jp2t.json" => Ok(Self::Jp2t),
134 #[cfg(feature = "japanese-conversion")]
135 "t2jp.json" => Ok(Self::T2jp),
136 _ => Err(OpenCCError::ConfigNotFound(filename.to_string())),
137 }
138 }
139}
140
141#[derive(Serialize, Deserialize, Debug)]
145pub struct ConversionNodeConfig {
146 pub dict: DictConfig,
148}
149
150#[derive(Serialize, Deserialize, Debug)]
152pub struct DictConfig {
153 #[serde(rename = "type")]
155 pub dict_type: String,
156 pub file: Option<String>,
158 pub dicts: Option<Vec<Self>>,
160}
161
162impl Config {
163 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
165 let path = path.as_ref();
166 let file = File::open(path)
167 .map_err(|e| OpenCCError::FileNotFound(format!("{}: {}", path.display(), e)))?;
168 let reader = BufReader::new(file);
169 let mut config: Self = serde_json::from_reader(reader)?;
170
171 config.directory = path.parent().unwrap_or_else(|| Path::new("")).to_path_buf();
173
174 Ok(config)
175 }
176
177 #[must_use]
179 pub fn get_config_directory(&self) -> &Path {
180 &self.directory
181 }
182}
183
184impl FromStr for Config {
185 type Err = OpenCCError;
186
187 fn from_str(s: &str) -> Result<Self> {
188 let config: Self = serde_json::from_str(s)?;
189 Ok(config)
190 }
191}