undo icon indicating copy to clipboard operation
undo copied to clipboard

Adding More Examples

Open wyhinton opened this issue 4 years ago • 2 comments

Hello, I was wondering if we could possibly have an example of a more complex Undo, maybe one where we a managing the state of an app with the RefCell pattern? I tried to implement Undo Redo for this simple example but could not figure it out, do you have an approach?

use undo::{Action, History};
use std::cell::RefCell;
use std::rc::Rc;
use std::error::Error;

#[derive(Clone, Debug)]
struct AppState{
    names: Vec<String>
    //...
}

struct AppContainer(Rc<RefCell<AppState>>);
type BoxResult<T> = Result<T,Box<Error>>;

impl AppContainer{
    fn new (inner: AppState)->AppContainer{
        let cl = AppState{
            names: inner.names.clone(),
        };
        return AppContainer(Rc::new(RefCell::new(cl)));
    }
    fn add_name(&self, name: String){
        (self.0.borrow_mut()).names.push(name);
    }
    fn remove_name(&self, to_remove: String){
        (self.0.borrow_mut()).names.retain(|x| *x != to_remove)
    }
    fn names(&self)->Vec<String>{
        (self.0.borrow()).names
    }
}

struct AddName(String);
impl Action for AddName{
    type Target = (String, AppContainer);
    type Output = ();
    type Error = &'static str;

    fn apply(&mut self, s: (String, &mut AppContainer)) -> undo::Result<AddName> {
        s.1.add_name(s.0);
        Ok(())
    }

    fn undo(&mut self, s: (String, &mut AppContainer)) -> undo::Result<AddName> {
        s.1.remove_name(s.0);
        Ok(())
    }
    
}
//inverse of AddName
struct RemoveName(String);
impl Action for RemoveName{
    type Target = (String, AppContainer);
    type Output = ();
    type Error = &'static str;

    fn apply(&mut self, s: (String, &mut AppContainer)) -> undo::Result<RemoveName> {
        s.1.remove_name(s.0);
        Ok(())
    }

    fn undo(&mut self, s: (String, &mut AppContainer)) -> undo::Result<RemoveName> {
        s.1.add_name(s.0);
        Ok(())
    }
    
}

// fn main()->undo::Result<Box<dyn Action>>{
fn main(){
    type Target = (String, AppContainer);
    type Output = ();
    type Error = &'static str;

    let initial_state = AppState{
        names: vec!["name 1".to_string(), "name 2".to_string()],
    };
    let mut history = History::new();
    let container = AppContainer::new(initial_state.clone());
    history.apply(&mut container, AddName("My Name".to_string())).unwrap();
    dbg!(container.names());
    history.apply(&mut container, RemoveName("Name 1".to_string())).unwrap();
    dbg!(container.names());
    // Ok(())
}

wyhinton avatar Apr 18 '21 20:04 wyhinton

Reddit User Kythzu was kind enough to provide me with this solution to my problem, maybe with a little clean up it could be added to the examples folder?

Also see this post on stackoverflow for another possible example.

use std::cell::RefCell;
use std::error::Error;
use std::rc::Rc;
use undo::{Action, History};

#[derive(Clone, Debug)]
struct AppState {
    names: Vec<String>, //...
}

struct AppContainer(Rc<RefCell<AppState>>);
type BoxResult<T> = Result<T, Box<dyn Error>>;

impl AppContainer {
    fn new(inner: AppState) -> AppContainer {
        let cl = AppState {
            names: inner.names.clone(),
        };
        return AppContainer(Rc::new(RefCell::new(cl)));
    }
    fn add_name(&self, name: String) {
        (self.0.borrow_mut()).names.push(name);
    }
    fn remove_name(&self, to_remove: String) {
        (self.0.borrow_mut()).names.retain(|x| *x != to_remove)
    }
    fn names(&self) -> Vec<String> {
        (self.0.borrow()).names.clone()
    }
}

struct AddName(String);
impl Action for AddName {
    type Target = AppContainer;
    type Output = ();
    type Error = &'static str;

    fn apply(&mut self, app: &mut AppContainer) -> undo::Result<AddName> {
        app.add_name(self.0.clone());
        Ok(())
    }

    fn undo(&mut self, app: &mut AppContainer) -> undo::Result<AddName> {
        app.remove_name(self.0.clone());
        Ok(())
    }
}
//inverse of AddName
struct RemoveName(String);
impl Action for RemoveName {
    type Target = AppContainer;
    type Output = ();
    type Error = &'static str;

    fn apply(&mut self, app: &mut AppContainer) -> undo::Result<RemoveName> {
        app.remove_name(self.0.clone());
        Ok(())
    }

    fn undo(&mut self, app: &mut AppContainer) -> undo::Result<RemoveName> {
        app.add_name(self.0.clone());
        Ok(())
    }
}

fn main() -> BoxResult<()> {
    let initial_state = AppState {
        names: vec!["name 1".to_string(), "name 2".to_string()],
    };
    let mut history = History::new();
    let mut container = AppContainer::new(initial_state.clone());
    history
        .apply(&mut container, AddName("My Name".to_string()))?;
    dbg!(container.names());
    history
        .undo(&mut container).unwrap()?;
    dbg!(container.names());
    Ok(())
}

wyhinton avatar Apr 24 '21 07:04 wyhinton

Hi! I will hopefully be able to add more and better examples/documentations at some point when i have the time and the libraries are more stable.

I'll keep these examples in mind for an example with global target and history state!

evenorog avatar Apr 25 '21 14:04 evenorog