Skip to content

Commit 7e8372a

Browse files
authored
refactor: allow other Rust applications to use Dialect and its string serdes (#17)
1 parent 1d3f874 commit 7e8372a

File tree

3 files changed

+81
-58
lines changed

3 files changed

+81
-58
lines changed

src/commands.rs

+2-28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::validate::Dialect;
2+
13
pub struct ParameterInfo {
24
pub allow_plural: bool,
35
pub allow_gender: bool,
@@ -10,34 +12,6 @@ pub enum Occurence {
1012
EXACT, //< Command must match exactly with base.
1113
}
1214

13-
#[derive(PartialEq, Copy, Clone)]
14-
pub enum Dialect {
15-
NEWGRF,
16-
GAMESCRIPT,
17-
OPENTTD,
18-
}
19-
20-
impl From<&str> for Dialect {
21-
fn from(src: &str) -> Self {
22-
match src {
23-
"newgrf" => Dialect::NEWGRF,
24-
"game-script" => Dialect::GAMESCRIPT,
25-
"openttd" => Dialect::OPENTTD,
26-
_ => panic!(),
27-
}
28-
}
29-
}
30-
31-
impl Into<String> for Dialect {
32-
fn into(self) -> String {
33-
match self {
34-
Dialect::NEWGRF => String::from("newgrf"),
35-
Dialect::GAMESCRIPT => String::from("game-script"),
36-
Dialect::OPENTTD => String::from("openttd"),
37-
}
38-
}
39-
}
40-
4115
pub struct CommandInfo<'a> {
4216
pub name: &'a str,
4317
pub norm_name: Option<&'a str>,

src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct Args {
2424
fn main() {
2525
let args = Args::parse();
2626
let config = validate::LanguageConfig {
27-
dialect: args.dialect,
27+
dialect: validate::Dialect::try_from(args.dialect.as_str()).unwrap(),
2828
cases: args.cases,
2929
genders: args.genders,
3030
plural_count: args.plural_count,

src/validate.rs

+78-29
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
use crate::commands::{CommandInfo, Dialect, Occurence, COMMANDS};
1+
use crate::commands::{CommandInfo, Occurence, COMMANDS};
22
use crate::parser::{FragmentContent, ParsedString};
33
use serde::{Deserialize, Serialize};
44
use std::collections::{BTreeMap, HashMap};
55

6+
#[derive(Debug, PartialEq, Copy, Clone)]
7+
pub enum Dialect {
8+
NEWGRF,
9+
GAMESCRIPT,
10+
OPENTTD,
11+
}
12+
613
#[derive(Deserialize, Debug)]
714
pub struct LanguageConfig {
8-
pub dialect: String, //< "newgrf", "game-script", "openttd"
15+
pub dialect: Dialect,
916
pub cases: Vec<String>,
1017
pub genders: Vec<String>,
1118
pub plural_count: usize,
@@ -32,17 +39,59 @@ pub struct ValidationResult {
3239
pub normalized: Option<String>,
3340
}
3441

35-
impl LanguageConfig {
36-
fn get_dialect(&self) -> Dialect {
37-
self.dialect.as_str().into()
42+
impl Dialect {
43+
pub fn allow_cases(&self) -> bool {
44+
*self != Self::GAMESCRIPT
45+
}
46+
47+
pub fn allow_genders(&self) -> bool {
48+
*self != Self::GAMESCRIPT
3849
}
3950

40-
pub fn allow_cases(&self) -> bool {
41-
self.get_dialect() != Dialect::GAMESCRIPT
51+
pub fn as_str(&self) -> &'static str {
52+
match self {
53+
Self::NEWGRF => "newgrf",
54+
Self::GAMESCRIPT => "game-script",
55+
Self::OPENTTD => "openttd",
56+
}
57+
}
58+
}
59+
60+
impl TryFrom<&str> for Dialect {
61+
type Error = String;
62+
63+
fn try_from(value: &str) -> Result<Self, Self::Error> {
64+
match value {
65+
"newgrf" => Ok(Dialect::NEWGRF),
66+
"game-script" => Ok(Dialect::GAMESCRIPT),
67+
"openttd" => Ok(Dialect::OPENTTD),
68+
_ => Err(String::from("Unknown dialect")),
69+
}
4270
}
71+
}
72+
73+
impl<'de> Deserialize<'de> for Dialect {
74+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75+
where
76+
D: serde::Deserializer<'de>,
77+
{
78+
let string = String::deserialize(deserializer)?;
79+
let value = Dialect::try_from(string.as_str());
80+
value.map_err(|_| {
81+
serde::de::Error::unknown_variant(
82+
string.as_str(),
83+
&["game-script", "newgrf", "openttd"],
84+
)
85+
})
86+
}
87+
}
4388

44-
fn allow_genders(&self) -> bool {
45-
self.get_dialect() != Dialect::GAMESCRIPT
89+
impl Serialize for Dialect {
90+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
91+
where
92+
S: serde::Serializer,
93+
{
94+
serializer.serialize_str(self.as_str())
4695
}
4796
}
4897

@@ -90,7 +139,7 @@ pub fn validate_base(config: &LanguageConfig, base: &String) -> ValidationResult
90139
}
91140
} else {
92141
sanitize_whitespace(&mut base);
93-
normalize_string(&config.get_dialect(), &mut base);
142+
normalize_string(&config.dialect, &mut base);
94143
ValidationResult {
95144
errors: errs,
96145
normalized: Some(base.compile()),
@@ -130,7 +179,7 @@ pub fn validate_translation(
130179
Ok(parsed) => parsed,
131180
};
132181
if case != "default" {
133-
if !config.allow_cases() {
182+
if !config.dialect.allow_cases() {
134183
return ValidationResult {
135184
errors: vec![ValidationError {
136185
severity: Severity::Error,
@@ -177,7 +226,7 @@ pub fn validate_translation(
177226
}
178227
} else {
179228
sanitize_whitespace(&mut translation);
180-
normalize_string(&config.get_dialect(), &mut translation);
229+
normalize_string(&config.dialect, &mut translation);
181230
ValidationResult {
182231
errors: errs,
183232
normalized: Some(translation.compile()),
@@ -299,9 +348,8 @@ fn validate_string(
299348
test: &ParsedString,
300349
base: Option<&ParsedString>,
301350
) -> Vec<ValidationError> {
302-
let dialect = config.get_dialect();
303351
let signature: StringSignature;
304-
match get_signature(&dialect, base.unwrap_or(test)) {
352+
match get_signature(&config.dialect, base.unwrap_or(test)) {
305353
Ok(sig) => signature = sig,
306354
Err(msgs) => {
307355
if base.is_some() {
@@ -333,12 +381,12 @@ fn validate_string(
333381
let opt_info =
334382
opt_expected
335383
.filter(|ex| ex.get_norm_name() == cmd.name)
336-
.or(COMMANDS
337-
.into_iter()
338-
.find(|ci| ci.name == cmd.name && ci.dialects.contains(&dialect)));
384+
.or(COMMANDS.into_iter().find(|ci| {
385+
ci.name == cmd.name && ci.dialects.contains(&config.dialect)
386+
}));
339387
if let Some(info) = opt_info {
340388
if let Some(c) = &cmd.case {
341-
if !config.allow_cases() {
389+
if !config.dialect.allow_cases() {
342390
errors.push(ValidationError {
343391
severity: Severity::Error,
344392
pos_begin: Some(fragment.pos_begin),
@@ -442,7 +490,7 @@ fn validate_string(
442490
front = 2;
443491
}
444492
FragmentContent::Gender(g) => {
445-
if !config.allow_genders() || config.genders.len() < 2 {
493+
if !config.dialect.allow_genders() || config.genders.len() < 2 {
446494
errors.push(ValidationError {
447495
severity: Severity::Error,
448496
pos_begin: Some(fragment.pos_begin),
@@ -497,7 +545,8 @@ fn validate_string(
497545
_ => panic!(),
498546
};
499547
let opt_ref_pos = cmd.indexref.or(opt_ref_pos);
500-
if cmd.name == "G" && (!config.allow_genders() || config.genders.len() < 2) {
548+
if cmd.name == "G" && (!config.dialect.allow_genders() || config.genders.len() < 2)
549+
{
501550
errors.push(ValidationError {
502551
severity: Severity::Error,
503552
pos_begin: Some(fragment.pos_begin),
@@ -881,7 +930,7 @@ mod tests {
881930
#[test]
882931
fn test_validate_empty() {
883932
let config = LanguageConfig {
884-
dialect: String::from("openttd"),
933+
dialect: Dialect::OPENTTD,
885934
cases: vec![],
886935
genders: vec![],
887936
plural_count: 0,
@@ -898,7 +947,7 @@ mod tests {
898947
#[test]
899948
fn test_validate_invalid() {
900949
let config = LanguageConfig {
901-
dialect: String::from("openttd"),
950+
dialect: Dialect::OPENTTD,
902951
cases: vec![],
903952
genders: vec![],
904953
plural_count: 0,
@@ -935,7 +984,7 @@ mod tests {
935984
#[test]
936985
fn test_validate_positional() {
937986
let config = LanguageConfig {
938-
dialect: String::from("openttd"),
987+
dialect: Dialect::OPENTTD,
939988
cases: vec![],
940989
genders: vec![],
941990
plural_count: 0,
@@ -1036,7 +1085,7 @@ mod tests {
10361085
#[test]
10371086
fn test_validate_front() {
10381087
let config = LanguageConfig {
1039-
dialect: String::from("openttd"),
1088+
dialect: Dialect::OPENTTD,
10401089
cases: vec![],
10411090
genders: vec![String::from("a"), String::from("b")],
10421091
plural_count: 0,
@@ -1119,7 +1168,7 @@ mod tests {
11191168
#[test]
11201169
fn test_validate_position_references() {
11211170
let config = LanguageConfig {
1122-
dialect: String::from("openttd"),
1171+
dialect: Dialect::OPENTTD,
11231172
cases: vec![String::from("x"), String::from("y")],
11241173
genders: vec![String::from("a"), String::from("b")],
11251174
plural_count: 2,
@@ -1293,7 +1342,7 @@ mod tests {
12931342
#[test]
12941343
fn test_validate_nochoices() {
12951344
let config = LanguageConfig {
1296-
dialect: String::from("openttd"),
1345+
dialect: Dialect::OPENTTD,
12971346
cases: vec![],
12981347
genders: vec![],
12991348
plural_count: 1,
@@ -1342,7 +1391,7 @@ mod tests {
13421391
#[test]
13431392
fn test_validate_gschoices() {
13441393
let config = LanguageConfig {
1345-
dialect: String::from("game-script"),
1394+
dialect: Dialect::GAMESCRIPT,
13461395
cases: vec![String::from("x"), String::from("y")],
13471396
genders: vec![String::from("a"), String::from("b")],
13481397
plural_count: 2,
@@ -1391,7 +1440,7 @@ mod tests {
13911440
#[test]
13921441
fn test_validate_choices() {
13931442
let config = LanguageConfig {
1394-
dialect: String::from("openttd"),
1443+
dialect: Dialect::OPENTTD,
13951444
cases: vec![String::from("x"), String::from("y")],
13961445
genders: vec![String::from("a"), String::from("b")],
13971446
plural_count: 2,
@@ -1455,7 +1504,7 @@ mod tests {
14551504
#[test]
14561505
fn test_validate_nonpositional() {
14571506
let config = LanguageConfig {
1458-
dialect: String::from("openttd"),
1507+
dialect: Dialect::OPENTTD,
14591508
cases: vec![],
14601509
genders: vec![],
14611510
plural_count: 0,

0 commit comments

Comments
 (0)