Skip to main content

EditSession

Struct EditSession 

Source
pub struct EditSession<T, C> { /* private fields */ }
Expand description

A single-use edit. It never writes to a document or records history itself.

This type intentionally does not implement Clone. finish consumes it, so the same session cannot accidentally emit two commits. A failed finish also consumes the draft; call check first if invalid input should remain editable in the UI.

Implementations§

Source§

impl<T: Clone, C> EditSession<T, C>

Source

pub fn new(original: T, context: C) -> Self

Begin from a value already validated by the application.

Cloning original must produce an independently editable snapshot. Shared mutable pointers such as Rc<RefCell<_>> can let edits change the original or live document and defeat cancellation/conflict checks. Use owned fields or immutable shared data. The context must also remain a stable snapshot until the session ends.

Examples found in repository?
examples/retry_command.rs (line 78)
75    fn open(host: &Host) -> Self {
76        Self {
77            text: host.value.clone(),
78            draft: Some(EditSession::new(host.value.clone(), host.context)),
79        }
80    }
More examples
Hide additional examples
examples/context_walkthrough.rs (line 59)
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}
Source

pub fn reset(&mut self)

Restore the original preview and clear invalid input, keeping the draft open.

The original value and context are retained: this does not rebase a stale draft or change the live document. The host must refresh its displayed preview itself.

Examples found in repository?
examples/context_walkthrough.rs (line 84)
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}
Source§

impl<T, C> EditSession<T, C>

Source

pub fn original(&self) -> &T

The validated value captured when editing began.

Examples found in repository?
examples/context_walkthrough.rs (line 38)
21fn observe(
22    action: &'static str,
23    live: u8,
24    context: Context,
25    draft: &EditSession<u8, Context>,
26) -> Row {
27    let check = match draft.check(&live, &context) {
28        Ok(()) => "Ready",
29        Err(FinishError::ContextChanged) => "ContextChanged",
30        Err(FinishError::ValueChanged) => "ValueChanged",
31        Err(FinishError::InvalidProposal) => "InvalidProposal",
32    };
33    Row {
34        action,
35        live,
36        revision: context.revision,
37        preview: *draft.preview(),
38        value_matches_original: live == *draft.original(),
39        check,
40    }
41}
42
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}
More examples
Hide additional examples
examples/retry_command.rs (line 99)
92    fn confirm(&mut self, host: &mut Host) -> Result<Outcome, ConfirmError> {
93        // This whole method runs on the host's serialized edit path, without an await
94        // or a callback that can change the target between check and command dispatch.
95        let draft = self.draft.as_ref().ok_or(ConfirmError::Closed)?;
96        draft
97            .check(&host.value, &host.context)
98            .map_err(ConfirmError::Draft)?;
99        let outcome = if draft.preview() == draft.original() {
100            Outcome::Unchanged // Do not dispatch a command or clear Redo for a no-op.
101        } else {
102            // Borrow the proposal: finish() would consume it before this fallible call.
103            host.apply(draft.preview())?;
104            Outcome::Applied
105        };
106        self.cancel(); // Close only after a successful command or a checked no-op.
107        Ok(outcome)
108    }
Source

pub fn preview(&self) -> &T

Last valid preview, even when a later incomplete/invalid input is present.

Examples found in repository?
examples/context_walkthrough.rs (line 37)
21fn observe(
22    action: &'static str,
23    live: u8,
24    context: Context,
25    draft: &EditSession<u8, Context>,
26) -> Row {
27    let check = match draft.check(&live, &context) {
28        Ok(()) => "Ready",
29        Err(FinishError::ContextChanged) => "ContextChanged",
30        Err(FinishError::ValueChanged) => "ValueChanged",
31        Err(FinishError::InvalidProposal) => "InvalidProposal",
32    };
33    Row {
34        action,
35        live,
36        revision: context.revision,
37        preview: *draft.preview(),
38        value_matches_original: live == *draft.original(),
39        check,
40    }
41}
42
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}
More examples
Hide additional examples
examples/retry_command.rs (line 99)
92    fn confirm(&mut self, host: &mut Host) -> Result<Outcome, ConfirmError> {
93        // This whole method runs on the host's serialized edit path, without an await
94        // or a callback that can change the target between check and command dispatch.
95        let draft = self.draft.as_ref().ok_or(ConfirmError::Closed)?;
96        draft
97            .check(&host.value, &host.context)
98            .map_err(ConfirmError::Draft)?;
99        let outcome = if draft.preview() == draft.original() {
100            Outcome::Unchanged // Do not dispatch a command or clear Redo for a no-op.
101        } else {
102            // Borrow the proposal: finish() would consume it before this fallible call.
103            host.apply(draft.preview())?;
104            Outcome::Applied
105        };
106        self.cancel(); // Close only after a successful command or a checked no-op.
107        Ok(outcome)
108    }
Source

