diff --git a/src/dynamic_string.rs b/src/dynamic_string.rs index 43c7e89d..1ef0e2b4 100644 --- a/src/dynamic_string.rs +++ b/src/dynamic_string.rs @@ -4,6 +4,9 @@ use gtk::prelude::*; use std::sync::{Arc, Mutex}; use tokio::spawn; +/// A segment of a dynamic string, +/// containing either a static string +/// or a script. #[derive(Debug)] enum DynamicStringSegment { Static(String), @@ -20,47 +23,7 @@ impl DynamicString { where F: FnMut(String) -> Continue + 'static, { - let mut segments = vec![]; - - let mut chars = input.chars().collect::>(); - while !chars.is_empty() { - let char = &chars[..=1]; - - let (token, skip) = if let ['{', '{'] = char { - const SKIP_BRACKETS: usize = 4; - - let str = chars - .iter() - .skip(2) - .enumerate() - .take_while(|(i, &c)| c != '}' && chars[i + 1] != '}') - .map(|(_, c)| c) - .collect::(); - - let len = str.len(); - - ( - DynamicStringSegment::Dynamic(Script::from(str.as_str())), - len + SKIP_BRACKETS, - ) - } else { - let str = chars - .iter() - .enumerate() - .take_while(|(i, &c)| !(c == '{' && chars[i + 1] == '{')) - .map(|(_, c)| c) - .collect::(); - - let len = str.len(); - - (DynamicStringSegment::Static(str), len) - }; - - assert_ne!(skip, 0); - - segments.push(token); - chars.drain(..skip); - } + let segments = Self::parse_input(input); let label_parts = Arc::new(Mutex::new(Vec::new())); let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); @@ -105,6 +68,62 @@ impl DynamicString { Self } + + /// Parses the input string into static and dynamic segments + fn parse_input(input: &str) -> Vec { + if !input.contains("{{") { + return vec![DynamicStringSegment::Static(input.to_string())]; + } + + let mut segments = vec![]; + + let mut chars = input.chars().collect::>(); + while !chars.is_empty() { + let char_pair = &chars[..=1]; + + let (token, skip) = if let ['{', '{'] = char_pair { + const SKIP_BRACKETS: usize = 4; // two braces either side + + let str = chars + .windows(2) + .skip(2) + .take_while(|win| win != &['}', '}']) + .map(|w| w[0]) + .collect::(); + + let len = str.len(); + + ( + DynamicStringSegment::Dynamic(Script::from(str.as_str())), + len + SKIP_BRACKETS, + ) + } else { + let mut str = chars + .windows(2) + .take_while(|win| win != &['{', '{']) + .map(|w| w[0]) + .collect::(); + + // if segment is at end of string, last char gets missed above due to uneven window. + if chars.len() == str.len() + 1 { + let remaining_char = *chars.get(str.len()).expect("Failed to find last char"); + str.push(remaining_char); + } + + let len = str.len(); + + (DynamicStringSegment::Static(str), len) + }; + + // quick runtime check to make sure the parser is working as expected + assert_ne!(skip, 0); + + segments.push(token); + chars.drain(..skip); + } + + segments + } } #[cfg(test)]