use muharrir::command::{Command, CommandHistory, CompoundCommand};
use std::convert::Infallible;
#[derive(Debug, Default)]
struct Document {
lines: Vec<String>,
}
#[derive(Debug)]
struct InsertLine {
index: usize,
text: String,
}
impl Command for InsertLine {
type Target = Document;
type Error = Infallible;
fn apply(&mut self, doc: &mut Document) -> Result<(), Infallible> {
doc.lines.insert(self.index, self.text.clone());
Ok(())
}
fn reverse(&mut self, doc: &mut Document) -> Result<(), Infallible> {
doc.lines.remove(self.index);
Ok(())
}
fn description(&self) -> &str {
"insert line"
}
}
#[derive(Debug)]
struct RemoveLine {
index: usize,
removed: Option<String>,
}
impl Command for RemoveLine {
type Target = Document;
type Error = Infallible;
fn apply(&mut self, doc: &mut Document) -> Result<(), Infallible> {
self.removed = Some(doc.lines.remove(self.index));
Ok(())
}
fn reverse(&mut self, doc: &mut Document) -> Result<(), Infallible> {
if let Some(text) = self.removed.take() {
doc.lines.insert(self.index, text);
}
Ok(())
}
fn description(&self) -> &str {
"remove line"
}
}
fn main() {
let mut doc = Document::default();
let mut history: CommandHistory<Box<dyn Command<Target = Document, Error = Infallible>>> =
CommandHistory::with_max_depth(100);
for (i, text) in ["Hello", "World", "!"].iter().enumerate() {
history
.execute(
Box::new(InsertLine {
index: i,
text: text.to_string(),
}),
&mut doc,
)
.unwrap();
}
println!("After inserts: {:?}", doc.lines);
let compound: Box<dyn Command<Target = Document, Error = Infallible>> =
Box::new(CompoundCommand::new(
"replace line",
vec![
Box::new(RemoveLine {
index: 1,
removed: None,
}) as Box<dyn Command<Target = Document, Error = Infallible>>,
Box::new(InsertLine {
index: 1,
text: "Muharrir".into(),
}),
],
));
history.execute(compound, &mut doc).unwrap();
println!("After replace: {:?}", doc.lines);
history.undo(&mut doc).unwrap();
println!("After undo: {:?}", doc.lines);
history.redo(&mut doc).unwrap();
println!("After redo: {:?}", doc.lines);
println!(
"\nHistory: {} undo, {} redo",
history.undo_count(),
history.redo_count()
);
#[cfg(feature = "history")]
{
use muharrir::history::{Action, History};
let mut audit = History::new();
audit.record(
"example",
Action::new("insert", serde_json::json!({"lines": 3})),
);
audit.record(
"example",
Action::new("replace", serde_json::json!({"index": 1})),
);
println!(
"Audit chain: {} entries, verified={}",
audit.len(),
audit.verify()
);
}
}