From e0130b5f1e82e8d761101eba94b448190e94ce16 Mon Sep 17 00:00:00 2001 From: David Senk Date: Sat, 7 Sep 2024 16:56:36 -0400 Subject: [PATCH] with state changing transaction --- src/lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++++ tests/state_changer.rs | 40 ++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 22f2cb9..a72d8b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,10 @@ pub struct StateOwner { state: Rc>, } +pub struct StateKeeper<'a> { + state: RwLockWriteGuard<'a, String>, +} + impl StateOwner { pub fn new(state: String) -> StateOwner { StateOwner { state: Rc::new(RwLock::new(state)) } @@ -17,5 +21,67 @@ impl StateOwner { Cow::from(ret) } + //WARNING: As this is just a "proof of concept" + //you can unintentionally cause deadlocks by trying to call + //.get_state() before closing out the transaction + //WARNING: StateKeeper can not be instantiated outside of this + //file as there is no public access to StateKeeper.state; however, + //.close_transaction() does not verify that the StateKeeper provided + //is the same one that was generated for &self, again, this is a "proof of concept" + //and handling this is outside the scope of the concept + pub fn begin_transaction(&self) -> StateKeeper { + StateKeeper { state: self.state.write().unwrap() } + } + + pub fn check_transactions(&self, state_keeper: &StateKeeper, change_queue: &[StateModifier]) -> bool { + for modifier in change_queue { + match modifier { + //ReplaceFull will always be valid + StateModifier::ReplaceFull(_) => {}, + StateModifier::ReplaceAt(pos, c) => { + if pos > &state_keeper.state.len() { + return false; + } + if !c.is_ascii_alphanumeric() { + return false; + } + } + } + } + true + } + + pub fn close_transaction(&self, state_keeper: StateKeeper, change_queue: Vec) { + + let mut state = state_keeper.state; + + for modifier in change_queue { + print!("Applying modifier: "); + match modifier { + StateModifier::ReplaceFull(new_state) => { + print!("Replace Full: Before: {state}, "); + *state = new_state; + } + StateModifier::ReplaceAt(pos, c) => { + //WARNING: This will panic if pos is out of bounds + //out of scope of concept to handle this + print!("Replace At: Before: {state}, "); + //This is being *really* naughty and unsafe + //but again, proof of concept + unsafe { + state.as_bytes_mut()[pos] = c; + } + } + } + println!("After: {state}"); + } + } } + + +#[derive(Debug)] +pub enum StateModifier { + ReplaceFull(String), + ReplaceAt(usize, u8), +} \ No newline at end of file diff --git a/tests/state_changer.rs b/tests/state_changer.rs index ecee02b..58a2d57 100644 --- a/tests/state_changer.rs +++ b/tests/state_changer.rs @@ -1,4 +1,4 @@ -use state_queue::StateOwner; +use state_queue::{StateModifier, StateOwner}; #[test] fn create_state_owner() { @@ -7,4 +7,42 @@ fn create_state_owner() { let example = StateOwner::new(String::from("State Owner Created!")); assert_eq!(example.get_state(), "State Owner Created!"); +} + +#[test] +fn create_and_modify_state_owner() { + let example = StateOwner::new(String::from("State Owner Created!")); + + let mut modifiers: Vec = Vec::new(); + + modifiers.push(StateModifier::ReplaceAt(0, b's')); + modifiers.push(StateModifier::ReplaceAt(6, b'o')); + modifiers.push(StateModifier::ReplaceAt(12, b'c')); + modifiers.push(StateModifier::ReplaceFull(String::from("State Fully Changed!"))); + + let keeper = example.begin_transaction(); + + //check individually + for modifier in &modifiers { + let is_valid = example.check_transactions(&keeper, std::slice::from_ref(modifier)); + println!("{modifier:?} - Valid? {is_valid}"); + + assert!(is_valid); + } + + //check all at once + + let is_valid = example.check_transactions(&keeper, modifiers.as_slice()); + + println!("All valid in one shot? {is_valid}"); + + assert!(is_valid); + + example.close_transaction(keeper, modifiers); + + println!("Current State? {}", example.get_state()); + + assert_eq!(example.get_state(), "State Fully Changed!"); + + } \ No newline at end of file