1pub fn strip_formatting(s: &str) -> String {
9 let mut out = String::with_capacity(s.len());
10 let mut it = s.chars();
11 while let Some(c) = it.next() {
12 if c == '\u{a7}' {
13 it.next();
14 } else {
15 out.push(c);
16 }
17 }
18 out
19}
20
21#[cfg(feature = "chat-json")]
23pub fn parse(json: &str) -> Result<serde_json::Value, serde_json::Error> {
24 serde_json::from_str(json)
25}
26
27#[cfg(feature = "chat-json")]
31pub fn to_plain_text(json: &str) -> String {
32 fn walk(v: &serde_json::Value, out: &mut String) {
33 use serde_json::Value;
34 match v {
35 Value::String(s) => out.push_str(s),
36 Value::Array(a) => a.iter().for_each(|x| walk(x, out)),
37 Value::Object(o) => {
38 if let Some(t) = o.get("text") {
39 walk(t, out);
40 }
41 if let Some(Value::String(t)) = o.get("translate") {
42 out.push_str(t);
43 if let Some(Value::Array(with)) = o.get("with") {
44 for w in with {
45 out.push(' ');
46 walk(w, out);
47 }
48 }
49 }
50 if let Some(e) = o.get("extra") {
51 walk(e, out);
52 }
53 }
54 Value::Number(n) => out.push_str(&n.to_string()),
55 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
56 Value::Null => {}
57 }
58 }
59 match parse(json) {
60 Ok(v) => {
61 let mut out = String::new();
62 walk(&v, &mut out);
63 out
64 }
65 Err(_) => json.to_string(),
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn strip() {
75 assert_eq!(
76 strip_formatting("\u{a7}aHello \u{a7}lworld\u{a7}"),
77 "Hello world"
78 );
79 }
80
81 #[cfg(feature = "chat-json")]
82 #[test]
83 fn plain_text() {
84 assert_eq!(
85 to_plain_text(r#"{"text":"Hi ","extra":[{"text":"there","bold":true},"!"]}"#),
86 "Hi there!"
87 );
88 assert_eq!(to_plain_text(r#""bare""#), "bare");
89 assert_eq!(to_plain_text("not json"), "not json");
90 }
91}