pub fn context(&self) -> &C

The host context captured when editing began; never changed by this session.

Examples found in repository?
examples/context_walkthrough.rs (line 87)
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}
Source

pub fn is_valid(&self) -> bool

Whether the latest proposal was accepted. This does not check live host state.

Source

pub fn propose<E>(&mut self, proposal: Result<T, E>) -> Result<(), E>

Accept a host-validated proposal. Errors invalidate commit without replacing the last valid preview. Units, mixed values, finite checks and bounds belong to the host.

Examples found in repository?
examples/retry_command.rs (lines 85-89)
82    fn set_text(&mut self, text: &str) -> Result<(), &'static str> {
83        let draft = self.draft.as_mut().ok_or("dialog is closed")?;
84        self.text = text.to_owned();
85        draft.propose(if text.trim().is_empty() {
86            Err("enter a title")
87        } else {
88            Ok(self.text.clone())
89        })
90    }
More examples
Hide additional examples
examples/context_walkthrough.rs (line 60)
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}
Source

pub fn cancel(self)

Discard the local draft. No rollback commands or history entries are generated.

Examples found in repository?
examples/retry_command.rs (line 112)
110    fn cancel(&mut self) {
111        if let Some(draft) = self.draft.take() {
112            draft.cancel();
113        }
114    }
Source§

impl<T: PartialEq, C: PartialEq> EditSession<T, C>

Source

pub fn check_live(&self, live: &T, context: &C) -> Result<(), FinishError>

Check whether the draft still belongs to the current host state, independently of invalid input. Useful when deciding whether an open editor must be dismissed.

This never returns InvalidProposal and does not authorize a commit. Use check or finish immediately before applying on the host’s serialized command path. Context conflicts take precedence over value conflicts. No lock is acquired.

Source

pub fn check(&self, live: &T, context: &C) -> Result<(), FinishError>

Check live host state and proposal validity immediately before applying on the host’s serialized command path. Live conflicts take precedence over invalid input. This comparison is not a lock and is not a distributed transaction.

Examples found in repository?
examples/context_walkthrough.rs (line 27)
21fn observe(
22    action: &'static str,
23    live: u8,
24    context: Context,
25    draft: &EditSession<u8, Context>,
26) -> Row {
27    let check = match draft.check(&live, &context) {
28        Ok(()) => "Ready",
29        Err(FinishError::ContextChanged) => "ContextChanged",
30        Err(FinishError::ValueChanged) => "ValueChanged",
31        Err(FinishError::InvalidProposal) => "InvalidProposal",
32    };
33    Row {
34        action,
35        live,
36        revision: context.revision,
37        preview: *draft.preview(),
38        value_matches_original: live == *draft.original(),
39        check,
40    }
41}
42
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}
More examples
Hide additional examples
examples/retry_command.rs (line 97)
92    fn confirm(&mut self, host: &mut Host) -> Result<Outcome, ConfirmError> {
93        // This whole method runs on the host's serialized edit path, without an await
94        // or a callback that can change the target between check and command dispatch.
95        let draft = self.draft.as_ref().ok_or(ConfirmError::Closed)?;
96        draft
97            .check(&host.value, &host.context)
98            .map_err(ConfirmError::Draft)?;
99        let outcome = if draft.preview() == draft.original() {
100            Outcome::Unchanged // Do not dispatch a command or clear Redo for a no-op.
101        } else {
102            // Borrow the proposal: finish() would consume it before this fallible call.
103            host.apply(draft.preview())?;
104            Outcome::Applied
105        };
106        self.cancel(); // Close only after a successful command or a checked no-op.
107        Ok(outcome)
108    }
Source

