srgn/actions/german/
machine.rs1use log::trace;
2
3use super::LetterCasing::{Lower, Upper};
4use super::SpecialCharacter::{Eszett, Umlaut};
5use super::Umlaut::{Ae, Oe, Ue};
6use super::{SpecialCharacter, Word};
7
8#[derive(Default, Debug)]
9enum State {
10 #[default]
11 Other,
12 Word(Option<Potential>),
13}
14
15#[derive(Debug)]
19struct Potential(SpecialCharacter);
20
21#[derive(Debug, Clone)]
22pub(super) enum Transition {
23 Entered,
25 Exited,
27 Internal,
29 External,
31}
32
33impl Transition {
34 const fn from_states(from: &State, to: &State) -> Self {
35 match (from, to) {
36 (State::Word(_), State::Other) => Self::Exited,
37 (State::Other, State::Word(_)) => Self::Entered,
38 (State::Word(_), State::Word(_)) => Self::Internal,
39 (State::Other, State::Other) => Self::External,
40 }
41 }
42}
43
44type MachineInput = char;
45
46#[derive(Debug)]
47pub(super) struct StateMachine {
48 state: State,
49 word: Word,
50 transition: Option<Transition>,
51}
52
53impl StateMachine {
54 pub(super) fn new() -> Self {
55 Self {
56 state: State::default(),
57 word: Word::default(),
58 transition: None,
59 }
60 }
61
62 pub(super) const fn current_word(&self) -> &Word {
63 &self.word
64 }
65
66 fn pre_transition(&mut self) {
67 if matches!(self.state, State::Other) {
68 self.word.clear();
69
70 trace!("Cleared current word, machine now is: {self:?}.");
71 }
72 }
73
74 pub(super) fn transition(&mut self, input: MachineInput) -> Transition {
75 self.pre_transition();
76
77 let next = match (&self.state, input) {
78 (State::Word(Some(Potential(Umlaut(umlaut)))), c @ ('e' | 'E')) => {
79 const LENGTH_OF_PREVIOUS_CHARACTER: usize = 1;
80
81 let pos = self.word.len();
82
83 debug_assert!(
86 'o'.len_utf8() == LENGTH_OF_PREVIOUS_CHARACTER
87 && 'u'.len_utf8() == LENGTH_OF_PREVIOUS_CHARACTER
88 && 'a'.len_utf8() == LENGTH_OF_PREVIOUS_CHARACTER
89 );
90
91 let start = pos - LENGTH_OF_PREVIOUS_CHARACTER;
92 let end = pos + c.len_utf8();
93 self.word.add_replacement(start, end, Umlaut(*umlaut));
94
95 trace!("Added replacement at position {pos}, machine now is: {self:?}.");
96
97 State::Word(None)
98 }
99 (State::Word(Some(Potential(Eszett(casing)))), c @ ('s' | 'S')) => {
100 let pos = self.word.len();
101
102 let start = pos - c.len_utf8(); let end = pos + c.len_utf8();
104 self.word.add_replacement(start, end, Eszett(*casing));
105
106 trace!("Added replacement at position {pos}, machine now is: {self:?}.");
107
108 State::Word(None)
109 }
110 (_, 'a') => State::Word(Some(Potential(Umlaut(Ae(Lower))))),
111 (_, 'A') => State::Word(Some(Potential(Umlaut(Ae(Upper))))),
112 (_, 'o') => State::Word(Some(Potential(Umlaut(Oe(Lower))))),
113 (_, 'O') => State::Word(Some(Potential(Umlaut(Oe(Upper))))),
114 (_, 'u') => State::Word(Some(Potential(Umlaut(Ue(Lower))))),
115 (_, 'U') => State::Word(Some(Potential(Umlaut(Ue(Upper))))),
116 (_, 's') => State::Word(Some(Potential(Eszett(Lower)))),
117 (_, 'S') => State::Word(Some(Potential(Eszett(Upper)))),
118 (_, c) if c.is_alphabetic() => State::Word(None),
119 _ => State::Other,
120 };
121
122 let transition = Transition::from_states(&self.state, &next);
123
124 self.state = next;
125 self.transition = Some(transition.clone()); self.post_transition(input);
128
129 transition
130 }
131
132 fn post_transition(&mut self, input: MachineInput) {
133 if let Some(Transition::Entered | Transition::Internal) = self.transition {
134 self.word.push(input);
135 trace!(
136 "Appending {:?} to current word due to transition {:?}.",
137 input, self.transition
138 );
139 }
140
141 trace!("After transition, machine is: {self:?}.");
142 }
143}