commit 185eef4fe522c561b621bda48d89c0816c603ffe
Author: Simon Watson <spesk@pm.me>
Date: Wed Dec 21 08:22:11 2022 -0500
Added basic CLI interface.
diff --git a/README.md b/README.md
index 7225c44..3b5411f 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,9 @@ in this version.
- Generic expense handling
- Encryption/decryption of old records
- Upload/download support like perl version
+ - Basic interactive mode, basic CLI mode
- TODO
- - Non interactive CLI Interface
- Clean up messy/inefficient stuff
- More robust error reporting
+ - Confirmation when changing encryption key
diff --git a/fin-lisp.lisp b/fin-lisp.lisp
index 689e2ff..897c861 100644
--- a/fin-lisp.lisp
+++ b/fin-lisp.lisp
@@ -25,7 +25,7 @@
;;; Used for pretty printing output
(defconstant +CELL-FORMATS+ '(:left "~vA"
:center "~v:@<~A~>"
- :right "~v@A"))
+ :right "~v@A"))
(defun format-table (stream data &key (column-label (loop for i from 1 to (length (car data))
collect (format nil "COL~D" i)))
@@ -74,11 +74,29 @@
;;; the user provided a valid key
(defun check-month (month-key)
(if (stringp month-key) month-key
- (return-from check-month NIL))
+ (return-from check-month NIL))
(if (ppcre:scan *old-month-line-regex* month-key) month-key
(if (ppcre:scan *new-month-line-regex* month-key) month-key
(return-from check-month NIL))))
+;; Called like: (add-month '202107)
+(defun add-month (month-key)
+ (if (check-month month-key)
+ (if (not (gethash month-key *records*))
+ (progn
+ (setf (gethash month-key *records*)
+ (make-hash-table :test 'equalp))
+ month-key))))
+
+(defun add-expense-to-month (expense value month)
+ (if (gethash month *records*)
+ (setf (gethash expense (gethash month *records*)) value)
+ (progn
+ (print (concatenate 'string "Adding" month))
+ (if (add-month month)
+ (setf (gethash expense (gethash month *records*)) value)
+ (print (concatenate 'string "Failed to add" month))))))
+
(opts:define-opts
(:name :help
:description "Print help text"
@@ -89,6 +107,21 @@
:short #\p
:long "print-month"
:arg-parser #'check-month)
+ (:name :add-expense
+ :description "Non interactive interface for recording an expense. Expects expense name as an argument, and requires -v|--value and -m|month"
+ :short #\e
+ :long "add-expense"
+ :arg-parser #'identity)
+ (:name :value
+ :description "Used with -e|--add-expense. Must be an integer."
+ :short #\v
+ :long "value"
+ :arg-parser #'parse-integer)
+ (:name :month
+ :description "Used with -e|--add-expense. Must be a valid month key."
+ :short #\m
+ :long "month"
+ :arg-parser #'check-month)
(:name :interactive-mode
:description "Run in interactive mode"
:short #\i
@@ -161,12 +194,6 @@
"Get records from remote server"
(setf *records* (yason:parse (decrypt-records key (gethash "content" (download-records))))))
-;; Called like: (add-month '202107)
-(defun add-month (month-key)
- (if (check-month month-key)
- (setf (gethash month-key *records*) (make-hash-table :test 'equalp))
- (return-from add-month NIL)))
-
;;; Taken from practical common lisp
(defun prompt-read (prompt)
(format *query-io* "~a: " prompt)
@@ -179,13 +206,6 @@
(parse-integer
(prompt-read "Enter expense value"))))
-(defun add-expense-to-month (month)
- (if (gethash month *records*)
- (let ((innerhash (gethash month *records*))
- (exp-l (prompt-for-expense)))
- (setf (gethash (first exp-l) innerhash) (second exp-l)))
- (add-expense-to-month (add-month month))))
-
;;; Given key for *records* hash,
;;; print expenses/values for month
(defun dump-month (month-key)
@@ -237,7 +257,11 @@
((answer (prompt-read "Select an option")))
(if (string= answer "1")
(generic-handler
- (add-expense-to-month (prompt-read "Enter month"))
+ (let ((month-input (prompt-read "Enter month"))
+ (expense-input (prompt-for-expense)))
+ (add-expense-to-month (first expense-input)
+ (second expense-input)
+ month-input))
"Invalid Input"))
(if (string= answer "2")
(generic-handler
@@ -256,7 +280,6 @@
(interactive-mode))
(defun display-help ()
- (format t "foo ~%")
(opts:describe
:prefix "fin-lisp.lisp - Basic expense tracker in lisp"
:usage-of "fin-lisp.lisp"
@@ -267,15 +290,29 @@
(defun main ()
(if (= 1 (length sb-ext:*posix-argv*)) (interactive-mode))
(let ((matches (opts:get-opts)))
- (format t "~a ~%" matches)
+ ;;(format t "~a ~%" matches)
(when-option (matches :help)
(display-help))
(when-option (matches :print-month)
- (let ((key (prompt-read "Enter decryption key: "))
+ (let ((key (prompt-read "Enter decryption key"))
(month-key (destructuring-bind (&key print-month) matches
print-month)))
(get-records key)
- (dump-month-table month-key)))
+ (dump-month-table month-key)
+ (quit)))
+ (when-option (matches :add-expense) ;; This is probably the wrong way to resolve the arguments
+ (when-option (matches :value)
+ (when-option (matches :month)
+ (let ((key (prompt-read "Enter decryption key"))
+ (name-value-month (destructuring-bind (&key add-expense value month) matches
+ (list add-expense value month))))
+ (get-records key)
+ (if (add-expense-to-month
+ (first name-value-month)
+ (second name-value-month)
+ (third name-value-month))
+ (push-records key)
+ (print "Invalid month input"))))))
(when-option (matches :interactive-mode)
(progn
(interactive-mode)