pub fn finish( self, live: &T, context: &C, ) -> Result<Option<Change<T>>, FinishError>

Emit at most one change; a no-op emits none, preserving the host’s redo stack. The host still validates and applies the command and handles execution failure.

Examples found in repository?
examples/context_walkthrough.rs (line 89)
43fn main() {
44    let args: Vec<_> = std::env::args().skip(1).collect();
45    let json = match args.as_slice() {
46        [] => false,
47        [flag] if flag == "--json" => true,
48        _ => {
49            eprintln!("Usage: context_walkthrough [--json]");
50            std::process::exit(2);
51        }
52    };
53    let mut live = 100_u8;
54    let mut context = Context {
55        document: 7,
56        target: 2,
57        revision: 42,
58    };
59    let mut draft = EditSession::new(live, context);
60    assert_eq!(draft.propose::<&str>(Ok(75)), Ok(()));
61    let mut rows = vec![observe("Open draft", live, context, &draft)];
62    assert_eq!(draft.check(&live, &context), Ok(()));
63
64    // The host owns both the external edit and its monotonic revision.
65    live = 120;
66    context.revision += 1;
67    rows.push(observe("Host edits", live, context, &draft));
68    assert_eq!(
69        draft.check(&live, &context),
70        Err(FinishError::ContextChanged)
71    );
72
73    // Undo restores the VALUE, but must advance the host's revision again.
74    live = 100;
75    context.revision += 1;
76    rows.push(observe("Host undoes", live, context, &draft));
77    assert_eq!(live, *draft.original());
78    assert_eq!(
79        draft.check(&live, &context),
80        Err(FinishError::ContextChanged)
81    );
82
83    // Reset changes the preview, never the captured document/target/revision.
84    draft.reset();
85    rows.push(observe("Reset draft", live, context, &draft));
86    assert_eq!(*draft.preview(), live);
87    assert_eq!(draft.context().revision, 42);
88    assert_eq!(
89        draft.finish(&live, &context),
90        Err(FinishError::ContextChanged)
91    );
92
93    if json {
94        println!("{{\"rows\":[");
95        for (i, row) in rows.iter().enumerate() {
96            println!(
97                "{{\"action\":\"{}\",\"live\":{},\"revision\":{},\"preview\":{},\"value_matches_original\":{},\"check\":\"{}\"}}{}",
98                row.action,
99                row.live,
100                row.revision,
101                row.preview,
102                row.value_matches_original,
103                row.check,
104                if i + 1 == rows.len() { "" } else { "," }
105            );
106        }
107        println!("],\"finish\":\"ContextChanged\"}}");
108    } else {
109        println!("Action        | Live | Revision | Preview | Value equals original | check()");
110        for row in &rows {
111            println!(
112                "{:<13} | {:>4} | {:>8} | {:>7} | {:<21} | {}",
113                row.action,
114                row.live,
115                row.revision,
116                row.preview,
117                row.value_matches_original,
118                row.check
119            );
120        }
121        println!("finish(): ContextChanged. The host must not apply the old draft.");
122    }
123}

Trait Implementations§

Source§

impl<T: Debug, C: Debug> Debug for EditSession<T, C>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<T, C> Freeze for EditSession<T, C>
where T: Freeze, C: Freeze,

§

impl<T, C> RefUnwindSafe for EditSession<T, C>

§

impl<T, C> Send for EditSession<T, C>
where T: Send, C: Send,

§

impl<T, C> Sync for EditSession<T, C>
where T: Sync, C: Sync,

§

impl<T, C> Unpin for EditSession<T, C>
where T: Unpin, C: Unpin,

§

impl<T, C> UnsafeUnpin for EditSession<T, C>
where T: UnsafeUnpin, C: UnsafeUnpin,

§

impl<T, C> UnwindSafe for EditSession<T, C>
where T: UnwindSafe, C: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.