Skip to content

Commit

Permalink
fix(dynamic string): parser issue related to incorrectly matching braces
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeStanger committed Apr 9, 2023
1 parent dfe1964 commit 9109453
Showing 1 changed file with 60 additions and 41 deletions.
101 changes: 60 additions & 41 deletions src/dynamic_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -20,47 +23,7 @@ impl DynamicString {
where
F: FnMut(String) -> Continue + 'static,
{
let mut segments = vec![];

let mut chars = input.chars().collect::<Vec<_>>();
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::<String>();

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::<String>();

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);
Expand Down Expand Up @@ -105,6 +68,62 @@ impl DynamicString {

Self
}

/// Parses the input string into static and dynamic segments
fn parse_input(input: &str) -> Vec<DynamicStringSegment> {
if !input.contains("{{") {
return vec![DynamicStringSegment::Static(input.to_string())];
}

let mut segments = vec![];

let mut chars = input.chars().collect::<Vec<_>>();
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::<String>();

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::<String>();

// 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)]
Expand Down

0 comments on commit 9109453

Please sign in to comment.