git.m455.casa

rodo

clone url: git://git.m455.casa/rodo


src/rodo.rkt

1 #lang racket/base
2
3 (require racket/file
4 racket/list
5 racket/match
6 racket/string)
7
8 ;; ------------------------------------------------
9 ;; Constants
10 ;; ------------------------------------------------
11 (define command-help-1 "help")
12 (define command-help-2 "--help")
13 (define command-help-3 "-h")
14 (define command-init "init")
15 (define command-ls "ls")
16 (define command-rm "rm")
17 (define command-add "add")
18 (define command-update "update")
19 (define program-name "rodo")
20 (define program-file (string-append "." program-name))
21 (define program-path (path->string (build-path (find-system-path 'home-dir) program-file)))
22 (define newline "\n")
23 (define double-newline "\n\n")
24
25 ;; ------------------------------------------------
26 ;; Messages
27 ;; ------------------------------------------------
28 (define messages
29 (hash 'error-too-many-arguments
30 (format (string-append "Error: Too many arguments." newline
31 "Try running '~a ~a' for more information.")
32 program-name
33 command-help-1)
34
35 'error-incorrect-usage
36 (format (string-append "Error: Incorrect usage." newline
37 "Try running '~a ~a' for more information.")
38 program-name
39 command-help-1)
40
41 'error-couldnt-find-file
42 (format (string-append "Error: Couldn't find ~a" newline
43 "If the file doesn't exist, try running ~a ~a")
44 program-path
45 program-name
46 command-init)
47
48 'error-something-exists
49 (format "Error: It looks like ~a already exists." program-path)
50
51 'error-file-doesnt-exist
52 (format (string-append "Error: '~a' doesn't exist." newline
53 "Try running '~a ~a'.")
54 program-path
55 program-name
56 command-init)
57
58 'error-item-not-found
59 "Error: Item not found."
60
61 'error-not-an-option
62 "Error: Not an option."
63
64 'error-not-a-number
65 "Error: Not a number."
66
67 'init-cancelled
68 (format "Cancelled the creation of '~a'." program-path)
69
70 'init-prompt
71 (format (string-append "The file '~a' will be created." newline
72 "Is this okay? [y/n]")
73 program-path)
74
75 'file-created
76 (format "Successfully created ~a." program-path)
77
78 'empty-list
79 "There is nothing in your list."
80
81 'updated-item
82 "Updated item number ~a."
83
84 'added
85 "Added '~a' to your list."
86
87 'removed
88 "Removed '~a' from your list."))
89
90 ;; ------------------------------------------------
91 ;; helpers
92 ;; ------------------------------------------------
93 (define (messages-ref key)
94 (hash-ref messages key))
95
96 (define (displayln-messages-ref key)
97 (displayln (messages-ref key)))
98
99 (define (displayln-format-messages-ref key value)
100 (displayln (format (messages-ref key) value)))
101
102 (define (create-file string)
103 (close-output-port (open-output-file string)))
104
105 ;; ------------------------------------------------
106 ;; init
107 ;; ------------------------------------------------
108 (define (init/cancel)
109 (displayln-messages-ref 'init-cancelled)
110 (exit))
111
112 (define (init/create-file)
113 (create-file program-path)
114 (displayln-messages-ref 'file-created))
115
116 (define (init/prompt)
117 (displayln-messages-ref 'init-prompt)
118 (display "> ")
119 (let ([input-symbol (string->symbol (read-line))])
120 (case input-symbol
121 ['y (init/create-file)]
122 ['n (init/cancel)]
123 [else (displayln-messages-ref 'error-not-an-option)])))
124
125 (define (init)
126 (if (or (file-exists? program-path)
127 (directory-exists? program-path))
128 (displayln-messages-ref 'error-something-exists)
129 (init/prompt)))
130
131 ;; ------------------------------------------------
132 ;; ls
133 ;; ------------------------------------------------
134 (define (ls/display-list listof-items)
135 (let* ([numbers (map number->string (range (length listof-items)))]
136 [numbered-items (map (lambda (a b) (string-append a ". " b))
137 numbers
138 listof-items)])
139 (for ([item numbered-items])
140 (displayln item))))
141
142 (define (ls)
143 (if (file-exists? program-path)
144 (let ([listof-items (file->lines program-path)])
145 (if (null? listof-items)
146 (displayln-messages-ref 'empty-list)
147 (ls/display-list listof-items)))
148 (displayln-messages-ref 'error-couldnt-find-file)))
149
150 ;; ------------------------------------------------
151 ;; rm
152 ;; ------------------------------------------------
153 (define (rm/remove-item item-number)
154 (let ([listof-items (file->lines program-path)])
155 (if (and (not (null? listof-items))
156 (exact? item-number)
157 (>= item-number 0)
158 (< item-number (length listof-items)))
159 (let* ([item-to-remove (list-ref listof-items item-number)]
160 [list-without-item (remove item-to-remove listof-items)])
161 (display-lines-to-file list-without-item program-path #:exists 'truncate)
162 (displayln-format-messages-ref 'removed item-to-remove))
163 (displayln-messages-ref 'error-item-not-found))))
164
165 (define (rm arg)
166 (if (file-exists? program-path)
167 (let ([item-number (string->number arg)])
168 (if item-number
169 (rm/remove-item item-number)
170 (displayln-format-messages-ref 'error-not-a-number arg)))
171 (displayln-messages-ref 'error-couldnt-find-file)))
172
173 ;; ------------------------------------------------
174 ;; add
175 ;; ------------------------------------------------
176 (define (add item)
177 (if (file-exists? program-path)
178 ;; The removing and adding of the '\n' is to
179 ;; ensure only one '\n' exists at the end of the
180 ;; item to be added.
181 (let* ([item-no-newline (string-replace item "\n" "")]
182 [item-newline (string-append item-no-newline "\n")])
183 (display-to-file item-newline program-path #:exists 'append)
184 (displayln-format-messages-ref 'added item-no-newline))
185 (displayln-messages-ref 'error-couldnt-find-file)))
186
187 (define (update/update-item n item-index text)
188 (let ([item-list (file->lines program-path)])
189 (if (and (not (null? item-list))
190 (>= item-index 0)
191 (exact? item-index)
192 (< item-index (length item-list)))
193 (begin (display-lines-to-file (list-set item-list item-index text)
194 program-path
195 #:exists 'truncate)
196 (displayln-format-messages-ref 'updated-item n))
197 (displayln-messages-ref 'error-item-not-found))))
198
199 (define (update n text)
200 (if (file-exists? program-path)
201 (let ([item-index (string->number n)])
202 (if item-index
203 (update/update-item n item-index text)
204 (displayln-messages-ref 'error-not-a-number)))
205 (displayln-messages-ref 'error-couldnt-find-file)))
206
207 ;; ------------------------------------------------
208 ;; help
209 ;; ------------------------------------------------
210 (define (help)
211 (displayln
212 (string-append
213 "Usage:" newline
214 (format " ~a [<command>] [<args>]" program-name)
215 double-newline
216
217 "Commands:" newline
218 (format " ~a - Creates a file in ~a, where your items will be stored."
219 command-init
220 program-path) newline
221 (format " ~a - Adds an item to your list." command-add) newline
222 (format " ~a - Prints a numbered list of the items you've added." command-ls) newline
223 (format " ~a - Removes an item from your list." command-rm) newline
224 (format " ~a - Replaces the contents of an item with new text." command-update)
225 double-newline
226
227 "Examples:" newline
228 (format " ~a" program-name) newline
229 (format " ~a ~a" program-name command-init) newline
230 (format " ~a ~a \"You are wonderful\"" program-name command-add) newline
231 (format " ~a ~a" program-name command-ls) newline
232 (format " ~a ~a 2" program-name command-rm) newline
233 (format " ~a ~a 2 \"This is new text!\"" program-name command-update))))
234
235 (define (process-args vectorof-args)
236 (match vectorof-args
237 [(vector (== command-update) n text) (update n text)]
238 [(vector (== command-add) a) (add a)]
239 [(vector (== command-rm) a) (rm a)]
240 [(vector (== command-ls)) (ls)]
241 [(vector (== command-init)) (init)]
242 [(or (vector (== command-help-1))
243 (vector (== command-help-2))
244 (vector (== command-help-3))) (help)]
245 [_ (displayln-messages-ref 'error-incorrect-usage)]))
246
247 (define (main vectorof-args)
248 (process-args vectorof-args))
249
250 (main (current-command-line-arguments))