diff options
Diffstat (limited to 'examples/cross_todo/nim_commandline/nimtodo.nim')
-rw-r--r-- | examples/cross_todo/nim_commandline/nimtodo.nim | 297 |
1 files changed, 0 insertions, 297 deletions
diff --git a/examples/cross_todo/nim_commandline/nimtodo.nim b/examples/cross_todo/nim_commandline/nimtodo.nim deleted file mode 100644 index be5b3407b..000000000 --- a/examples/cross_todo/nim_commandline/nimtodo.nim +++ /dev/null @@ -1,297 +0,0 @@ -# Implements a command line interface against the backend. - -import backend, db_sqlite, os, parseopt, parseutils, strutils, times - -const - USAGE = """nimtodo - Nim cross platform todo manager - -Usage: - nimtodo [command] [list options] - -Commands: - -a=int text Adds a todo entry with the specified priority and text. - -c=int Marks the specified todo entry as done. - -u=int Marks the specified todo entry as not done. - -d=int|all Deletes a single entry from the database, or all entries. - -g Generates some rows with values for testing. - -l Lists the contents of the database. - -h, --help shows this help - -List options (optional): - -p=+|- Sorts list by ascending|descending priority. Default:descending. - -m=+|- Sorts list by ascending|descending date. Default:descending. - -t Show checked entries. By default they are not shown. - -z Hide unchecked entries. By default they are shown. - -Examples: - nimtodo -a=4 Water the plants - nimtodo -c:87 - nimtodo -d:2 - nimtodo -d:all - nimtodo -l -p=+ -m=- -t - -""" - -type - Command = enum # The possible types of commands - cmdAdd # The user wants to add a new todo entry. - cmdCheck # User wants to check a todo entry. - cmdUncheck # User wants to uncheck a todo entry. - cmdDelete # User wants to delete a single todo entry. - cmdNuke # User wants to purge all database entries. - cmdGenerate # Add random rows to the database, for testing. - cmdList # User wants to list contents. - - ParamConfig = object - # Structure containing the parsed options from the commandline. - command: Command # Store the type of operation - addPriority: int # Only valid with cmdAdd, stores priority. - addText: seq[string] # Only valid with cmdAdd, stores todo text. - todoId: int64 # The todo id for operations like check or delete. - listParams: PagedParams # Uses the backend structure directly for params. - -proc initDefaults(params: var ParamConfig) = - ## Initialises defaults value in the structure. - ## - ## Most importantly we want to have an empty list for addText. - params.listParams.initDefaults - params.addText = @[] - -proc abort(message: string, value: int) = - # Simple wrapper to abort also displaying the help to the user. - stdout.write(USAGE) - quit(message, value) - -template parseTodoIdAndSetCommand(newCommand: Command): untyped = - ## Helper to parse a big todo identifier into todoId and set command. - try: - let numChars = val.parseBiggestInt(newId) - if numChars < 1: raise newException(ValueError, "Empty string?") - result.command = newCommand - result.todoId = newId - except OverflowError: - raise newException(ValueError, "Value $1 too big" % val) - -template verifySingleCommand(actions: typed): typed = - ## Helper to make sure only one command has been specified so far. - if specifiedCommand: - abort("Only one command can be specified at a time! (extra:$1)" % [key], 2) - else: - actions - specifiedCommand = true - -proc parsePlusMinus(val: string, debugText: string): bool = - ## Helper to process a plus or minus character from the commandline. - ## - ## Pass the string to parse and the type of parameter for debug errors. - ## The processed parameter will be returned as true for a '+' and false for a - ## '-'. The proc aborts with a debug message if the passed parameter doesn't - ## contain one of those values. - case val - of "+": - return true - of "-": - return false - else: - abort("$1 parameter should be + or - but was '$2'." % [debugText, val], 4) - -proc parseCmdLine(): ParamConfig = - ## Parses the commandline. - ## - ## Returns a ParamConfig structure filled with the proper values or directly - ## calls quit() with the appropriate error message. - var - specifiedCommand = false - usesListParams = false - p = initOptParser() - key, val: TaintedString - newId: BiggestInt - result.initDefaults - try: - while true: - next(p) - key = p.key - val = p.val - case p.kind - of cmdArgument: - if specifiedCommand and cmdAdd == result.command: - result.addText.add(key) - else: - abort("Argument ($1) detected without add command." % [key], 1) - of cmdLongOption, cmdShortOption: - case normalize(key) - of "help", "h": - stdout.write(USAGE) - quit(0) - of "a": - verifySingleCommand: - result.command = cmdAdd - result.addPriority = val.parseInt - of "c": - verifySingleCommand: - parseTodoIdAndSetCommand(cmdCheck) - of "u": - verifySingleCommand: - parseTodoIdAndSetCommand cmdUncheck - of "d": - verifySingleCommand: - if "all" == val: - result.command = cmdNuke - else: - parseTodoIdAndSetCommand cmdDelete - of "g": - verifySingleCommand: - if val.len > 0: - abort("Unexpected value '$1' for switch l." % [val], 3) - result.command = cmdGenerate - of "l": - verifySingleCommand: - if val.len > 0: - abort("Unexpected value '$1' for switch l." % [val], 3) - result.command = cmdList - of "p": - usesListParams = true - result.listParams.priorityAscending = parsePlusMinus(val, "Priority") - of "m": - usesListParams = true - result.listParams.dateAscending = parsePlusMinus(val, "Date") - of "t": - usesListParams = true - if val.len > 0: - abort("Unexpected value '$1' for switch t." % [val], 5) - result.listParams.showChecked = true - of "z": - usesListParams = true - if val.len > 0: - abort("Unexpected value '$1' for switch z." % [val], 5) - result.listParams.showUnchecked = false - else: - abort("Unexpected option '$1'." % [key], 6) - of cmdEnd: - break - except ValueError: - abort("Invalid integer value '$1' for parameter '$2'." % [val, key], 7) - if not specifiedCommand: - abort("Didn't specify any command.", 8) - if cmdAdd == result.command and result.addText.len < 1: - abort("Used the add command, but provided no text/description.", 9) - if usesListParams and cmdList != result.command: - abort("Used list options, but didn't specify the list command.", 10) - -proc generateDatabaseRows(conn: DbConn) = - ## Adds some rows to the database ignoring errors. - discard conn.addTodo(1, "Watch another random youtube video") - discard conn.addTodo(2, "Train some starcraft moves for the league") - discard conn.addTodo(3, "Spread the word about Nim") - discard conn.addTodo(4, "Give fruit superavit to neighbours") - var todo = conn.addTodo(4, "Send tax form through snail mail") - todo.isDone = true - discard todo.save(conn) - discard conn.addTodo(1, "Download new anime to watch") - todo = conn.addTodo(2, "Build train model from scraps") - todo.isDone = true - discard todo.save(conn) - discard conn.addTodo(5, "Buy latest Britney Spears album") - discard conn.addTodo(6, "Learn a functional programming language") - echo("Generated some entries, they were added to your database.") - -proc listDatabaseContents(conn: DbConn; listParams: PagedParams) = - ## Dumps the database contents formatted to the standard output. - ## - ## Pass the list/filter parameters parsed from the commandline. - var params = listParams - params.pageSize = -1 - let todos = conn.getPagedTodos(params) - if todos.len < 1: - echo("Database empty") - return - echo("Todo id, is done, priority, last modification date, text:") - # First detect how long should be our columns for formatting. - var cols: array[0..2, int] - for todo in todos: - cols[0] = max(cols[0], ($todo.getId).len) - cols[1] = max(cols[1], ($todo.priority).len) - cols[2] = max(cols[2], ($todo.getModificationDate).len) - # Now dump all the rows using the calculated alignment sizes. - for todo in todos: - echo("$1 $2 $3, $4, $5" % [ - ($todo.getId).align(cols[0]), - if todo.isDone: "[X]" else: "[-]", - ($todo.priority).align(cols[1]), - ($todo.getModificationDate).align(cols[2]), - todo.text]) - -proc deleteOneTodo(conn: DbConn; todoId: int64) = - ## Deletes a single todo entry from the database. - let numDeleted = conn.deleteTodo(todoId) - if numDeleted > 0: - echo("Deleted todo id " & $todoId) - else: - quit("Couldn't delete todo id " & $todoId, 11) - -proc deleteAllTodos(conn: DbConn) = - ## Deletes all the contents from the database. - ## - ## Note that it would be more optimal to issue a direct DELETE sql statement - ## on the database, but for the sake of the example we will restrict - ## ourselfves to the API exported by backend. - var - counter: int64 - params: PagedParams - params.initDefaults - params.pageSize = -1 - params.showUnchecked = true - params.showChecked = true - let todos = conn.getPagedTodos(params) - for todo in todos: - if conn.deleteTodo(todo.getId) > 0: - counter += 1 - else: - quit("Couldn't delete todo id " & $todo.getId, 12) - echo("Deleted $1 todo entries from database." % $counter) - -proc setTodoCheck(conn: DbConn; todoId: int64; value: bool) = - ## Changes the check state of a todo entry to the specified value. - let - newState = if value: "checked" else: "unchecked" - todo = conn.getTodo(todoId) - if todo == nil: - quit("Can't modify todo id $1, its not in the database." % $todoId, 13) - if todo[].isDone == value: - echo("Todo id $1 was already set to $2." % [$todoId, newState]) - return - todo[].isDone = value - if todo[].save(conn): - echo("Todo id $1 set to $2." % [$todoId, newState]) - else: - quit("Error updating todo id $1 to $2." % [$todoId, newState]) - -proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) = - ## Adds to the database a todo with the specified priority. - ## - ## The tokens are joined as a single string using the space character. The - ## created id will be displayed to the user. - let todo = conn.addTodo(priority, tokens.join(" ")) - echo("Created todo entry with id:$1 for priority $2 and text '$3'." % [ - $todo.getId, $todo.priority, todo.text]) - -when isMainModule: - ## Main entry point. - let - opt = parseCmdLine() - dbPath = getConfigDir() / "nimtodo.sqlite3" - if not dbPath.existsFile: - createDir(getConfigDir()) - echo("No database found at $1, it will be created for you." % dbPath) - let conn = openDatabase(dbPath) - try: - case opt.command - of cmdAdd: addTodo(conn, opt.addPriority, opt.addText) - of cmdCheck: setTodoCheck(conn, opt.todoId, true) - of cmdUncheck: setTodoCheck(conn, opt.todoId, false) - of cmdDelete: deleteOneTodo(conn, opt.todoId) - of cmdNuke: deleteAllTodos(conn) - of cmdGenerate: generateDatabaseRows(conn) - of cmdList: listDatabaseContents(conn, opt.listParams) - finally: - conn.close |