doido_core/inflector/config.rs
1//! Project-level inflection overrides loaded from `config/inflection.yaml`.
2//!
3//! The file layers custom rules on top of the default English rules. Every
4//! section is optional:
5//!
6//! ```yaml
7//! # config/inflection.yaml
8//! irregulars:
9//! - { singular: goose, plural: geese }
10//! - { singular: person, plural: people }
11//! uncountables:
12//! - bitcoin
13//! - equipment
14//! acronyms:
15//! - API
16//! - HTTP
17//! plurals:
18//! - { pattern: "(quiz)$", replacement: "${1}zes" }
19//! singulars:
20//! - { pattern: "(quiz)zes$", replacement: "${1}" }
21//! ```
22
23use serde::Deserialize;
24
25use super::Inflections;
26
27/// A regex-based plural/singular rule: `pattern` is matched (case-insensitively
28/// at use time) and rewritten with `replacement` (supports `${1}` captures).
29#[derive(Debug, Clone, Deserialize)]
30pub struct Rule {
31 pub pattern: String,
32 pub replacement: String,
33}
34
35/// An irregular word pair, e.g. `person` ↔ `people`.
36#[derive(Debug, Clone, Deserialize)]
37pub struct Irregular {
38 pub singular: String,
39 pub plural: String,
40}
41
42/// Deserialized form of `config/inflection.yaml`. Unknown keys are rejected so
43/// typos surface instead of being silently ignored.
44#[derive(Debug, Clone, Default, Deserialize)]
45#[serde(default, deny_unknown_fields)]
46pub struct InflectionConfig {
47 pub plurals: Vec<Rule>,
48 pub singulars: Vec<Rule>,
49 pub irregulars: Vec<Irregular>,
50 pub uncountables: Vec<String>,
51 pub acronyms: Vec<String>,
52}
53
54impl InflectionConfig {
55 /// Parse a YAML document into a config.
56 pub fn from_yaml(yaml: &str) -> Result<Self, serde_norway::Error> {
57 serde_norway::from_str(yaml)
58 }
59
60 /// Apply these overrides onto an existing `Inflections` set. Rules added here
61 /// take priority over the defaults already present (last-added wins).
62 pub fn apply(&self, inflections: &mut Inflections) {
63 for rule in &self.plurals {
64 inflections.plural(&rule.pattern, &rule.replacement);
65 }
66 for rule in &self.singulars {
67 inflections.singular(&rule.pattern, &rule.replacement);
68 }
69 for irr in &self.irregulars {
70 inflections.irregular(&irr.singular, &irr.plural);
71 }
72 for word in &self.uncountables {
73 inflections.uncountable(word);
74 }
75 for word in &self.acronyms {
76 inflections.acronym(word);
77 }
78 }
79}