use std::collections::{HashMap, HashSet};
#[derive(Default)]
pub struct EditBuffers {
text: HashMap<String, String>,
number: HashMap<String, f64>,
boolean: HashMap<String, bool>,
choice: HashMap<String, String>,
synced_this_frame: HashSet<String>,
generation: u64,
}
impl EditBuffers {
pub fn begin_frame(&mut self) {
self.synced_this_frame.clear();
}
pub fn invalidate(&mut self) {
self.text.clear();
self.number.clear();
self.boolean.clear();
self.choice.clear();
self.synced_this_frame.clear();
self.generation = self.generation.wrapping_add(1);
}
pub fn text_buffer(&mut self, id: &str, resolved: &str, focused: bool) -> &mut String {
if !self.text.contains_key(id) {
self.text.insert(id.to_string(), resolved.to_string());
self.synced_this_frame.insert(id.to_string());
} else if !focused && !self.synced_this_frame.contains(id) {
self.text.insert(id.to_string(), resolved.to_string());
self.synced_this_frame.insert(id.to_string());
}
self.text.get_mut(id).expect("just inserted/ensured")
}
pub fn number_buffer(&mut self, id: &str, resolved: f64) -> &mut f64 {
if !self.number.contains_key(id) || !self.synced_this_frame.contains(id) {
self.number.insert(id.to_string(), resolved);
self.synced_this_frame.insert(id.to_string());
}
self.number.get_mut(id).expect("just inserted/ensured")
}
pub fn boolean_buffer(&mut self, id: &str, resolved: bool) -> &mut bool {
if !self.boolean.contains_key(id) || !self.synced_this_frame.contains(id) {
self.boolean.insert(id.to_string(), resolved);
self.synced_this_frame.insert(id.to_string());
}
self.boolean.get_mut(id).expect("just inserted/ensured")
}
pub fn choice_buffer(&mut self, id: &str, resolved: &str) -> &mut String {
if !self.choice.contains_key(id) || !self.synced_this_frame.contains(id) {
self.choice.insert(id.to_string(), resolved.to_string());
self.synced_this_frame.insert(id.to_string());
}
self.choice.get_mut(id).expect("just inserted/ensured")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn text_buffer_seeds_from_resolved_when_absent() {
let mut eb = EditBuffers::default();
let buf = eb.text_buffer("f1", "hello", false);
assert_eq!(buf, "hello");
}
#[test]
fn text_buffer_keeps_buffer_across_frames_until_externally_changed() {
let mut eb = EditBuffers::default();
eb.begin_frame();
*eb.text_buffer("f1", "hello", false) = "world".to_string();
eb.begin_frame();
let buf = eb.text_buffer("f1", "hello", true);
assert_eq!(buf, "world", "focused component keeps its buffer");
}
#[test]
fn text_buffer_reseeds_when_not_focused_and_unsynced() {
let mut eb = EditBuffers::default();
eb.begin_frame();
*eb.text_buffer("f1", "hello", false) = "world".to_string();
eb.begin_frame();
let buf = eb.text_buffer("f1", "changed", false);
assert_eq!(buf, "changed");
}
#[test]
fn number_and_boolean_seed() {
let mut eb = EditBuffers::default();
eb.begin_frame();
let n = eb.number_buffer("s", 7.0);
assert!((*n - 7.0).abs() < 1e-9);
eb.begin_frame();
let b = eb.boolean_buffer("c", true);
assert!(*b);
}
#[test]
fn invalidate_drops_all_buffers() {
let mut eb = EditBuffers::default();
eb.begin_frame();
*eb.text_buffer("f1", "hello", false) = "x".to_string();
eb.invalidate();
let buf = eb.text_buffer("f1", "fresh", false);
assert_eq!(buf, "fresh");
}
}