convert-content.scm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. (use-modules (srfi srfi-1)
  2. (ice-9 ftw)
  3. (ice-9 match)
  4. (ice-9 popen)
  5. (ice-9 rdelim)
  6. (ice-9 textual-ports)
  7. (htmlprag))
  8. ;;; general stuff
  9. ;;; =============
  10. (define (get-filenames)
  11. (map car (cddr (file-system-tree "content"))))
  12. (define (filename-extension fname)
  13. (last (string-split fname #\.)))
  14. (define (write-metadata metadata op)
  15. (for-each (match-lambda
  16. ;; fix up my name where need be
  17. (('author . val)
  18. (display "author: Christine Lemmer-Webber\n" op))
  19. ((key . val)
  20. (format op "~a: ~a\n" key val)))
  21. metadata)
  22. (display "---\n" op))
  23. (define (maybe-append-slug metadata default-slug)
  24. (if (assoc 'slug metadata)
  25. metadata
  26. (append metadata `((slug . ,default-slug)))))
  27. (define (get-stripped-line ip)
  28. (string-trim-both (get-line ip) char-set:whitespace))
  29. ;;; rst stuff
  30. ;;; =========
  31. (define (make-convert-rst output-format)
  32. (define (convert-rst default-slug ip op)
  33. (define metadata
  34. (maybe-append-slug (read-rst-metadata ip)
  35. default-slug))
  36. (write-metadata metadata op)
  37. (pandocify-rst ip op output-format))
  38. convert-rst)
  39. (define convert-rst->md
  40. (make-convert-rst "markdown-smart"))
  41. (define convert-rst->html
  42. (make-convert-rst "html"))
  43. (define (read-rst-metadata ip)
  44. (define title #f)
  45. (define rest-metadata #f)
  46. (set! title (get-stripped-line ip))
  47. (when (string-match "^[=-~]+$" title)
  48. (set! title (get-stripped-line ip)))
  49. (get-line ip)
  50. (get-line ip)
  51. (set! rest-metadata
  52. (let lp ()
  53. (let ((line (get-stripped-line ip)))
  54. (if (equal? line "")
  55. '()
  56. (let ((colon-pos (string-index line #\: 1)))
  57. (if colon-pos
  58. (let* ((key-str (substring line 1 colon-pos))
  59. (key (string->symbol (string-downcase key-str)))
  60. (val (substring line (+ colon-pos 2))))
  61. (cons (cons key val)
  62. (lp)))
  63. '()))))))
  64. (cons (cons 'title title)
  65. rest-metadata))
  66. (define (pandocify-rst ip op out-format)
  67. (define tmpfile (tmpnam))
  68. (define pipe
  69. (open-pipe (format #f "pandoc -f rst -t ~a -o ~a"
  70. out-format tmpfile)
  71. OPEN_WRITE))
  72. (display (get-string-all ip) pipe)
  73. (close-pipe pipe)
  74. (let ((converted (call-with-input-file tmpfile get-string-all)))
  75. (display converted op)
  76. (delete-file tmpfile)))
  77. ;;; html stuff
  78. ;;; ==========
  79. (define (html-head->metadata head)
  80. (let lp ((head head))
  81. (match head
  82. ('() '())
  83. ((('title title) rest ...)
  84. (cons (cons 'title title)
  85. (lp rest)))
  86. ((('meta ('@ tags ...)) rest ...)
  87. (let* ((key (string->symbol (string-downcase (cadr (assoc 'name tags)))))
  88. (val (cadr (assoc 'contents tags))))
  89. (cons (cons key val)
  90. (lp rest))))
  91. ((_ rest ...)
  92. (lp rest)))))
  93. (define (convert-html default-slug ip op)
  94. (define-values (head body)
  95. (get-html-head-body (html->sxml ip)))
  96. (define new-metadata
  97. (maybe-append-slug (html-head->metadata head)
  98. default-slug))
  99. (write-metadata new-metadata op)
  100. (display (sxml->html body) op))
  101. (define (get-html-head-body post-html)
  102. (let* ((html-data
  103. (match post-html
  104. (('*TOP* (html html-data ...) _ ...)
  105. html-data)))
  106. (head
  107. (find (match-lambda
  108. (('head _ ...) #t)
  109. (_ #f))
  110. html-data))
  111. (body
  112. (find (match-lambda
  113. (('body _ ...) #t)
  114. (_ #f))
  115. html-data)))
  116. (values head body)))
  117. ;;; markdown stuff
  118. ;;; ==============
  119. (define (convert-md default-slug ip op)
  120. (define new-metadata
  121. (maybe-append-slug (read-md-metadata ip)
  122. default-slug))
  123. (write-metadata new-metadata op)
  124. (newline op)
  125. (display (get-string-all ip) op))
  126. (define (read-md-metadata ip)
  127. (let lp ()
  128. (define line
  129. (get-stripped-line ip))
  130. (define colon-pos
  131. (string-index line #\:))
  132. (if colon-pos
  133. (let* ((key-str (substring line 0 colon-pos))
  134. (key (string->symbol (string-downcase key-str)))
  135. (val (string-trim-both (substring line (+ colon-pos 1)) char-set:whitespace)))
  136. (cons (cons key val)
  137. (lp)))
  138. '())))
  139. ;;; conversion stuff
  140. ;;; ================
  141. ;; Returns two values to its continuation: build-output and new-filename
  142. (define* (decide-file-conversion fname #:key rst->md?)
  143. (match (filename-extension fname)
  144. ("rst"
  145. (values (if rst->md?
  146. convert-rst->md
  147. convert-rst->html)
  148. (string-append (car (string-split fname #\.))
  149. (if rst->md? ".md" ".html"))))
  150. ("html"
  151. (values convert-html fname))
  152. ("md"
  153. (values convert-md fname))))
  154. (define* (convert-one fname #:key rst->md?)
  155. (define-values (converter new-fname)
  156. (decide-file-conversion fname #:rst->md? rst->md?))
  157. (define default-slug (car (string-split fname #\.)))
  158. (call-with-input-file (string-append "content/" fname)
  159. (lambda (ip)
  160. (call-with-output-file (string-append "posts/" new-fname)
  161. (lambda (op)
  162. (converter default-slug ip op))))))