summaryrefslogtreecommitdiff
path: root/tempest.lisp
blob: f5caabcfc23bb2ecbe40bb0aa536205d48d1791b (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
(in-package #:tempest)

(defun read-tempest-string (filename)
  "Read file content as string. If it's not a .lisp file and does not have tempest comment at top, return NIL."
  (let ((content (uiop:read-file-string filename)))
    (if (str:starts-with-p ";;;; TEMPEST" content)
      content NIL)))

(defun compile-to-html (tempest-string)
  "Compiles spinneret string to HTML string. Redirects to standard output."
  (let ((*read-eval* nil))
        (-> (concatenate 'string "(spinneret:with-html " tempest-string ")")
             read-from-string 
             eval)))

(defun build-template (filename)
  (let ((raw (read-tempest-string filename)))
    (if raw 
      (let ((target-file (str:replace-all ".lisp" ".html" filename)))
        (with-open-file (*standard-output* target-file
                        :direction :output
                        :if-exists :supersede
                        :if-does-not-exist :create)
          (compile-to-html raw)
          target-file))
      NIL)))

(defun build-template-and-print (name)
  (let ((built (build-template name)))
    (if built 
      (format t "built ~a~%" built)
      (format t "ignore ~a, not a valid tempest file.~%" name))))

(defun build (dir)
  (cl-fad:walk-directory dir
    (lambda (path-name)
      (let ((name (namestring path-name)))
        (when (str:ends-with-p ".lisp" name :ignore-case T)
          (build-template-and-print name))))))

;; TODO handle frequent/simultaneous events, only build once
;; TODO handle :MOVE event, delete old one and create new one 
;; TODO handle :DELETE event, delete template as well
(defun watch (dir)
  (file-notify:watch dir)
  (file-notify:with-events (file change :timeout T)
    (when (and (str:ends-with-p ".lisp" file :ignore-case T)
               (or (eql change :MODIFY)
                   (eql change :CREATE))) 
      (let ((built-file (build-template file)))
        (if built-file
          (format t "Built ~a, because of ~a~%" built-file change)
          nil)))))

(defun run (subcommand &rest args)
  (cond ((equal subcommand "build")
         (mapcar 'build (car args)))
        ((equal subcommand "build-each")
         (mapcar 'build-template-and-print (car args)))
        ;; TODO support watching multiple dirs
        ((equal subcommand "watch")
         (watch (car (car args))))))

(defun main ()
  (let ((cli-args (cdr sb-ext:*posix-argv*)))
    (run (car cli-args) (cdr cli-args))))