-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmain.rkt
206 lines (173 loc) · 7.61 KB
/
main.rkt
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#lang racket/base
;; Identikon - parses username into a sha1-based identifier list and
;; interfaces with rule-sets to create identicon image
(require racket/date
racket/list
racket/runtime-path
racket/string
racket/contract
openssl/sha1
2htdp/image
sugar
identikon/utils
identikon/transforms)
(provide (contract-out [identikon (->* (exact-positive-integer?
exact-positive-integer?
any/c)
((or/c string? symbol?)
#:filename boolean?)
image?)]
[save-identikon (->* (string?
(or/c symbol? string?)
image?)
(#:quality number?)
(or/c void? exn:fail?))]
[identikon->string (->* ((or/c symbol? string?)
image?)
(#:quality number?)
string?)]))
;; Identifier we overwrite dynamically with module functions
(define draw-rules null)
(define-namespace-anchor a)
(define-runtime-path RULES-DIR "rules")
;; Dynamically load in a rules file
(define (load-plug-in file)
(let ([ns (make-base-empty-namespace)]
[filename (build-path RULES-DIR file)])
(namespace-attach-module (namespace-anchor->empty-namespace a)
'2htdp/image
ns)
(parameterize ([current-namespace ns])
(dynamic-require filename 'draw-rules))))
;; Create a filename and check if the file already exists, if so
;; append a timestamp
(define (make-filename name size extension)
(let* ([ext (format ".~a" (->string extension))]
[sizename (format "~a_~a" (->string name) (number->string size))]
[filename (string-join (list sizename ext) "")])
(if (file-exists? filename)
(string-join
(list sizename "_"
(number->string (date->seconds (current-date))) ext) "")
filename)))
;; Save the file based on type - png, jpeg or svg
(define (save-identikon filename type image #:quality [quality 75])
(let* ([ext (->string type)]
[path (make-filename filename (image-width image) ext)])
(cond
[(string=? "svg" ext) (save-svg image path)]
[(string=? "png" ext) (save-bitmap image path)]
[(string=? "jpeg" ext) (save-bitmap image path 'jpeg #:quality quality)]
[else (error 'save-identicon
"failed because could not not save file type of ~a" type)])))
;; Output the image as a string representation
;; (svg as xml, png/jpeg as base64 bytes)
(define (identikon->string type image #:quality [quality 75])
(let* ([ext (->string type)])
(cond
[(string=? "svg" ext) (image->svg-string image)]
[(or (string=? "png" ext) (string=? "jpeg" ext))
(image->bitmap-string image type quality)]
[else (error 'identikon->string "~a is not a valid image type" ext)])))
;; Convert a symbol or string into a rules filename
(define (create-rules-filename rules)
(let ([root (if (string? rules)
rules
(->string rules))])
(format "~a.rkt" rules)))
#|
Identikon - build an identicon of a specific size based on username and
using a rule-set. Will automatically drop the identicon in the repl unless
you tell it to save
ex: (identikon 300 300 "dfsdf")
(identikon 300 300 'dfsdf 'qbert)
|#
(define (identikon width height input
[rules "default"] #:filename [filename #f])
(let* ([processed-input (if filename
(file->numberlist input)
(string->numberlist input))]
[rule-file (create-rules-filename rules)])
;; Load rules file if provided
(set! draw-rules (load-plug-in rule-file))
;; Create identicon
(define rendered (draw-rules width height processed-input))
;; Return identikon (image)
rendered))
(module+ test
(require rackunit
sugar
2htdp/image)
(test-case
"create-rules-filename will append .rkt to anything that can be stringed"
(check-regexp-match ".rkt" (create-rules-filename "rza"))
(check-regexp-match ".rkt" (create-rules-filename 'wutang))
(check-regexp-match ".rkt" (create-rules-filename 187)))
(test-case
"identikon returns an image"
(check-pred image? (identikon 100 100 'rza)))
(test-case
"identikon->string returns a string"
(check-pred string? (identikon->string 'jpeg (identikon 100 100 'rza)))
(check-pred string? (identikon->string 'png (identikon 100 100 'rza)))
(check-pred string? (identikon->string 'svg (identikon 100 100 'rza))))
(test-case
"save-identikon returns correct values"
(check-pred
void?
(save-identikon "/tmp/koji" 'svg
(identikon 10 10 "koji")))
(check-exn
exn:fail?
(λ ()
(save-identikon "/tmp/koji" 'zzz
(identikon 10 10 "koji"))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Command line handling for Identikon
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ main
(require racket/cmdline
racket/list)
(define size-flags (make-parameter null))
(define rules-set (make-parameter '("default")))
(define input-str (make-parameter null))
(define file-name (make-parameter null))
(define ext (make-parameter "png"))
(define make-identikon
(command-line
#:program "identikon"
#:once-each
[("-i" "--input-str") in
"String input-str to convert to identikon"
(input-str in)]
[("-f" "--file") fl
"File or input stream used to generate identikon"
(file-name fl)]
[("-t" "--type") ty
"File type: png or svg"
(ext ty)]
[("-r" "--rules") rs
"Use specific rules"
(rules-set (cons rs (rules-set)))]
#:multi
[("-s" "--size") sz
"Add a square size(s) to generate. You can create multiple sizes."
(size-flags (cons sz (size-flags)))]))
(cond
[(and (empty? (size-flags))
(empty? (input-str))) (printf "No information provided ~n")]
[(empty? (size-flags)) (printf "No sizes were provided, -s ~n")]
[(empty? (input-str)) (printf "No input provided to process, -i ~n")]
[(not (empty? (file-name))) (for ([s (size-flags)])
(save-identikon (file-name) (ext) (identikon (string->number s)
(string->number s)
(file-name)
(first (rules-set))
#:filename #t))
(printf "Saved ~apx identicon for ~a ~n" s (file-name)))]
[else (for ([s (size-flags)])
(save-identikon (input-str) (ext) (identikon (string->number s)
(string->number s)
(input-str)
(first (rules-set))))
(printf "Saved ~apx identicon for ~a ~n" s (input-str)))]))