on master help sign in
TodoMVC: Use an RAII type to auto render and save to local storage

Instead of a `with<F>`-style function. It is a bit more idiomatic, and provides nicer usage, but takes a little more to write.

commit24743b93f0d6de503dd0afcca0acfdc6c1b3a57f authorNick Fitzgerald <fitzgen@gmail.com> dateTue 12 Feb 2019, 10:59 (8 years ago) parent13de5e6
1 file changed+83−67
examples/todomvc/src/controller.rs+83−67
examples/todomvc/src/controller.rs+83−67
@@ -6,6 +6,7 @@ use crate::todos::{Todos, TodosActions};
66use crate::visibility::Visibility;
77use dodrio::{RootRender, VdomWeak};
88use serde::{Deserialize, Serialize};
9+use std::ops::{Deref, DerefMut};
910
1011/// The controller for the TodoMVC app.
1112///
@@ -18,107 +19,122 @@ use serde::{Deserialize, Serialize};
1819#[derive(Default, Deserialize, Serialize)]
1920pub struct Controller;
2021
21−/// Invoke `f` with the root `Todos` component, save the (likely just modified)
22−/// todos to local storage, and schedule a new `dodrio` render.
23−fn with_todos<F>(root: &mut dyn RootRender, vdom: VdomWeak, f: F)
24−where
25− F: FnOnce(&mut Todos),
26−{
27− let todos = root.unwrap_mut::<Todos>();
28− f(todos);
29− todos.save_to_local_storage();
30− vdom.schedule_render();
31−}
32−
3322impl TodosActions for Controller {
3423 fn toggle_all(root: &mut dyn RootRender, vdom: VdomWeak) {
35− with_todos(root, vdom, |todos| {
36− let all_complete = todos.todos().iter().all(|t| t.is_complete());
37− for t in todos.todos_mut() {
38− t.set_complete(!all_complete);
39− }
40− });
24+ let mut todos = AutoCommitTodos::new(root, vdom);
25+ let all_complete = todos.todos().iter().all(|t| t.is_complete());
26+ for t in todos.todos_mut() {
27+ t.set_complete(!all_complete);
28+ }
4129 }
4230
4331 fn update_draft(root: &mut dyn RootRender, vdom: VdomWeak, draft: String) {
44− with_todos(root, vdom, |todos| {
45− todos.set_draft(draft);
46− });
32+ let mut todos = AutoCommitTodos::new(root, vdom);
33+ todos.set_draft(draft);
4734 }
4835
4936 fn finish_draft(root: &mut dyn RootRender, vdom: VdomWeak) {
50− with_todos(root, vdom, |todos| {
51− let title = todos.take_draft();
52− let title = title.trim();
53− if !title.is_empty() {
54− let id = todos.todos().len();
55− let new = Todo::new(id, title);
56− todos.add_todo(new);
57− }
58− });
37+ let mut todos = AutoCommitTodos::new(root, vdom);
38+ let title = todos.take_draft();
39+ let title = title.trim();
40+ if !title.is_empty() {
41+ let id = todos.todos().len();
42+ let new = Todo::new(id, title);
43+ todos.add_todo(new);
44+ }
5945 }
6046
6147 fn change_visibility(root: &mut dyn RootRender, vdom: VdomWeak, vis: Visibility) {
62− with_todos(root, vdom, |todos| {
63− todos.set_visibility(vis);
64− });
48+ let mut todos = AutoCommitTodos::new(root, vdom);
49+ todos.set_visibility(vis);
6550 }
6651
6752 fn delete_completed(root: &mut dyn RootRender, vdom: VdomWeak) {
68− with_todos(root, vdom, |todos| {
69− todos.delete_completed();
70− });
53+ let mut todos = AutoCommitTodos::new(root, vdom);
54+ todos.delete_completed();
7155 }
7256}
7357
7458impl TodoActions for Controller {
7559 fn toggle_completed(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
76− with_todos(root, vdom, |todos| {
77− let t = &mut todos.todos_mut()[id];
78− let completed = t.is_complete();
79− t.set_complete(!completed);
80− });
60+ let mut todos = AutoCommitTodos::new(root, vdom);
61+ let t = &mut todos.todos_mut()[id];
62+ let completed = t.is_complete();
63+ t.set_complete(!completed);
8164 }
8265
8366 fn delete(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
84− with_todos(root, vdom, |todos| {
85− todos.delete_todo(id);
86− });
67+ let mut todos = AutoCommitTodos::new(root, vdom);
68+ todos.delete_todo(id);
8769 }
8870
8971 fn begin_editing(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
90− with_todos(root, vdom, |todos| {
91− let t = &mut todos.todos_mut()[id];
92− let desc = t.title().to_string();
93− t.set_edits(Some(desc));
94− });
72+ let mut todos = AutoCommitTodos::new(root, vdom);
73+ let t = &mut todos.todos_mut()[id];
74+ let desc = t.title().to_string();
75+ t.set_edits(Some(desc));
9576 }
9677
9778 fn update_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize, edits: String) {
98− with_todos(root, vdom, |todos| {
99− let t = &mut todos.todos_mut()[id];
100− t.set_edits(Some(edits));
101− });
79+ let mut todos = AutoCommitTodos::new(root, vdom);
80+ let t = &mut todos.todos_mut()[id];
81+ t.set_edits(Some(edits));
10282 }
10383
10484 fn finish_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
105− with_todos(root, vdom, |todos| {
106− let t = &mut todos.todos_mut()[id];
107− if let Some(edits) = t.take_edits() {
108− let edits = edits.trim();
109− if edits.is_empty() {
110− todos.delete_todo(id);
111− } else {
112− t.set_title(edits);
113− }
85+ let mut todos = AutoCommitTodos::new(root, vdom);
86+ let t = &mut todos.todos_mut()[id];
87+ if let Some(edits) = t.take_edits() {
88+ let edits = edits.trim();
89+ if edits.is_empty() {
90+ todos.delete_todo(id);
91+ } else {
92+ t.set_title(edits);
11493 }
115− });
94+ }
11695 }
11796
11897 fn cancel_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
119− with_todos(root, vdom, |todos| {
120− let t = &mut todos.todos_mut()[id];
121− let _ = t.take_edits();
122− });
98+ let mut todos = AutoCommitTodos::new(root, vdom);
99+ let t = &mut todos.todos_mut()[id];
100+ let _ = t.take_edits();
101+ }
102+}
103+
104+/// An RAII type that dereferences to a `Todos` and once it is dropped, saves
105+/// the (presumably just modified) todos to local storage, and schedules a new
106+/// `dodrio` render.
107+pub struct AutoCommitTodos<'a> {
108+ todos: &'a mut Todos,
109+ vdom: VdomWeak,
110+}
111+
112+impl AutoCommitTodos<'_> {
113+ /// Construct a new `AutoCommitTodos` from the root rendering component and
114+ /// `vdom` handle.
115+ pub fn new(root: &mut dyn RootRender, vdom: VdomWeak) -> AutoCommitTodos {
116+ let todos = root.unwrap_mut::<Todos>();
117+ AutoCommitTodos { todos, vdom }
118+ }
119+}
120+
121+impl Drop for AutoCommitTodos<'_> {
122+ fn drop(&mut self) {
123+ self.todos.save_to_local_storage();
124+ self.vdom.schedule_render();
125+ }
126+}
127+
128+impl Deref for AutoCommitTodos<'_> {
129+ type Target = Todos;
130+
131+ fn deref(&self) -> &Todos {
132+ &self.todos
133+ }
134+}
135+
136+impl DerefMut for AutoCommitTodos<'_> {
137+ fn deref_mut(&mut self) -> &mut Todos {
138+ &mut self.todos
123139 }
124140}