timecalc/timecalc.lisp

124 lines
3.7 KiB
Common Lisp

;;;; timecalc.lisp
(defpackage #:timecalc
(:use #:cl #:with-user-abort)
(:import-from :timecalc-logic
:convert
:reload-models
:opts-ensure-directory)
(:import-from :timecalc-web
:start-server)
(:import-from :unix-opts
:get-opts
:define-opts))
(in-package #:timecalc)
;; defines all of our command line arguments
(define-opts
(:name :help
:description "prints this help"
:short #\h
:long "help")
(:name :web
:description "starts the web interface (defaults to 9001)"
:long "web")
(:name :port
:description "port for the web server to run"
:long "port"
:arg-parser #'parse-integer
:meta-var "PORT")
(:name :from
:description "specifies what planet's time you want to convert from"
:short #\f
:long "from"
:arg-parser #'identity
:meta-var "FROM")
(:name :to
:description "specifies what planet's time you want to convert to"
:short #\t
:long "to"
:arg-parser #'identity
:meta-var "TO")
(:name :version
:description "prints the version of the app"
:long "version")
(:name :load
:description "loads extra models from DIRECTORY"
:short #\l
:long "load"
:required t
:arg-parser #'opts-ensure-directory
:meta-var "DIRECTORY"))
(define-condition timecalc-signal ()
((message :initarg :message :reader message)
(args :initarg :args :reader signal-args
:initform nil)))
(defun main ()
"binary entry point"
;; ensures that if we only pass --version or --help
;; we don't worry about the required --load option
(handler-bind ((opts:missing-required-option
#'(lambda (x) (invoke-restart 'opts:skip-option))))
(multiple-value-bind (opts args) (get-opts)
;; if we get passed --help OR the binary is ran with NO arguments at all
(when (or (getf opts :help)
(not (uiop:command-line-arguments)))
(opts:describe :usage-of "timecalc"
:args "TIME"
:available-options-label "Options")
(uiop:quit 0))
(when (getf opts :version)
(format t "timecalc v~A~%" #.(asdf:component-version (asdf:find-system :timecalc)))
(uiop:quit 0))))
(handler-case
(with-user-abort
(multiple-value-bind (opts args) (get-opts)
(when (getf opts :load)
(unless (uiop:directory-exists-p (getf opts :load))
(signal 'timecalc-signal
:message "specified directory does not exist: ~A"
:args (list (getf opts :load))))
(reload-models (getf opts :load)))
(when (getf opts :web)
(let ((port (getf opts :port 9001)))
(start-server port)
(uiop:quit 0)))
(unless args
(signal 'timecalc-signal :message "please provide a timestring to convert"))
(let ((from (getf opts :from "gts"))
(to (getf opts :to)))
(format t "~A" (convert (first args) from to)))))
;; catches ctrl-c
(user-abort ()
(uiop:quit 0))
;; catches our custom condition for easier error printing
(timecalc-signal (s)
(apply #'format `(t ,(message s) ,@(signal-args s)))
(uiop:quit 2))
;; catches missing arguments for cmd line options
(opts:missing-arg (c)
(format t "~A~%" c)
(uiop:quit 1))
;; catches missing options that are required
(opts:missing-required-option (c)
(format t "~A~%" c)
(uiop:quit 1))
;; catches any and everything else :relieved:
(error (e)
(format t "encountered error:~%~A~%" e)
(uiop:quit 1))))