1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Mood {
15 Frustrated,
16 Urgent,
17 Sad,
18 Confused,
19 Grateful,
20 Excited,
21 Playful,
22}
23
24impl Mood {
25 fn label(self) -> &'static str {
27 match self {
28 Mood::Frustrated => "frustrated",
29 Mood::Urgent => "in a hurry",
30 Mood::Sad => "down",
31 Mood::Confused => "confused",
32 Mood::Grateful => "appreciative",
33 Mood::Excited => "excited",
34 Mood::Playful => "playful",
35 }
36 }
37
38 fn guidance(self) -> &'static str {
40 match self {
41 Mood::Frustrated => {
42 "Acknowledge the frustration briefly, skip the cheer, and get straight to fixing the problem."
43 }
44 Mood::Urgent => {
45 "Be fast and direct — give the answer first and trim everything optional."
46 }
47 Mood::Sad => {
48 "Lead with a little genuine warmth before the practical part; don't be chirpy."
49 }
50 Mood::Confused => {
51 "Slow down, use plainer words, and give one clear step at a time."
52 }
53 Mood::Grateful => {
54 "A brief, warm acknowledgement is enough — don't overdo it."
55 }
56 Mood::Excited => "Match their energy a little, then keep things moving.",
57 Mood::Playful => {
58 "A light, casual tone fits here, and a touch of humor is welcome."
59 }
60 }
61 }
62}
63
64fn contains_any(haystack: &str, needles: &[&str]) -> bool {
65 needles.iter().any(|n| haystack.contains(n))
66}
67
68pub fn detect_mood(text: &str) -> Option<Mood> {
72 let t = text.trim();
73 if t.is_empty() {
74 return None;
75 }
76 let lower = t.to_lowercase();
77
78 let strong_punct = t.contains("?!") || t.contains("!!!") || t.contains("?!");
80 if contains_any(
81 &lower,
82 &[
83 "frustrat",
84 "annoying",
85 "annoyed",
86 "this is ridiculous",
87 "not working",
88 "still broken",
89 "still not working",
90 "doesn't work",
91 "doesnt work",
92 "wtf",
93 " ugh",
94 "ugh ",
95 "useless",
96 "i hate",
97 "so stupid",
98 "again?!",
99 ],
100 ) || contains_any(
101 t,
102 &[
103 "烦",
104 "崩溃",
105 "无语",
106 "搞什么",
107 "又不行",
108 "还是不行",
109 "气死",
110 "什么破",
111 "废物",
112 "垃圾",
113 "受不了",
114 "服了",
115 ],
116 ) || (strong_punct
117 && (contains_any(&lower, &["why", "still", "again"])
118 || contains_any(t, &["为什么", "怎么", "又"])))
119 {
120 return Some(Mood::Frustrated);
121 }
122
123 if contains_any(
125 &lower,
126 &[
127 "asap",
128 "urgent",
129 "right now",
130 "immediately",
131 "hurry",
132 "deadline",
133 "emergency",
134 "as fast as",
135 "quickly please",
136 "need it now",
137 ],
138 ) || contains_any(
139 t,
140 &[
141 "很急",
142 "急",
143 "马上",
144 "立刻",
145 "赶紧",
146 "尽快",
147 "快点",
148 "现在就要",
149 "等不及",
150 ],
151 ) {
152 return Some(Mood::Urgent);
153 }
154
155 if contains_any(
157 &lower,
158 &[
159 "depressed",
160 "burned out",
161 "burnt out",
162 "overwhelmed",
163 "exhausted",
164 "feeling down",
165 "so tired",
166 "i'm sad",
167 "im sad",
168 "can't cope",
169 "cant cope",
170 ],
171 ) || contains_any(
172 t,
173 &[
174 "难过",
175 "伤心",
176 "好累",
177 "累了",
178 "压力好大",
179 "撑不住",
180 "焦虑",
181 "心累",
182 "失落",
183 ],
184 ) {
185 return Some(Mood::Sad);
186 }
187
188 if contains_any(
190 &lower,
191 &[
192 "i'm confused",
193 "im confused",
194 "i don't understand",
195 "i dont understand",
196 "doesn't make sense",
197 "what do you mean",
198 "i'm lost",
199 "im lost",
200 "no idea what",
201 ],
202 ) || contains_any(
203 t,
204 &[
205 "看不懂",
206 "不明白",
207 "搞不懂",
208 "什么意思",
209 "没看懂",
210 "懵",
211 "不懂",
212 ],
213 ) {
214 return Some(Mood::Confused);
215 }
216
217 if contains_any(
219 &lower,
220 &[
221 "thank you",
222 "thanks",
223 "appreciate it",
224 "you're the best",
225 "lifesaver",
226 "much appreciated",
227 ],
228 ) || contains_any(t, &["谢谢", "感谢", "太感谢", "辛苦了", "多谢", "感激"])
229 {
230 return Some(Mood::Grateful);
231 }
232
233 if contains_any(
235 &lower,
236 &[
237 "awesome",
238 "amazing",
239 "let's go",
240 "lets go",
241 "can't wait",
242 "cant wait",
243 "so excited",
244 "this is great",
245 "love it",
246 "incredible",
247 "🎉",
248 "🚀",
249 "🔥",
250 ],
251 ) || contains_any(
252 t,
253 &[
254 "太棒",
255 "太好了",
256 "厉害",
257 "牛",
258 "期待",
259 "激动",
260 "爱了",
261 "yyds",
262 ],
263 ) {
264 return Some(Mood::Excited);
265 }
266
267 if contains_any(&lower, &["lol", "lmao", "rofl", "😂", "🤣", "😜", "haha"])
269 || contains_any(t, &["哈哈", "嘻嘻", "2333", "doge"])
270 {
271 return Some(Mood::Playful);
272 }
273
274 None
275}
276
277pub fn mood_hint(text: &str) -> Option<String> {
279 detect_mood(text).map(|m| format!("The user sounds {}. {}", m.label(), m.guidance()))
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn neutral_text_yields_no_mood() {
288 assert_eq!(detect_mood("what's the capital of France?"), None);
289 assert_eq!(detect_mood(""), None);
290 assert!(mood_hint("please summarize this file").is_none());
291 }
292
293 #[test]
294 fn detects_frustration_en_and_zh() {
295 assert_eq!(
296 detect_mood("this is still not working, ugh"),
297 Some(Mood::Frustrated)
298 );
299 assert_eq!(detect_mood("又不行,气死了"), Some(Mood::Frustrated));
300 }
301
302 #[test]
303 fn detects_urgency_and_sadness() {
304 assert_eq!(detect_mood("I need this ASAP"), Some(Mood::Urgent));
305 assert_eq!(detect_mood("赶紧帮我看下"), Some(Mood::Urgent));
306 assert_eq!(
307 detect_mood("I'm so exhausted and overwhelmed"),
308 Some(Mood::Sad)
309 );
310 }
311
312 #[test]
313 fn gratitude_and_excitement_and_hint_text() {
314 assert_eq!(detect_mood("thank you so much!"), Some(Mood::Grateful));
315 assert_eq!(detect_mood("太棒了,厉害"), Some(Mood::Excited));
316 let hint = mood_hint("this is still broken, wtf").unwrap();
317 assert!(hint.contains("frustrated"));
318 }
319}