Skip to main content

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}