aboutsummaryrefslogtreecommitdiff
path: root/build-app.lisp
blob: 155f8e9b660814ae70069851f1e4dcd54e6977a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
;;; build-app.lisp -- Contains the command line frontend definition
;;; to build the cli, simply load this file from the command line.

(asdf:load-system "oneliners.cli")

(defpackage #:oneliners.cli.app
  (:use #:cl #:net.didierverna.clon)
  (:local-nicknames (#:a #:alexandria)
                    (#:cli #:oneliners.cli)))

(in-package :oneliners.cli.app)

;;; HELP TEXTS
(defparameter +invite-help-text+
  " 
New contributor accounts are added to the your oneliners server by
redeeming invite tokens.

When the --redeem option is passed, the ARGS section is expected to be
three items long, and is interpreted as TOKEN USERNAME PASSWORD. E.g.:

    ol --redeem PHONEYTOKEN c00lhacker my1337pw

Would attempt to make a new user named c00lhacker with password
my1337pw.")

;;; CLON SYNOPSIS DEFINITION 

(defsynopsis (:postfix "[TERMS ...] | N [ARGS ...]")
  (group (:header "Search")
         (text :contents "Usage: ol [OPTIONS] [TERMS ...]")
         (text :contents "Each term may be a command name or some tag like 'server' or 'tunnel'")
         (lispobj :long-name "limit" 
                  :argument-type :optional
                  :argument-name "NUMBER"
                  :default-value 10
                  :description "The maximum number of results to return."
                  :typespec 'integer)
         (flag :long-name "not-flagged"
               :description "Request that no flagged oneliners are returned."))
  (text :contents " ")
  (group (:header "Running Oneliners")
         (text :contents "Usage: ol [OPTIONS] N [ARGS ...]")
         (text :contents "Runs the Nth search result with possible arguments ARGS.")
         (flag :long-name "clip"
               :description "Force an attempt to copy the oneliner to the clipboard instead of running it."))
  (text :contents " ")
  (group (:header "Help")
         (flag :long-name "help" 
               :description "Print this help menu.")
         (enum :long-name "help-topic"
               :enum '(:contributor :adding :invites)
               :argument-name "TOPIC"
               :description "Print help for a topic. Topics are:  adding, contributor, invites"))
  (group (:header "Adding" :hidden t)
         (flag :long-name "add"
               :description  "Intaractively add a oneliner and update the wiki.")
         (flag :long-name "update"
               :description "Interactively edit a oneliner and update the wiki."))
  (group (:header "Contributor" :hidden t)
         (flag :long-name "login"
               :description "Attempt to login to your contributor account. ARGS are interpreted as USERNAME PASSWORD. Success will return ab API token, writing it automatically into your config file.")
         (flag :long-name "revoke"
               :description "Revoke your own access token."))
  (group (:header "Invites" :hidden t)
         (flag :long-name "invite"
               :description "Request an invite token to send to a friend.")
         (flag :long-name "redeem"
               :description "Redeem an invite token.")
         (text :contents +invite-help-text+)))


;;; HELPERS

(defun find-group-with-header (header)
  "This function should be built in. Is it? How to know? The
documentation is both extensive and trash.  Any manual that expects
you to go to sleep with it at night is written for the author more
than the users."
  (loop for item in (net.didierverna.clon::items *synopsis*)
        when (and (typep item 'net.didierverna.clon::group)
                  (string-equal header (net.didierverna.clon::header item)))
          return item))


;;; MAIN ENTRY POINT

(defun main ()
  "Entry point for our standalone application."
  (make-context)
  (when (getopt :long-name "help")
    (help )
    (uiop:quit))
  (a:when-let (topic (getopt :long-name "help-topic"))
    (help :item (find-group-with-header (symbol-name topic)))
    (uiop:quit))
  (handler-case
      (let ((arguments (remainder)))
        (cond

          ((getopt :long-name "redeem")
           (destructuring-bind (token name pass) arguments
             (cli::redeem-invite token name pass)))

          ((getopt :long-name "login")
           (destructuring-bind (user pass) arguments
             (cli::login user pass)))

          ((getopt :long-name "invite")
           (cli::request-invite-code))

          ((getopt :long-name "add")
           (cli::add-new))

          (arguments
           ;; when the first argument is a number, try run a oneliner 
           (a:when-let (hist-number (parse-integer (first arguments) :junk-allowed t))
             (if (getopt :long-name "flag")
                 (format t "TBD: going to flag a command~%") ;(cli::flag-item hist-number) 
                 (format t "TBD: Going to run command ~a with arguments ~a~%"
                         hist-number (rest arguments)))
             (uiop:quit))   
           ;; otherwise search for oneliners
           (cli::search-for-oneliners arguments
                                      (getopt :long-name "limit")
                                      (getopt :long-name "not-flagged")))
          (t
           (help)))
        (uiop:quit))
    (error (e)
      (format *error-output* "ERROR: ~a~%" e)
      (help)
      (uiop:quit))))

;;; DUMP EXECUTABLE

(dump "ol" main)