use std::{
fs::File,
io::BufReader,
path::{
Path,
PathBuf,
},
str::FromStr,
};
use serde::{
Deserialize,
Serialize,
};
use crate::error::{
OpenCCError,
Result,
};
#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
pub name: String,
pub conversion_chain: Vec<ConversionNodeConfig>,
#[serde(skip)]
directory: PathBuf,
}
#[repr(i32)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuiltinConfig {
#[cfg(feature = "s2t-conversion")]
S2t = 0,
#[cfg(feature = "t2s-conversion")]
T2s = 1,
#[cfg(feature = "s2t-conversion")]
S2tw = 2,
#[cfg(feature = "t2s-conversion")]
Tw2s = 3,
#[cfg(feature = "s2t-conversion")]
S2hk = 4,
#[cfg(feature = "t2s-conversion")]
Hk2s = 5,
#[cfg(feature = "s2t-conversion")]
S2twp = 6,
#[cfg(feature = "t2s-conversion")]
Tw2sp = 7,
#[cfg(feature = "t2s-conversion")]
T2tw = 8,
#[cfg(feature = "s2t-conversion")]
Tw2t = 9,
#[cfg(feature = "s2t-conversion")]
T2hk = 10,
#[cfg(feature = "t2s-conversion")]
Hk2t = 11,
#[cfg(feature = "japanese-conversion")]
Jp2t = 12,
#[cfg(feature = "japanese-conversion")]
T2jp = 13,
}
impl BuiltinConfig {
#[must_use]
pub const fn to_filename(&self) -> &'static str {
match self {
#[cfg(feature = "s2t-conversion")]
Self::S2t => "s2t.json",
#[cfg(feature = "t2s-conversion")]
Self::T2s => "t2s.json",
#[cfg(feature = "s2t-conversion")]
Self::S2tw => "s2tw.json",
#[cfg(feature = "t2s-conversion")]
Self::Tw2s => "tw2s.json",
#[cfg(feature = "s2t-conversion")]
Self::S2hk => "s2hk.json",
#[cfg(feature = "t2s-conversion")]
Self::Hk2s => "hk2s.json",
#[cfg(feature = "s2t-conversion")]
Self::S2twp => "s2twp.json",
#[cfg(feature = "t2s-conversion")]
Self::Tw2sp => "tw2sp.json",
#[cfg(feature = "t2s-conversion")]
Self::T2tw => "t2tw.json",
#[cfg(feature = "s2t-conversion")]
Self::Tw2t => "tw2t.json",
#[cfg(feature = "s2t-conversion")]
Self::T2hk => "t2hk.json",
#[cfg(feature = "t2s-conversion")]
Self::Hk2t => "hk2t.json",
#[cfg(feature = "japanese-conversion")]
Self::Jp2t => "jp2t.json",
#[cfg(feature = "japanese-conversion")]
Self::T2jp => "t2jp.json",
}
}
pub fn from_filename(filename: &str) -> Result<Self> {
match filename {
#[cfg(feature = "s2t-conversion")]
"s2t.json" => Ok(Self::S2t),
#[cfg(feature = "t2s-conversion")]
"t2s.json" => Ok(Self::T2s),
#[cfg(feature = "s2t-conversion")]
"s2tw.json" => Ok(Self::S2tw),
#[cfg(feature = "t2s-conversion")]
"tw2s.json" => Ok(Self::Tw2s),
#[cfg(feature = "s2t-conversion")]
"s2hk.json" => Ok(Self::S2hk),
#[cfg(feature = "t2s-conversion")]
"hk2s.json" => Ok(Self::Hk2s),
#[cfg(feature = "s2t-conversion")]
"s2twp.json" => Ok(Self::S2twp),
#[cfg(feature = "t2s-conversion")]
"tw2sp.json" => Ok(Self::Tw2sp),
#[cfg(feature = "t2s-conversion")]
"t2tw.json" => Ok(Self::T2tw),
#[cfg(feature = "s2t-conversion")]
"tw2t.json" => Ok(Self::Tw2t),
#[cfg(feature = "s2t-conversion")]
"t2hk.json" => Ok(Self::T2hk),
#[cfg(feature = "t2s-conversion")]
"hk2t.json" => Ok(Self::Hk2t),
#[cfg(feature = "japanese-conversion")]
"jp2t.json" => Ok(Self::Jp2t),
#[cfg(feature = "japanese-conversion")]
"t2jp.json" => Ok(Self::T2jp),
_ => Err(OpenCCError::ConfigNotFound(filename.to_string())),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ConversionNodeConfig {
pub dict: DictConfig,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DictConfig {
#[serde(rename = "type")]
pub dict_type: String,
pub file: Option<String>,
pub dicts: Option<Vec<Self>>,
}
impl Config {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let file = File::open(path)
.map_err(|e| OpenCCError::FileNotFound(format!("{}: {}", path.display(), e)))?;
let reader = BufReader::new(file);
let mut config: Self = serde_json::from_reader(reader)?;
config.directory = path.parent().unwrap_or_else(|| Path::new("")).to_path_buf();
Ok(config)
}
#[must_use]
pub fn get_config_directory(&self) -> &Path {
&self.directory
}
}
impl FromStr for Config {
type Err = OpenCCError;
fn from_str(s: &str) -> Result<Self> {
let config: Self = serde_json::from_str(s)?;
Ok(config)
}
}