//! Text from outside (model output, tool arguments, server errors) made safe to show: control //! characters, zero-width and bidirectional marks, line separators and the BOM become `\uXXXX`. //! Used by `bxctl` for the terminal and by `gatewayd` for Mattermost, so both show the same. // The control bytes, the zero-width and Unicode line/paragraph separators, and the BOM. A code // point in any of these ranges must never reach the terminal raw. const ESCAPE_RANGES: &[(u32, u32)] = &[ (0x0000, 0x001f), (0x007f, 0x009f), (0x200b, 0x200f), (0x2028, 0x202e), (0x2060, 0x2069), (0xfeff, 0xfeff), ]; pub fn escape_json_text(s: &str) -> String { escape(s, false) } pub fn escape_model_text(s: &str) -> String { escape(s, true) } fn escape(s: &str, model_text: bool) -> String { let mut out = String::with_capacity(s.len()); for c in s.chars() { let cp = u32::from(c); if should_escape(c, model_text) { out.push_str(&format!("\\u{:04x}", cp)); } else { out.push(c); } } out } // A control point is escaped, except that model text keeps \n and \t. fn should_escape(c: char, model_text: bool) -> bool { let cp = u32::from(c); if ESCAPE_RANGES.iter().any(|(lo, hi)| cp >= *lo && cp <= *hi) { !(model_text && (cp == 0x09 || cp == 0x0a)) } else { false } }