| 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 |