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>
impl<T: Clone, C> EditSession<T, C>
Sourcepub fn new(original: T, context: C) -> Self
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?
More examples
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}Sourcepub fn reset(&mut self)
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?
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>
impl<T, C> EditSession<T, C>
Sourcepub fn original(&self) -> &T
pub fn original(&self) -> &T
The validated value captured when editing began.
Examples found in repository?
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
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 }Sourcepub fn preview(&self) -> &T
pub fn preview(&self) -> &T
Last valid preview, even when a later incomplete/invalid input is present.
Examples found in repository?
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
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 }Sourcepub fn context(&self) -> &C
pub fn context(&self) -> &C
The host context captured when editing began; never changed by this session.
Examples found in repository?
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}Sourcepub fn is_valid(&self) -> bool
pub fn is_valid(&self) -> bool
Whether the latest proposal was accepted. This does not check live host state.
Sourcepub fn propose<E>(&mut self, proposal: Result<T, E>) -> Result<(), E>
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?
More examples
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: PartialEq, C: PartialEq> EditSession<T, C>
impl<T: PartialEq, C: PartialEq> EditSession<T, C>
Sourcepub fn check_live(&self, live: &T, context: &C) -> Result<(), FinishError>
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.
Sourcepub fn check(&self, live: &T, context: &C) -> Result<(), FinishError>
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?
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
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 }Sourcepub fn finish(
self,
live: &T,
context: &C,
) -> Result<Option<Change<T>>, FinishError>
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?
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}