2020-10-24 22:17:16 -04:00
|
|
|
use std::collections::HashMap;
|
2019-08-07 11:05:06 -04:00
|
|
|
use std::io::Read;
|
|
|
|
|
2019-08-07 10:40:06 -04:00
|
|
|
pub trait ConfigSource {
|
2019-08-08 09:22:46 -04:00
|
|
|
fn walk(&self) -> Option<&dyn WalkSource> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default(&self) -> Option<&dyn FileSource> {
|
|
|
|
None
|
|
|
|
}
|
2019-08-07 12:05:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait WalkSource {
|
2019-08-07 18:14:29 -04:00
|
|
|
/// Search binary files.
|
|
|
|
fn binary(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-08-25 21:54:42 -04:00
|
|
|
/// The root for `ignore_patterns`
|
|
|
|
fn ignore_root(&self) -> Option<&std::path::Path> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Ignore the specified patterns (gitignore syntax)
|
|
|
|
fn ignore_patterns(&self) -> Option<&[String]> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-08-07 10:40:06 -04:00
|
|
|
/// Skip hidden files and directories.
|
|
|
|
fn ignore_hidden(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Respect ignore files.
|
|
|
|
fn ignore_files(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Respect .ignore files.
|
|
|
|
fn ignore_dot(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Respect ignore files in vcs directories.
|
|
|
|
fn ignore_vcs(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Respect global ignore files.
|
|
|
|
fn ignore_global(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Respect ignore files in parent directories.
|
|
|
|
fn ignore_parent(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 09:22:46 -04:00
|
|
|
pub trait FileSource {
|
|
|
|
/// Verifying spelling in file names.
|
|
|
|
fn check_filename(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-11-02 10:57:07 -04:00
|
|
|
/// Verifying spelling in files.
|
2019-08-08 09:22:46 -04:00
|
|
|
fn check_file(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-11-02 11:15:51 -04:00
|
|
|
/// Do not check identifiers that appear to be hexadecimal values.
|
2019-08-08 09:22:46 -04:00
|
|
|
fn ignore_hex(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
2019-08-08 09:37:06 -04:00
|
|
|
|
2019-11-02 11:15:51 -04:00
|
|
|
/// Allow identifiers to start with digits, in addition to letters.
|
|
|
|
fn identifier_leading_digits(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Allow identifiers to start with one of these characters.
|
|
|
|
fn identifier_leading_chars(&self) -> Option<&str> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Allow identifiers to include digits, in addition to letters.
|
2019-08-08 09:37:06 -04:00
|
|
|
fn identifier_include_digits(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-11-02 11:15:51 -04:00
|
|
|
/// Allow identifiers to include these characters.
|
2019-08-08 09:37:06 -04:00
|
|
|
fn identifier_include_chars(&self) -> Option<&str> {
|
|
|
|
None
|
|
|
|
}
|
2020-05-27 21:46:41 -04:00
|
|
|
|
|
|
|
fn locale(&self) -> Option<Locale> {
|
|
|
|
None
|
|
|
|
}
|
2020-09-02 21:12:49 -04:00
|
|
|
|
2020-10-24 22:17:16 -04:00
|
|
|
fn extend_identifiers(&self) -> Box<dyn Iterator<Item = (&str, &str)> + '_> {
|
|
|
|
Box::new(None.into_iter())
|
2020-09-02 21:12:49 -04:00
|
|
|
}
|
|
|
|
|
2020-10-24 22:17:16 -04:00
|
|
|
fn extend_words(&self) -> Box<dyn Iterator<Item = (&str, &str)> + '_> {
|
|
|
|
Box::new(None.into_iter())
|
2020-09-02 21:12:49 -04:00
|
|
|
}
|
2019-08-08 09:22:46 -04:00
|
|
|
}
|
|
|
|
|
2019-08-07 10:40:06 -04:00
|
|
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
|
|
|
#[serde(deny_unknown_fields, default)]
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct Config {
|
2019-08-07 12:05:19 -04:00
|
|
|
pub files: Walk,
|
2019-08-08 09:22:46 -04:00
|
|
|
pub default: FileConfig,
|
2019-08-07 10:40:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
2019-10-29 13:36:50 -04:00
|
|
|
pub fn from_file(path: &std::path::Path) -> Result<Self, anyhow::Error> {
|
2019-08-07 11:05:06 -04:00
|
|
|
let mut file = std::fs::File::open(path)?;
|
|
|
|
let mut s = String::new();
|
|
|
|
file.read_to_string(&mut s)?;
|
2020-08-25 21:54:42 -04:00
|
|
|
let mut c = Self::from_toml(&s)?;
|
|
|
|
c.files.ignore_root = path.parent().map(|p| p.to_owned());
|
|
|
|
Ok(c)
|
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> {
|
2019-08-07 11:05:06 -04:00
|
|
|
let content = toml::from_str(data)?;
|
|
|
|
Ok(content)
|
|
|
|
}
|
|
|
|
|
2019-10-29 13:36:50 -04:00
|
|
|
pub fn derive(cwd: &std::path::Path) -> Result<Self, anyhow::Error> {
|
2020-10-30 09:33:43 -04:00
|
|
|
if let Some(path) = find_project_file(cwd, &["typos.toml", "_typos.toml", ".typos.toml"]) {
|
2019-08-07 11:16:57 -04:00
|
|
|
Self::from_file(&path)
|
|
|
|
} else {
|
|
|
|
Ok(Default::default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-07 10:40:06 -04:00
|
|
|
pub fn update(&mut self, source: &dyn ConfigSource) {
|
2019-08-07 12:05:19 -04:00
|
|
|
if let Some(walk) = source.walk() {
|
|
|
|
self.files.update(walk);
|
|
|
|
}
|
2019-08-08 09:22:46 -04:00
|
|
|
if let Some(default) = source.default() {
|
|
|
|
self.default.update(default);
|
|
|
|
}
|
2019-08-07 12:05:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigSource for Config {
|
|
|
|
fn walk(&self) -> Option<&dyn WalkSource> {
|
|
|
|
Some(&self.files)
|
|
|
|
}
|
2020-10-28 21:58:48 -04:00
|
|
|
|
|
|
|
fn default(&self) -> Option<&dyn FileSource> {
|
|
|
|
Some(&self.default)
|
|
|
|
}
|
2019-08-07 12:05:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
|
|
|
#[serde(deny_unknown_fields, default)]
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct Walk {
|
2019-08-07 18:14:29 -04:00
|
|
|
pub binary: Option<bool>,
|
2020-08-25 21:54:42 -04:00
|
|
|
#[serde(skip)]
|
|
|
|
pub ignore_root: Option<std::path::PathBuf>,
|
|
|
|
pub ignore_patterns: Option<Vec<String>>,
|
2019-08-07 12:05:19 -04:00
|
|
|
pub ignore_hidden: Option<bool>,
|
|
|
|
pub ignore_files: Option<bool>,
|
|
|
|
pub ignore_dot: Option<bool>,
|
|
|
|
pub ignore_vcs: Option<bool>,
|
|
|
|
pub ignore_global: Option<bool>,
|
|
|
|
pub ignore_parent: Option<bool>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Walk {
|
|
|
|
pub fn update(&mut self, source: &dyn WalkSource) {
|
2019-08-07 18:14:29 -04:00
|
|
|
if let Some(source) = source.binary() {
|
|
|
|
self.binary = Some(source);
|
|
|
|
}
|
2020-08-25 21:54:42 -04:00
|
|
|
if let (Some(root), Some(source)) = (source.ignore_root(), source.ignore_patterns()) {
|
|
|
|
self.ignore_root = Some(root.to_owned());
|
|
|
|
self.ignore_patterns = Some(source.to_owned());
|
|
|
|
}
|
2019-08-07 10:40:06 -04:00
|
|
|
if let Some(source) = source.ignore_hidden() {
|
|
|
|
self.ignore_hidden = Some(source);
|
|
|
|
}
|
|
|
|
if let Some(source) = source.ignore_files() {
|
|
|
|
self.ignore_files = Some(source);
|
|
|
|
self.ignore_dot = None;
|
|
|
|
self.ignore_vcs = None;
|
|
|
|
self.ignore_global = None;
|
|
|
|
self.ignore_parent = None;
|
|
|
|
}
|
|
|
|
if let Some(source) = source.ignore_dot() {
|
|
|
|
self.ignore_dot = Some(source);
|
|
|
|
}
|
|
|
|
if let Some(source) = source.ignore_vcs() {
|
|
|
|
self.ignore_vcs = Some(source);
|
|
|
|
self.ignore_global = None;
|
|
|
|
}
|
|
|
|
if let Some(source) = source.ignore_global() {
|
|
|
|
self.ignore_global = Some(source);
|
|
|
|
}
|
|
|
|
if let Some(source) = source.ignore_parent() {
|
|
|
|
self.ignore_parent = Some(source);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-07 18:14:29 -04:00
|
|
|
pub fn binary(&self) -> bool {
|
|
|
|
self.binary.unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
2020-08-25 21:54:42 -04:00
|
|
|
pub fn ignore_root(&self) -> Option<&std::path::Path> {
|
|
|
|
self.ignore_root.as_deref()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ignore_patterns(&self) -> Option<&[String]> {
|
|
|
|
self.ignore_patterns.as_deref()
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-07 12:05:19 -04:00
|
|
|
impl WalkSource for Walk {
|
2019-08-07 18:14:29 -04:00
|
|
|
fn binary(&self) -> Option<bool> {
|
|
|
|
self.binary
|
|
|
|
}
|
|
|
|
|
2020-08-25 21:54:42 -04:00
|
|
|
fn ignore_root(&self) -> Option<&std::path::Path> {
|
|
|
|
self.ignore_root.as_deref()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_patterns(&self) -> Option<&[String]> {
|
|
|
|
self.ignore_patterns.as_deref()
|
|
|
|
}
|
|
|
|
|
2019-08-07 10:40:06 -04:00
|
|
|
fn ignore_hidden(&self) -> Option<bool> {
|
|
|
|
self.ignore_hidden
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_files(&self) -> Option<bool> {
|
|
|
|
self.ignore_files
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_dot(&self) -> Option<bool> {
|
|
|
|
self.ignore_dot
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_vcs(&self) -> Option<bool> {
|
|
|
|
self.ignore_vcs
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_global(&self) -> Option<bool> {
|
|
|
|
self.ignore_global
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_parent(&self) -> Option<bool> {
|
|
|
|
self.ignore_parent
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 11:16:57 -04:00
|
|
|
|
2019-08-08 09:22:46 -04:00
|
|
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
|
|
|
#[serde(deny_unknown_fields, default)]
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct FileConfig {
|
|
|
|
pub check_filename: Option<bool>,
|
|
|
|
pub check_file: Option<bool>,
|
|
|
|
pub ignore_hex: Option<bool>,
|
2019-11-02 11:15:51 -04:00
|
|
|
pub identifier_leading_digits: Option<bool>,
|
|
|
|
pub identifier_leading_chars: Option<String>,
|
2019-08-08 09:37:06 -04:00
|
|
|
pub identifier_include_digits: Option<bool>,
|
|
|
|
pub identifier_include_chars: Option<String>,
|
2020-05-27 21:46:41 -04:00
|
|
|
pub locale: Option<Locale>,
|
2020-10-24 22:17:16 -04:00
|
|
|
pub extend_identifiers: HashMap<String, String>,
|
|
|
|
pub extend_words: HashMap<String, String>,
|
2019-08-08 09:22:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FileConfig {
|
|
|
|
pub fn update(&mut self, source: &dyn FileSource) {
|
|
|
|
if let Some(source) = source.check_filename() {
|
|
|
|
self.check_filename = Some(source);
|
|
|
|
}
|
|
|
|
if let Some(source) = source.check_file() {
|
|
|
|
self.check_file = Some(source);
|
|
|
|
}
|
|
|
|
if let Some(source) = source.ignore_hex() {
|
|
|
|
self.ignore_hex = Some(source);
|
|
|
|
}
|
2019-11-02 11:15:51 -04:00
|
|
|
if let Some(source) = source.identifier_leading_digits() {
|
|
|
|
self.identifier_leading_digits = Some(source);
|
|
|
|
}
|
|
|
|
if let Some(source) = source.identifier_leading_chars() {
|
|
|
|
self.identifier_leading_chars = Some(source.to_owned());
|
|
|
|
}
|
2019-08-08 09:37:06 -04:00
|
|
|
if let Some(source) = source.identifier_include_digits() {
|
|
|
|
self.identifier_include_digits = Some(source);
|
|
|
|
}
|
|
|
|
if let Some(source) = source.identifier_include_chars() {
|
|
|
|
self.identifier_include_chars = Some(source.to_owned());
|
|
|
|
}
|
2020-05-27 21:46:41 -04:00
|
|
|
if let Some(source) = source.locale() {
|
|
|
|
self.locale = Some(source);
|
|
|
|
}
|
2020-10-24 22:17:16 -04:00
|
|
|
self.extend_identifiers.extend(
|
|
|
|
source
|
|
|
|
.extend_identifiers()
|
|
|
|
.map(|(k, v)| (k.to_owned(), v.to_owned())),
|
|
|
|
);
|
|
|
|
self.extend_words.extend(
|
|
|
|
source
|
|
|
|
.extend_words()
|
|
|
|
.map(|(k, v)| (k.to_owned(), v.to_owned())),
|
|
|
|
);
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ignore_hex(&self) -> bool {
|
|
|
|
self.ignore_hex.unwrap_or(true)
|
|
|
|
}
|
2019-08-08 09:37:06 -04:00
|
|
|
|
2019-11-02 11:15:51 -04:00
|
|
|
pub fn identifier_leading_digits(&self) -> bool {
|
|
|
|
self.identifier_leading_digits.unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn identifier_leading_chars(&self) -> &str {
|
2020-07-04 21:41:32 -04:00
|
|
|
self.identifier_leading_chars.as_deref().unwrap_or("_")
|
2019-11-02 11:15:51 -04:00
|
|
|
}
|
|
|
|
|
2019-08-08 09:37:06 -04:00
|
|
|
pub fn identifier_include_digits(&self) -> bool {
|
|
|
|
self.identifier_include_digits.unwrap_or(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn identifier_include_chars(&self) -> &str {
|
2020-07-04 21:41:32 -04:00
|
|
|
self.identifier_include_chars.as_deref().unwrap_or("_'")
|
2019-08-08 09:37:06 -04:00
|
|
|
}
|
2020-05-27 21:46:41 -04:00
|
|
|
|
|
|
|
pub fn locale(&self) -> Locale {
|
|
|
|
self.locale.unwrap_or_default()
|
|
|
|
}
|
2020-09-02 21:12:49 -04:00
|
|
|
|
2020-10-24 22:17:16 -04:00
|
|
|
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())),
|
|
|
|
)
|
2020-09-02 21:12:49 -04:00
|
|
|
}
|
|
|
|
|
2020-10-24 22:17:16 -04:00
|
|
|
pub fn extend_words(&self) -> Box<dyn Iterator<Item = (&str, &str)> + '_> {
|
|
|
|
Box::new(
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
impl FileSource for FileConfig {
|
|
|
|
fn check_filename(&self) -> Option<bool> {
|
|
|
|
self.check_filename
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_file(&self) -> Option<bool> {
|
|
|
|
self.check_file
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_hex(&self) -> Option<bool> {
|
|
|
|
self.ignore_hex
|
|
|
|
}
|
2019-08-08 09:37:06 -04:00
|
|
|
|
2019-11-02 11:15:51 -04:00
|
|
|
fn identifier_leading_digits(&self) -> Option<bool> {
|
|
|
|
self.identifier_leading_digits
|
|
|
|
}
|
|
|
|
|
|
|
|
fn identifier_leading_chars(&self) -> Option<&str> {
|
2020-07-04 21:41:32 -04:00
|
|
|
self.identifier_leading_chars.as_deref()
|
2019-11-02 11:15:51 -04:00
|
|
|
}
|
|
|
|
|
2019-08-08 09:37:06 -04:00
|
|
|
fn identifier_include_digits(&self) -> Option<bool> {
|
|
|
|
self.identifier_include_digits
|
|
|
|
}
|
|
|
|
|
|
|
|
fn identifier_include_chars(&self) -> Option<&str> {
|
2020-07-04 21:41:32 -04:00
|
|
|
self.identifier_include_chars.as_deref()
|
2019-08-08 09:37:06 -04:00
|
|
|
}
|
2020-05-27 21:46:41 -04:00
|
|
|
|
|
|
|
fn locale(&self) -> Option<Locale> {
|
|
|
|
self.locale
|
|
|
|
}
|
2020-09-02 21:12:49 -04:00
|
|
|
|
2020-10-24 22:17:16 -04:00
|
|
|
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())),
|
|
|
|
)
|
2020-09-02 21:12:49 -04:00
|
|
|
}
|
|
|
|
|
2020-10-24 22:17:16 -04:00
|
|
|
fn extend_words(&self) -> Box<dyn Iterator<Item = (&str, &str)> + '_> {
|
|
|
|
Box::new(
|
|
|
|
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> {
|
|
|
|
for ancestor in dir.ancestors() {
|
|
|
|
let mut file_path = ancestor.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
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub enum Locale {
|
|
|
|
En,
|
|
|
|
EnUs,
|
|
|
|
EnGb,
|
|
|
|
EnCa,
|
|
|
|
EnAu,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Locale {
|
|
|
|
pub fn category(self) -> Option<typos_vars::Category> {
|
|
|
|
match self {
|
|
|
|
Locale::En => None,
|
|
|
|
Locale::EnUs => Some(typos_vars::Category::American),
|
|
|
|
Locale::EnGb => Some(typos_vars::Category::BritishIse),
|
|
|
|
Locale::EnCa => Some(typos_vars::Category::Canadian),
|
|
|
|
Locale::EnAu => Some(typos_vars::Category::Australian),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn variants() -> [&'static str; 5] {
|
|
|
|
["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"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|