OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE.md file. | |
4 | |
5 #include <cstdio> | |
6 #include <cstring> | |
7 #include <vector> | |
8 #include <string> | |
9 | |
10 #include "todomvc_shared.h" // NOLINT(build/include) | |
11 #include "cc/struct.h" | |
12 #include "cc/todomvc_presenter.h" | |
13 #include "cc/todomvc_service.h" | |
14 | |
15 static int trim(char* str) { | |
16 if (!str) return 0; | |
17 int first = 0; | |
18 while (str[first] == ' ') ++first; | |
19 | |
20 int last = first; | |
21 while (str[last] != '\0') ++last; | |
22 | |
23 while (last-- > 0 && (str[last] == ' ' || str[last] == '\n')) {} | |
24 if (last <= 0) return 0; | |
25 | |
26 int size = 1 + last - first; | |
27 for (int i = 0; i < size; ++i) str[i] = str[first + i]; | |
28 str[size] = '\0'; | |
29 | |
30 return size; | |
31 } | |
32 | |
33 static bool is(char* str1, const char* str2) { | |
34 return strcmp(str1, str2) == 0; | |
35 } | |
36 | |
37 class TodoListView : public TodoMVCPresenter { | |
38 public: | |
39 class Item { | |
40 public: | |
41 Item(char* title, | |
42 bool done, | |
43 event delete_event, | |
44 event complete_event, | |
45 event uncomplete_event) | |
46 : title_(title), | |
47 done_(done), | |
48 delete_event_(delete_event), | |
49 complete_event_(complete_event), | |
50 uncomplete_event_(uncomplete_event) | |
51 {} | |
52 | |
53 ~Item() { | |
54 delete title_; | |
55 } | |
56 | |
57 char* title() const { return title_; } | |
58 void set_title(char* title) { | |
59 if (title_) delete title_; | |
60 title_ = title; | |
61 } | |
62 | |
63 bool done() const { return done_; } | |
64 | |
65 void deleteEvent() { | |
66 TodoMVCPresenter::dispatch(delete_event_); | |
67 } | |
68 | |
69 void completeEvent() { | |
70 TodoMVCPresenter::dispatch(complete_event_); | |
71 } | |
72 | |
73 void uncompleteEvent() { | |
74 TodoMVCPresenter::dispatch(uncomplete_event_); | |
75 } | |
76 | |
77 private: | |
78 char* title_; | |
79 bool done_; | |
80 event delete_event_; | |
81 event complete_event_; | |
82 event uncomplete_event_; | |
83 | |
84 friend class TodoListView; | |
85 }; | |
86 | |
87 ~TodoListView() { | |
88 for (size_t i = 0; i < todos.size(); ++i) { | |
89 delete todos[i]; | |
90 } | |
91 todos.clear(); | |
92 } | |
93 | |
94 void help() { | |
95 printf( | |
96 "Commands: list, new, del, done, undone, toggle, clear, quit, help\n"); | |
97 } | |
98 | |
99 void list() { | |
100 if (!todos.size()) { | |
101 printf("You're all done!\n"); | |
102 return; | |
103 } | |
104 | |
105 for (unsigned i = 0; i < todos.size(); ++i) { | |
106 Item* item = todos[i]; | |
107 printf(" %d. [%s]: %s\n", | |
108 i, | |
109 item->done() ? "done" : "todo", | |
110 item->title()); | |
111 } | |
112 } | |
113 | |
114 void create() { | |
115 static int max_str = 256; | |
116 char buffer[max_str]; // NOLINT(runtime/arrays) | |
117 int length = trim(fgets(buffer, max_str, stdin)); | |
118 if (!length) { | |
119 printf("Please specify a todo text\n"); | |
120 return; | |
121 } | |
122 createItem(buffer); | |
123 } | |
124 | |
125 void destroy() { | |
126 if (Item* item = readItem()) { | |
127 item->deleteEvent(); | |
128 } | |
129 } | |
130 | |
131 void done() { | |
132 if (Item* item = readItem()) { | |
133 item->completeEvent(); | |
134 } | |
135 } | |
136 | |
137 void undone() { | |
138 if (Item* item = readItem()) { | |
139 item->uncompleteEvent(); | |
140 } | |
141 } | |
142 | |
143 void toggle() { | |
144 if (Item* item = readItem()) { | |
145 if (item->done()) { | |
146 item->uncompleteEvent(); | |
147 } else { | |
148 item->completeEvent(); | |
149 } | |
150 } | |
151 } | |
152 | |
153 void clear() { | |
154 clearItems(); | |
155 } | |
156 | |
157 private: | |
158 int readId() { | |
159 int id = 0; | |
160 int match = fscanf(stdin, "%d", &id); | |
161 return (match == 1) ? id : -1; | |
162 } | |
163 | |
164 Item* readItem() { | |
165 int id = readId(); | |
166 if (id < 0 || static_cast<unsigned>(id) >= todos.size()) { | |
167 printf("Invalid todo item index %d\n", id); | |
168 return 0; | |
169 } | |
170 return todos[id]; | |
171 } | |
172 | |
173 // State when applying a patch. We assume a right-hanging encoding of a list. | |
174 enum Context { | |
175 IN_LIST, | |
176 IN_ITEM, | |
177 IN_TITLE, | |
178 IN_DONE, | |
179 IN_DELETE_EVENT, | |
180 IN_COMPLETE_EVENT, | |
181 IN_UNCOMPLETE_EVENT | |
182 }; | |
183 | |
184 Context context; | |
185 int index; | |
186 | |
187 void enterPatch() { | |
188 context = IN_LIST; | |
189 index = 0; | |
190 } | |
191 | |
192 void enterConsFst() { | |
193 context = (context == IN_ITEM) ? IN_TITLE : IN_ITEM; | |
194 } | |
195 | |
196 void enterConsSnd() { | |
197 if (context == IN_ITEM) { | |
198 context = IN_DONE; | |
199 } else { | |
200 index++; | |
201 } | |
202 } | |
203 | |
204 void enterConsDeleteEvent() { | |
205 if (context != IN_ITEM) abort(); | |
206 context = IN_DELETE_EVENT; | |
207 } | |
208 | |
209 void enterConsCompleteEvent() { | |
210 if (context != IN_ITEM) abort(); | |
211 context = IN_COMPLETE_EVENT; | |
212 } | |
213 | |
214 void enterConsUncompleteEvent() { | |
215 if (context != IN_ITEM) abort(); | |
216 context = IN_UNCOMPLETE_EVENT; | |
217 } | |
218 | |
219 void updateNode(const Node& node) { | |
220 switch (context) { | |
221 case IN_TITLE: | |
222 todos[index]->set_title(node.getStr()); | |
223 break; | |
224 case IN_DONE: | |
225 todos[index]->done_ = node.getTruth(); | |
226 break; | |
227 case IN_DELETE_EVENT: | |
228 todos[index]->delete_event_ = node.getNum(); | |
229 break; | |
230 case IN_COMPLETE_EVENT: | |
231 todos[index]->complete_event_ = node.getNum(); | |
232 break; | |
233 case IN_UNCOMPLETE_EVENT: | |
234 todos[index]->uncomplete_event_ = node.getNum(); | |
235 break; | |
236 case IN_ITEM: | |
237 delete todos[index]; | |
238 todos[index] = newItem(node); | |
239 break; | |
240 case IN_LIST: | |
241 for (size_t i = index; i < todos.size(); ++i) { | |
242 delete todos[i]; | |
243 } | |
244 todos.resize(index); | |
245 addItems(node); | |
246 break; | |
247 default: | |
248 abort(); | |
249 } | |
250 } | |
251 | |
252 void addItems(const Node& content) { | |
253 if (content.isNil()) return; | |
254 Cons cons = content.getCons(); | |
255 addItem(cons.getFst()); | |
256 addItems(cons.getSnd()); | |
257 } | |
258 | |
259 void addItem(const Node& content) { | |
260 todos.push_back(newItem(content)); | |
261 } | |
262 | |
263 Item* newItem(const Node& content) { | |
264 Cons cons = content.getCons(); | |
265 char* title = cons.getFst().getStr(); | |
266 bool done = cons.getSnd().getTruth(); | |
267 return new Item( | |
268 title, | |
269 done, | |
270 cons.getDeleteEvent(), | |
271 cons.getCompleteEvent(), | |
272 cons.getUncompleteEvent()); | |
273 } | |
274 | |
275 std::vector<Item*> todos; | |
276 }; | |
277 | |
278 static void InteractWithService() { | |
279 TodoMVCService::setup(); | |
280 bool running = true; | |
281 TodoListView view; | |
282 while (running) { | |
283 printf("todo> "); | |
284 char buffer[256]; | |
285 int parsed_command = scanf("%255s", buffer); | |
286 if (parsed_command != 1 || is(buffer, "quit")) { | |
287 running = false; | |
288 break; | |
289 } | |
290 | |
291 if (strcmp(buffer, "list") == 0) { | |
292 view.sync(); | |
293 view.list(); | |
294 } else if (strcmp(buffer, "new") == 0) { | |
295 view.create(); | |
296 } else if (strcmp(buffer, "del") == 0) { | |
297 view.destroy(); | |
298 } else if (strcmp(buffer, "done") == 0) { | |
299 view.done(); | |
300 } else if (strcmp(buffer, "undone") == 0) { | |
301 view.undone(); | |
302 } else if (strcmp(buffer, "toggle") == 0) { | |
303 view.toggle(); | |
304 } else if (strcmp(buffer, "clear") == 0) { | |
305 view.clear(); | |
306 } else if (strcmp(buffer, "help") == 0) { | |
307 view.help(); | |
308 } else { | |
309 printf("Invalid command %s\n", buffer); | |
310 view.help(); | |
311 } | |
312 } | |
313 | |
314 printf("Exiting\n"); | |
315 TodoMVCService::tearDown(); | |
316 } | |
317 | |
318 int main(int argc, char** argv) { | |
319 if (argc < 2) { | |
320 printf("Usage: %s <snapshot>\n", argv[0]); | |
321 return 1; | |
322 } | |
323 SetupTodoMVC(argc, argv); | |
324 InteractWithService(); | |
325 TearDownTodoMVC(); | |
326 return 0; | |
327 } | |
OLD | NEW |