2020-10-24 22:17:16 -04:00
|
|
|
use std::collections::HashMap;
|
2019-08-07 11:05:06 -04:00
|
|
|
|
2021-04-07 21:49:52 -04:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2022-01-24 12:52:49 -05:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
#[serde(default)]
|
2019-08-07 10:40:06 -04:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct Config {
|
2019-08-07 12:05:19 -04:00
|
|
|
pub files: Walk,
|
2021-03-01 21:40:21 -05:00
|
|
|
pub default: EngineConfig,
|
2021-04-05 22:03:41 -04:00
|
|
|
#[serde(rename = "type")]
|
2021-08-04 07:55:46 -04:00
|
|
|
pub type_: TypeEngineConfig,
|
2021-04-05 08:34:05 -04:00
|
|
|
#[serde(skip)]
|
2021-04-05 22:03:41 -04:00
|
|
|
pub overrides: EngineConfig,
|
2019-08-07 10:40:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
2021-03-29 14:39:48 -04:00
|
|
|
pub fn from_dir(cwd: &std::path::Path) -> Result<Option<Self>, anyhow::Error> {
|
|
|
|
let config = if let Some(path) =
|
|
|
|
find_project_file(cwd, &["typos.toml", "_typos.toml", ".typos.toml"])
|
|
|
|
{
|
2021-05-14 15:02:58 -04:00
|
|
|
log::debug!("Loading {}", path.display());
|
2021-03-29 14:39:48 -04:00
|
|
|
Some(Self::from_file(&path)?)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
Ok(config)
|
|
|
|
}
|
|
|
|
|
2019-10-29 13:36:50 -04:00
|
|
|
pub fn from_file(path: &std::path::Path) -> Result<Self, anyhow::Error> {
|
2021-03-01 13:19:56 -05:00
|
|
|
let s = std::fs::read_to_string(path)?;
|
2020-11-03 20:55:21 -05:00
|
|
|
Self::from_toml(&s)
|
2019-08-07 11:05:06 -04:00
|
|
|
}
|
|
|
|
|
2019-10-29 13:36:50 -04:00
|
|
|
pub fn from_toml(data: &str) -> Result<Self, anyhow::Error> {
|
2023-02-01 10:31:38 -05:00
|
|
|
let content = toml::from_str(data)?;
|
2019-08-07 11:05:06 -04:00
|
|
|
Ok(content)
|
|
|
|
}
|
|
|
|
|
2021-01-04 17:34:18 -05:00
|
|
|
pub fn from_defaults() -> Self {
|
|
|
|
Self {
|
|
|
|
files: Walk::from_defaults(),
|
2021-03-01 21:40:21 -05:00
|
|
|
default: EngineConfig::from_defaults(),
|
2021-08-04 07:55:46 -04:00
|
|
|
type_: TypeEngineConfig::from_defaults(),
|
2021-04-05 22:03:41 -04:00
|
|
|
overrides: EngineConfig::default(),
|
2021-01-04 17:34:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 22:23:30 -04:00
|
|
|
pub fn update(&mut self, source: &Config) {
|
|
|
|
self.files.update(&source.files);
|
|
|
|
self.default.update(&source.default);
|
2021-08-04 07:55:46 -04:00
|
|
|
self.type_.update(&source.type_);
|
2021-04-06 21:53:34 -04:00
|
|
|
self.overrides.update(&source.overrides);
|
2020-10-28 21:58:48 -04:00
|
|
|
}
|
2019-08-07 12:05:19 -04:00
|
|
|
}
|
|
|
|
|
2021-04-07 21:49:52 -04:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2022-01-24 12:52:49 -05:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
#[serde(default)]
|
2019-08-07 12:05:19 -04:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct Walk {
|
2021-05-20 21:55:32 -04:00
|
|
|
pub extend_exclude: Vec<String>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Skip hidden files and directories.
|
2019-08-07 12:05:19 -04:00
|
|
|
pub ignore_hidden: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Respect ignore files.
|
2019-08-07 12:05:19 -04:00
|
|
|
pub ignore_files: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Respect .ignore files.
|
2019-08-07 12:05:19 -04:00
|
|
|
pub ignore_dot: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Respect ignore files in vcs directories.
|
2019-08-07 12:05:19 -04:00
|
|
|
pub ignore_vcs: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Respect global ignore files.
|
2019-08-07 12:05:19 -04:00
|
|
|
pub ignore_global: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Respect ignore files in parent directories.
|
2019-08-07 12:05:19 -04:00
|
|
|
pub ignore_parent: Option<bool>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Walk {
|
2021-01-04 17:34:18 -05:00
|
|
|
pub fn from_defaults() -> Self {
|
|
|
|
let empty = Self::default();
|
|
|
|
Self {
|
2021-05-20 21:55:32 -04:00
|
|
|
extend_exclude: empty.extend_exclude.clone(),
|
2021-01-04 17:34:18 -05:00
|
|
|
ignore_hidden: Some(empty.ignore_hidden()),
|
|
|
|
ignore_files: Some(true),
|
|
|
|
ignore_dot: Some(empty.ignore_dot()),
|
|
|
|
ignore_vcs: Some(empty.ignore_vcs()),
|
|
|
|
ignore_global: Some(empty.ignore_global()),
|
|
|
|
ignore_parent: Some(empty.ignore_parent()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 22:23:30 -04:00
|
|
|
pub fn update(&mut self, source: &Walk) {
|
2021-05-20 21:55:32 -04:00
|
|
|
self.extend_exclude
|
|
|
|
.extend(source.extend_exclude.iter().cloned());
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.ignore_hidden {
|
2019-08-07 10:40:06 -04:00
|
|
|
self.ignore_hidden = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.ignore_files {
|
2019-08-07 10:40:06 -04:00
|
|
|
self.ignore_files = Some(source);
|
|
|
|
self.ignore_dot = None;
|
|
|
|
self.ignore_vcs = None;
|
|
|
|
self.ignore_global = None;
|
|
|
|
self.ignore_parent = None;
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.ignore_dot {
|
2019-08-07 10:40:06 -04:00
|
|
|
self.ignore_dot = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.ignore_vcs {
|
2019-08-07 10:40:06 -04:00
|
|
|
self.ignore_vcs = Some(source);
|
|
|
|
self.ignore_global = None;
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.ignore_global {
|
2019-08-07 10:40:06 -04:00
|
|
|
self.ignore_global = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.ignore_parent {
|
2019-08-07 10:40:06 -04:00
|
|
|
self.ignore_parent = Some(source);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-20 21:55:32 -04:00
|
|
|
pub fn extend_exclude(&self) -> &[String] {
|
|
|
|
&self.extend_exclude
|
|
|
|
}
|
|
|
|
|
2019-08-07 10:40:06 -04:00
|
|
|
pub fn ignore_hidden(&self) -> bool {
|
|
|
|
self.ignore_hidden.unwrap_or(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ignore_dot(&self) -> bool {
|
2020-10-24 22:17:16 -04:00
|
|
|
self.ignore_dot.or(self.ignore_files).unwrap_or(true)
|
2019-08-07 10:40:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ignore_vcs(&self) -> bool {
|
2020-10-24 22:17:16 -04:00
|
|
|
self.ignore_vcs.or(self.ignore_files).unwrap_or(true)
|
2019-08-07 10:40:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ignore_global(&self) -> bool {
|
|
|
|
self.ignore_global
|
2020-10-24 22:17:16 -04:00
|
|
|
.or(self.ignore_vcs)
|
|
|
|
.or(self.ignore_files)
|
2019-08-07 10:40:06 -04:00
|
|
|
.unwrap_or(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ignore_parent(&self) -> bool {
|
2020-10-24 22:17:16 -04:00
|
|
|
self.ignore_parent.or(self.ignore_files).unwrap_or(true)
|
2019-08-07 10:40:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-07 21:49:52 -04:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2022-01-24 12:52:49 -05:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
#[serde(default)]
|
2021-08-04 07:55:46 -04:00
|
|
|
#[serde(transparent)]
|
2021-04-06 22:14:35 -04:00
|
|
|
pub struct TypeEngineConfig {
|
2021-08-04 07:55:46 -04:00
|
|
|
pub patterns: std::collections::HashMap<kstring::KString, GlobEngineConfig>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TypeEngineConfig {
|
|
|
|
pub fn from_defaults() -> Self {
|
|
|
|
let empty = Self::default();
|
|
|
|
Self {
|
|
|
|
patterns: empty.patterns().collect(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update(&mut self, source: &Self) {
|
|
|
|
for (type_name, engine) in source.patterns.iter() {
|
|
|
|
self.patterns
|
|
|
|
.entry(type_name.to_owned())
|
|
|
|
.or_insert_with(GlobEngineConfig::default)
|
|
|
|
.update(engine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn patterns(&self) -> impl Iterator<Item = (kstring::KString, GlobEngineConfig)> {
|
2021-08-04 08:00:01 -04:00
|
|
|
let mut patterns = self.patterns.clone();
|
2021-10-23 09:42:21 -04:00
|
|
|
patterns
|
|
|
|
.entry("lock".into())
|
|
|
|
.or_insert_with(|| GlobEngineConfig {
|
|
|
|
extend_glob: Vec::new(),
|
|
|
|
engine: EngineConfig {
|
|
|
|
check_file: Some(false),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
});
|
2022-08-23 09:25:12 -04:00
|
|
|
patterns
|
|
|
|
.entry("vim".into())
|
|
|
|
.or_insert_with(|| GlobEngineConfig {
|
|
|
|
extend_glob: Vec::new(),
|
|
|
|
engine: EngineConfig {
|
|
|
|
dict: Some(DictConfig {
|
|
|
|
extend_identifiers: maplit::hashmap! {
|
|
|
|
"windo".into() => "windo".into(),
|
|
|
|
},
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
});
|
|
|
|
patterns
|
|
|
|
.entry("vimscript".into())
|
|
|
|
.or_insert_with(|| GlobEngineConfig {
|
|
|
|
extend_glob: Vec::new(),
|
|
|
|
engine: EngineConfig {
|
|
|
|
dict: Some(DictConfig {
|
|
|
|
extend_identifiers: maplit::hashmap! {
|
|
|
|
"windo".into() => "windo".into(),
|
|
|
|
},
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
});
|
2021-10-23 09:42:30 -04:00
|
|
|
patterns
|
|
|
|
.entry("rust".into())
|
|
|
|
.or_insert_with(|| GlobEngineConfig {
|
2022-03-09 09:37:22 -05:00
|
|
|
extend_glob: Vec::new(),
|
2021-10-23 09:42:30 -04:00
|
|
|
engine: EngineConfig {
|
2021-10-23 09:58:13 -04:00
|
|
|
dict: Some(DictConfig {
|
2022-08-23 09:32:27 -04:00
|
|
|
extend_identifiers: maplit::hashmap! {
|
|
|
|
"flate2".into() => "flate2".into(),
|
|
|
|
},
|
2021-10-23 09:58:13 -04:00
|
|
|
extend_words: maplit::hashmap! {
|
|
|
|
"ser".into() => "ser".into(),
|
|
|
|
},
|
|
|
|
..Default::default()
|
|
|
|
}),
|
2021-10-23 09:42:30 -04:00
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
});
|
2022-09-08 12:17:45 -04:00
|
|
|
patterns
|
|
|
|
.entry("py".into())
|
|
|
|
.or_insert_with(|| GlobEngineConfig {
|
|
|
|
extend_glob: Vec::new(),
|
|
|
|
engine: EngineConfig {
|
|
|
|
dict: Some(DictConfig {
|
|
|
|
extend_identifiers: maplit::hashmap! {
|
|
|
|
"NDArray".into() => "NDArray".into(),
|
|
|
|
},
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
});
|
2021-10-23 09:42:30 -04:00
|
|
|
patterns
|
2022-03-09 09:37:22 -05:00
|
|
|
.entry("cert".into())
|
2021-10-23 09:42:30 -04:00
|
|
|
.or_insert_with(|| GlobEngineConfig {
|
2022-03-09 09:37:22 -05:00
|
|
|
extend_glob: Vec::new(),
|
2021-08-04 08:00:01 -04:00
|
|
|
engine: EngineConfig {
|
|
|
|
check_file: Some(false),
|
|
|
|
..Default::default()
|
|
|
|
},
|
2022-03-09 09:37:22 -05:00
|
|
|
});
|
2021-08-04 08:00:01 -04:00
|
|
|
patterns.into_iter()
|
2021-08-04 07:55:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2022-01-24 12:53:56 -05:00
|
|
|
//#[serde(deny_unknown_fields)] // Doesn't work with `flatten`
|
2022-01-24 12:52:49 -05:00
|
|
|
#[serde(default)]
|
2021-08-04 07:55:46 -04:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct GlobEngineConfig {
|
2021-04-06 22:14:35 -04:00
|
|
|
pub extend_glob: Vec<kstring::KString>,
|
|
|
|
#[serde(flatten)]
|
|
|
|
pub engine: EngineConfig,
|
|
|
|
}
|
|
|
|
|
2021-08-04 07:55:46 -04:00
|
|
|
impl GlobEngineConfig {
|
|
|
|
pub fn update(&mut self, source: &GlobEngineConfig) {
|
2021-04-06 22:14:35 -04:00
|
|
|
self.extend_glob.extend(source.extend_glob.iter().cloned());
|
|
|
|
self.engine.update(&source.engine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-07 21:49:52 -04:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2022-01-24 12:53:56 -05:00
|
|
|
//#[serde(deny_unknown_fields)] // Doesn't work with `flatten`
|
2022-01-24 12:52:49 -05:00
|
|
|
#[serde(default)]
|
2019-08-08 09:22:46 -04:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
2021-03-01 21:40:21 -05:00
|
|
|
pub struct EngineConfig {
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Check binary files.
|
2021-01-05 11:35:43 -05:00
|
|
|
pub binary: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Verifying spelling in file names.
|
2019-08-08 09:22:46 -04:00
|
|
|
pub check_filename: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Verifying spelling in files.
|
2019-08-08 09:22:46 -04:00
|
|
|
pub check_file: Option<bool>,
|
2021-03-01 21:37:05 -05:00
|
|
|
#[serde(flatten)]
|
|
|
|
pub tokenizer: Option<TokenizerConfig>,
|
|
|
|
#[serde(flatten)]
|
|
|
|
pub dict: Option<DictConfig>,
|
2019-08-08 09:22:46 -04:00
|
|
|
}
|
|
|
|
|
2021-03-01 21:40:21 -05:00
|
|
|
impl EngineConfig {
|
2021-01-04 17:34:18 -05:00
|
|
|
pub fn from_defaults() -> Self {
|
|
|
|
let empty = Self::default();
|
2021-03-01 21:40:21 -05:00
|
|
|
EngineConfig {
|
2021-01-05 11:35:43 -05:00
|
|
|
binary: Some(empty.binary()),
|
2021-01-04 17:34:18 -05:00
|
|
|
check_filename: Some(empty.check_filename()),
|
|
|
|
check_file: Some(empty.check_file()),
|
2021-03-01 21:37:05 -05:00
|
|
|
tokenizer: Some(
|
|
|
|
empty
|
|
|
|
.tokenizer
|
2021-03-29 21:28:01 -04:00
|
|
|
.unwrap_or_else(TokenizerConfig::from_defaults),
|
2021-03-01 21:37:05 -05:00
|
|
|
),
|
2021-03-29 21:28:01 -04:00
|
|
|
dict: Some(empty.dict.unwrap_or_else(DictConfig::from_defaults)),
|
2021-01-04 17:34:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 22:23:30 -04:00
|
|
|
pub fn update(&mut self, source: &EngineConfig) {
|
|
|
|
if let Some(source) = source.binary {
|
2021-01-05 11:35:43 -05:00
|
|
|
self.binary = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.check_filename {
|
2019-08-08 09:22:46 -04:00
|
|
|
self.check_filename = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.check_file {
|
2019-08-08 09:22:46 -04:00
|
|
|
self.check_file = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.tokenizer.as_ref() {
|
2021-03-01 21:37:05 -05:00
|
|
|
let mut tokenizer = None;
|
|
|
|
std::mem::swap(&mut tokenizer, &mut self.tokenizer);
|
|
|
|
let mut tokenizer = tokenizer.unwrap_or_default();
|
|
|
|
tokenizer.update(source);
|
|
|
|
let mut tokenizer = Some(tokenizer);
|
|
|
|
std::mem::swap(&mut tokenizer, &mut self.tokenizer);
|
2019-08-08 09:22:46 -04:00
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.dict.as_ref() {
|
2021-03-01 21:37:05 -05:00
|
|
|
let mut dict = None;
|
|
|
|
std::mem::swap(&mut dict, &mut self.dict);
|
|
|
|
let mut dict = dict.unwrap_or_default();
|
|
|
|
dict.update(source);
|
|
|
|
let mut dict = Some(dict);
|
|
|
|
std::mem::swap(&mut dict, &mut self.dict);
|
2019-08-08 09:37:06 -04:00
|
|
|
}
|
2019-08-08 09:22:46 -04:00
|
|
|
}
|
|
|
|
|
2021-01-05 11:35:43 -05:00
|
|
|
pub fn binary(&self) -> bool {
|
|
|
|
self.binary.unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
2019-08-08 09:22:46 -04:00
|
|
|
pub fn check_filename(&self) -> bool {
|
|
|
|
self.check_filename.unwrap_or(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_file(&self) -> bool {
|
|
|
|
self.check_file.unwrap_or(true)
|
|
|
|
}
|
2021-03-01 21:37:05 -05:00
|
|
|
}
|
2019-08-08 09:22:46 -04:00
|
|
|
|
2021-04-07 21:49:52 -04:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2022-01-24 12:52:49 -05:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
#[serde(default)]
|
2021-03-01 21:37:05 -05:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct TokenizerConfig {
|
2021-04-29 13:30:56 -04:00
|
|
|
/// Allow unicode characters in identifiers (and not just ASCII)
|
|
|
|
pub unicode: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Do not check identifiers that appear to be hexadecimal values.
|
2021-03-01 21:37:05 -05:00
|
|
|
pub ignore_hex: Option<bool>,
|
2021-03-31 22:23:30 -04:00
|
|
|
/// Allow identifiers to start with digits, in addition to letters.
|
2021-03-01 21:37:05 -05:00
|
|
|
pub identifier_leading_digits: Option<bool>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TokenizerConfig {
|
|
|
|
pub fn from_defaults() -> Self {
|
|
|
|
let empty = Self::default();
|
|
|
|
Self {
|
2021-04-29 13:30:56 -04:00
|
|
|
unicode: Some(empty.unicode()),
|
2021-03-01 21:37:05 -05:00
|
|
|
ignore_hex: Some(empty.ignore_hex()),
|
|
|
|
identifier_leading_digits: Some(empty.identifier_leading_digits()),
|
|
|
|
}
|
2020-05-27 21:46:41 -04:00
|
|
|
}
|
2020-09-02 21:12:49 -04:00
|
|
|
|
2021-03-31 22:23:30 -04:00
|
|
|
pub fn update(&mut self, source: &TokenizerConfig) {
|
2021-04-29 13:30:56 -04:00
|
|
|
if let Some(source) = source.unicode {
|
|
|
|
self.unicode = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.ignore_hex {
|
2021-03-01 21:37:05 -05:00
|
|
|
self.ignore_hex = Some(source);
|
|
|
|
}
|
2021-03-31 22:23:30 -04:00
|
|
|
if let Some(source) = source.identifier_leading_digits {
|
2021-03-01 21:37:05 -05:00
|
|
|
self.identifier_leading_digits = Some(source);
|
|
|
|
}
|
2020-09-02 21:12:49 -04:00
|
|
|
}
|
|
|
|
|
2021-04-29 13:30:56 -04:00
|
|
|
pub fn unicode(&self) -> bool {
|
|
|
|
self.unicode.unwrap_or(true)
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:37:05 -05:00
|
|
|
pub fn ignore_hex(&self) -> bool {
|
|
|
|
self.ignore_hex.unwrap_or(true)
|
2020-09-02 21:12:49 -04:00
|
|
|
}
|
2019-08-08 09:22:46 -04:00
|
|
|
|
2021-03-01 21:37:05 -05:00
|
|
|
pub fn identifier_leading_digits(&self) -> bool {
|
|
|
|
self.identifier_leading_digits.unwrap_or(false)
|
2021-01-05 11:35:43 -05:00
|
|
|
}
|
2021-03-01 21:37:05 -05:00
|
|
|
}
|
2019-08-08 09:22:46 -04:00
|
|
|
|
2021-04-07 21:49:52 -04:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2022-01-24 12:52:49 -05:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
#[serde(default)]
|
2021-03-01 21:37:05 -05:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct DictConfig {
|
|
|
|
pub locale: Option<Locale>,
|
|
|
|
pub extend_identifiers: HashMap<kstring::KString, kstring::KString>,
|
|
|
|
pub extend_words: HashMap<kstring::KString, kstring::KString>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DictConfig {
|
|
|
|
pub fn from_defaults() -> Self {
|
|
|
|
let empty = Self::default();
|
|
|
|
Self {
|
|
|
|
locale: Some(empty.locale()),
|
|
|
|
extend_identifiers: Default::default(),
|
|
|
|
extend_words: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 22:23:30 -04:00
|
|
|
pub fn update(&mut self, source: &DictConfig) {
|
|
|
|
if let Some(source) = source.locale {
|
2021-03-01 21:37:05 -05:00
|
|
|
self.locale = Some(source);
|
|
|
|
}
|
|
|
|
self.extend_identifiers.extend(
|
|
|
|
source
|
2021-03-31 22:23:30 -04:00
|
|
|
.extend_identifiers
|
|
|
|
.iter()
|
|
|
|
.map(|(key, value)| (key.clone(), value.clone())),
|
2021-03-01 21:37:05 -05:00
|
|
|
);
|
|
|
|
self.extend_words.extend(
|
|
|
|
source
|
2021-03-31 22:23:30 -04:00
|
|
|
.extend_words
|
|
|
|
.iter()
|
|
|
|
.map(|(key, value)| (key.clone(), value.clone())),
|
2021-03-01 21:37:05 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn locale(&self) -> Locale {
|
|
|
|
self.locale.unwrap_or_default()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn extend_identifiers(&self) -> Box<dyn Iterator<Item = (&str, &str)> + '_> {
|
|
|
|
Box::new(
|
|
|
|
self.extend_identifiers
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| (k.as_str(), v.as_str())),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn extend_words(&self) -> Box<dyn Iterator<Item = (&str, &str)> + '_> {
|
|
|
|
Box::new(
|
2020-10-24 22:17:16 -04:00
|
|
|
self.extend_words
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| (k.as_str(), v.as_str())),
|
|
|
|
)
|
2020-09-02 21:12:49 -04:00
|
|
|
}
|
2019-08-08 09:22:46 -04:00
|
|
|
}
|
|
|
|
|
2020-10-30 09:33:43 -04:00
|
|
|
fn find_project_file(dir: &std::path::Path, names: &[&str]) -> Option<std::path::PathBuf> {
|
2021-03-29 14:39:48 -04:00
|
|
|
let mut file_path = dir.join("placeholder");
|
|
|
|
for name in names {
|
|
|
|
file_path.set_file_name(name);
|
|
|
|
if file_path.exists() {
|
|
|
|
return Some(file_path);
|
2019-08-07 11:16:57 -04:00
|
|
|
}
|
|
|
|
}
|
2020-10-30 09:33:43 -04:00
|
|
|
None
|
2019-08-07 11:16:57 -04:00
|
|
|
}
|
2020-05-27 21:46:41 -04:00
|
|
|
|
2021-04-07 21:49:52 -04:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
2020-05-27 21:46:41 -04:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub enum Locale {
|
|
|
|
En,
|
|
|
|
EnUs,
|
|
|
|
EnGb,
|
|
|
|
EnCa,
|
|
|
|
EnAu,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Locale {
|
2021-04-30 22:31:20 -04:00
|
|
|
pub const fn category(self) -> Option<varcon_core::Category> {
|
2020-05-27 21:46:41 -04:00
|
|
|
match self {
|
|
|
|
Locale::En => None,
|
2021-04-30 22:31:20 -04:00
|
|
|
Locale::EnUs => Some(varcon_core::Category::American),
|
|
|
|
Locale::EnGb => Some(varcon_core::Category::BritishIse),
|
|
|
|
Locale::EnCa => Some(varcon_core::Category::Canadian),
|
|
|
|
Locale::EnAu => Some(varcon_core::Category::Australian),
|
2020-05-27 21:46:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 13:27:07 -05:00
|
|
|
pub const fn variants() -> [&'static str; 5] {
|
2020-05-27 21:46:41 -04:00
|
|
|
["en", "en-us", "en-gb", "en-ca", "en-au"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Locale {
|
|
|
|
fn default() -> Self {
|
|
|
|
Locale::En
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::str::FromStr for Locale {
|
|
|
|
type Err = String;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
match s {
|
|
|
|
"en" => Ok(Locale::En),
|
|
|
|
"en-us" => Ok(Locale::EnUs),
|
|
|
|
"en-gb" => Ok(Locale::EnGb),
|
|
|
|
"en-ca" => Ok(Locale::EnCa),
|
|
|
|
"en-au" => Ok(Locale::EnAu),
|
|
|
|
_ => Err("valid values: en, en-us, en-gb, en-ca, en-au".to_owned()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for Locale {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
match *self {
|
|
|
|
Locale::En => write!(f, "en"),
|
|
|
|
Locale::EnUs => write!(f, "en-us"),
|
|
|
|
Locale::EnGb => write!(f, "en-gb"),
|
|
|
|
Locale::EnCa => write!(f, "en-ca"),
|
|
|
|
Locale::EnAu => write!(f, "en-au"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-07 21:49:52 -04:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_from_defaults() {
|
|
|
|
let null = Config::default();
|
|
|
|
let defaulted = Config::from_defaults();
|
|
|
|
assert_ne!(defaulted, null);
|
|
|
|
assert_ne!(defaulted.files, null.files);
|
|
|
|
assert_ne!(defaulted.default, null.default);
|
|
|
|
assert_ne!(defaulted.default.tokenizer, null.default.tokenizer);
|
|
|
|
assert_ne!(defaulted.default.dict, null.default.dict);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_from_nothing() {
|
|
|
|
let null = Config::default();
|
|
|
|
let defaulted = Config::from_defaults();
|
|
|
|
|
|
|
|
let mut actual = defaulted.clone();
|
|
|
|
actual.update(&null);
|
|
|
|
|
|
|
|
assert_eq!(actual, defaulted);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_from_defaults() {
|
|
|
|
let null = Config::default();
|
|
|
|
let defaulted = Config::from_defaults();
|
|
|
|
|
2021-04-10 14:07:07 -04:00
|
|
|
let mut actual = null;
|
2021-04-07 21:49:52 -04:00
|
|
|
actual.update(&defaulted);
|
|
|
|
|
|
|
|
assert_eq!(actual, defaulted);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_extend_glob_updates() {
|
2021-08-04 07:55:46 -04:00
|
|
|
let null = GlobEngineConfig::default();
|
|
|
|
let extended = GlobEngineConfig {
|
2021-04-07 21:49:52 -04:00
|
|
|
extend_glob: vec!["*.foo".into()],
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
2021-04-10 14:07:07 -04:00
|
|
|
let mut actual = null;
|
2021-04-07 21:49:52 -04:00
|
|
|
actual.update(&extended);
|
|
|
|
|
|
|
|
assert_eq!(actual, extended);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_extend_glob_extends() {
|
2021-08-04 07:55:46 -04:00
|
|
|
let base = GlobEngineConfig {
|
2021-04-07 21:49:52 -04:00
|
|
|
extend_glob: vec!["*.foo".into()],
|
|
|
|
..Default::default()
|
|
|
|
};
|
2021-08-04 07:55:46 -04:00
|
|
|
let extended = GlobEngineConfig {
|
2021-04-07 21:49:52 -04:00
|
|
|
extend_glob: vec!["*.bar".into()],
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
2021-04-10 14:07:07 -04:00
|
|
|
let mut actual = base;
|
2021-04-07 21:49:52 -04:00
|
|
|
actual.update(&extended);
|
|
|
|
|
|
|
|
let expected: Vec<kstring::KString> = vec!["*.foo".into(), "*.bar".into()];
|
|
|
|
assert_eq!(actual.extend_glob, expected);
|
|
|
|
}
|
2022-01-24 10:45:14 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parse_extend_globs() {
|
|
|
|
let input = r#"[type.po]
|
|
|
|
extend-glob = ["*.po"]
|
2022-01-24 12:53:56 -05:00
|
|
|
check-file = true
|
2022-01-24 10:45:14 -05:00
|
|
|
"#;
|
|
|
|
let mut expected = Config::default();
|
|
|
|
expected.type_.patterns.insert(
|
|
|
|
"po".into(),
|
|
|
|
GlobEngineConfig {
|
|
|
|
extend_glob: vec!["*.po".into()],
|
|
|
|
engine: EngineConfig {
|
|
|
|
tokenizer: Some(TokenizerConfig::default()),
|
|
|
|
dict: Some(DictConfig::default()),
|
2022-01-24 12:53:56 -05:00
|
|
|
check_file: Some(true),
|
2022-01-24 10:45:14 -05:00
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
let actual = Config::from_toml(input).unwrap();
|
|
|
|
assert_eq!(actual, expected);
|
|
|
|
}
|
2022-01-24 12:59:58 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parse_extend_words() {
|
|
|
|
let input = r#"[type.shaders]
|
|
|
|
extend-glob = [
|
|
|
|
'*.shader',
|
|
|
|
'*.cginc',
|
|
|
|
]
|
|
|
|
|
|
|
|
[type.shaders.extend-words]
|
|
|
|
inout = "inout"
|
|
|
|
"#;
|
|
|
|
let mut expected = Config::default();
|
|
|
|
expected.type_.patterns.insert(
|
|
|
|
"shaders".into(),
|
|
|
|
GlobEngineConfig {
|
|
|
|
extend_glob: vec!["*.shader".into(), "*.cginc".into()],
|
|
|
|
engine: EngineConfig {
|
|
|
|
tokenizer: Some(TokenizerConfig::default()),
|
|
|
|
dict: Some(DictConfig {
|
|
|
|
extend_words: maplit::hashmap! {
|
|
|
|
"inout".into() => "inout".into(),
|
|
|
|
},
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
let actual = Config::from_toml(input).unwrap();
|
|
|
|
assert_eq!(actual, expected);
|
|
|
|
}
|
2021-04-07 21:49:52 -04:00
|
|
|
}
|