1use std::{
2 fs::File,
3 io::BufReader,
4 path::{
5 Path,
6 PathBuf,
7 },
8 str::FromStr,
9};
10
11use serde::{
12 Deserialize,
13 Serialize,
14};
15
16use crate::error::{
17 OpenCCError,
18 Result,
19};
20
21#[derive(Serialize, Deserialize, Debug)]
23pub struct Config {
24 pub name: String,
26 pub conversion_chain: Vec<ConversionNodeConfig>,
28
29 #[serde(skip)]
31 directory: PathBuf,
32}
33
34#[repr(i32)]
36#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
37pub enum BuiltinConfig {
38 #[cfg(feature = "s2t-conversion")]
40 S2t = 0,
41 #[cfg(feature = "t2s-conversion")]
43 T2s = 1,
44 #[cfg(feature = "s2t-conversion")]
46 S2tw = 2,
47 #[cfg(feature = "t2s-conversion")]
49 Tw2s = 3,
50 #[cfg(feature = "s2t-conversion")]
52 S2hk = 4,
53 #[cfg(feature = "t2s-conversion")]
55 Hk2s = 5,
56 #[cfg(feature = "s2t-conversion")]
58 S2twp = 6,
59 #[cfg(feature = "t2s-conversion")]
61 Tw2sp = 7,
62 #[cfg(feature = "t2s-conversion")]
64 T2tw = 8,
65 #[cfg(feature = "s2t-conversion")]
67 Tw2t = 9,
68 #[cfg(feature = "s2t-conversion")]
70 T2hk = 10,
71 #[cfg(feature = "t2s-conversion")]
73 Hk2t = 11,
74 #[cfg(feature = "japanese-conversion")]
76 Jp2t = 12,
77 #[cfg(feature = "japanese-conversion")]
79 T2jp = 13,
80}
81
82impl BuiltinConfig {
83 #[must_use]
85 pub const fn to_filename(&self) -> &'static str {
86 match self {
87 #[cfg(feature = "s2t-conversion")]
88 Self::S2t => "s2t.json",
89 #[cfg(feature = "t2s-conversion")]
90 Self::T2s => "t2s.json",
91 #[cfg(feature = "s2t-conversion")]
92 Self::S2tw => "s2tw.json",
93 #[cfg(feature = "t2s-conversion")]
94 Self::Tw2s => "tw2s.json",
95 #[cfg(feature = "s2t-conversion")]
96 Self::S2hk => "s2hk.json",
97 #[cfg(feature = "t2s-conversion")]
98 Self::Hk2s => "hk2s.json",
99 #[cfg(feature = "s2t-conversion")]
100 Self::S2twp => "s2twp.json",
101 #[cfg(feature = "t2s-conversion")]
102 Self::Tw2sp => "tw2sp.json",
103 #[cfg(feature = "t2s-conversion")]
104 Self::T2tw => "t2tw.json",
105 #[cfg(feature = "s2t-conversion")]
106 Self::Tw2t => "tw2t.json",
107 #[cfg(feature = "s2t-conversion")]
108 Self::T2hk => "t2hk.json",
109 #[cfg(feature = "t2s-conversion")]
110 Self::Hk2t => "hk2t.json",
111 #[cfg(feature = "japanese-conversion")]
112 Self::Jp2t => "jp2t.json",
113 #[cfg(feature = "japanese-conversion")]
114 Self::T2jp => "t2jp.json",
115 }
116 }
117
118 pub fn from_filename(filename: &str) -> Result<Self> {
120 match filename {
121 #[cfg(feature = "s2t-conversion")]
122 "s2t.json" => Ok(Self::S2t),
123 #[cfg(feature = "t2s-conversion")]
124 "t2s.json" => Ok(Self::T2s),
125 #[cfg(feature = "s2t-conversion")]
126 "s2tw.json" => Ok(Self::S2tw),
127 #[cfg(feature = "t2s-conversion")]
128 "tw2s.json" => Ok(Self::Tw2s),
129 #[cfg(feature = "s2t-conversion")]
130 "s2hk.json" => Ok(Self::S2hk),
131 #[cfg(feature = "t2s-conversion")]
132 "hk2s.json" => Ok(Self::Hk2s),
133 #[cfg(feature = "s2t-conversion")]
134 "s2twp.json" => Ok(Self::S2twp),
135 #[cfg(feature = "t2s-conversion")]
136 "tw2sp.json" => Ok(Self::Tw2sp),
137 #[cfg(feature = "t2s-conversion")]
138 "t2tw.json" => Ok(Self::T2tw),
139 #[cfg(feature = "s2t-conversion")]
140 "tw2t.json" => Ok(Self::Tw2t),
141 #[cfg(feature = "s2t-conversion")]
142 "t2hk.json" => Ok(Self::T2hk),
143 #[cfg(feature = "t2s-conversion")]
144 "hk2t.json" => Ok(Self::Hk2t),
145 #[cfg(feature = "japanese-conversion")]
146 "jp2t.json" => Ok(Self::Jp2t),
147 #[cfg(feature = "japanese-conversion")]
148 "t2jp.json" => Ok(Self::T2jp),
149 _ => Err(OpenCCError::ConfigNotFound(filename.to_string())),
150 }
151 }
152}
153
154#[derive(Serialize, Deserialize, Debug)]
158pub struct ConversionNodeConfig {
159 pub dict: DictConfig,
161}
162
163#[derive(Serialize, Deserialize, Debug)]
166pub struct DictConfig {
167 #[serde(rename = "type")]
169 pub dict_type: String,
170 pub file: Option<String>,
172 pub dicts: Option<Vec<Self>>,
174}
175
176impl Config {
177 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
179 let path = path.as_ref();
180 let file = File::open(path)
181 .map_err(|e| OpenCCError::FileNotFound(format!("{}: {}", path.display(), e)))?;
182 let reader = BufReader::new(file);
183 let mut config: Self = serde_json::from_reader(reader)?;
184
185 config.directory = path.parent().unwrap_or_else(|| Path::new("")).to_path_buf();
186
187 Ok(config)
188 }
189
190 #[must_use]
192 pub fn get_config_directory(&self) -> &Path {
193 &self.directory
194 }
195}
196
197impl FromStr for Config {
198 type Err = OpenCCError;
199
200 fn from_str(s: &str) -> Result<Self> {
201 let config: Self = serde_json::from_str(s)?;
202 Ok(config)
203 }
204}