summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ennu-html.el4
-rw-r--r--ennu.el193
2 files changed, 92 insertions, 105 deletions
diff --git a/ennu-html.el b/ennu-html.el
index 36796d1..9a24f9c 100644
--- a/ennu-html.el
+++ b/ennu-html.el
@@ -18,11 +18,11 @@
 ;; TODO: Pass title through org-export-data-with-backend or something
 ;; similar in order to export org syntax in title
 (defun ennu-export-post (path desc backend)
-  (let ((post (concat (expand-file-name path (ennu-setting :posts-directory))
+  (let ((filename (concat (expand-file-name path (ennu-setting :posts-directory))
                       ".org")))
     (xmlgen `(a :href ,(expand-file-name*
                         path (ennu-setting :posts-directory))
-                ,(or desc (plist-get (ennu-post-metadata post) :title))))))
+                ,(or desc (ennu-post-title (ennu-read-post filename)))))))
 
 (org-link-set-parameters
  "post" :export 'ennu-export-post)
diff --git a/ennu.el b/ennu.el
index c24739e..04017ba 100644
--- a/ennu.el
+++ b/ennu.el
@@ -12,6 +12,64 @@
 (defvar ennu-version "0.1.0"
   "Ennu version string")
 
+(cl-defstruct (ennu-post (:constructor ennu-make-post)
+                         (:copier nil))
+  filename author date language links
+  summary tags thumbnail title translation-group)
+
+(defun ennu-posts (posts-directory)
+  (sort (seq-map 'ennu-read-post
+                 (file-expand-wildcards
+                  (concat (file-name-as-directory
+                           (ennu-setting :posts-directory))
+                          "*.org")))
+        'ennu-later-post-p))
+
+(defun ennu-later-post-p (post1 post2)
+  (time-less-p (ennu-post-date post2)
+               (ennu-post-date post1)))
+
+(defun ennu-read-post (filename)
+  (ennu--read-post
+   filename (file-attribute-modification-time
+             (file-attributes filename))))
+
+(defmemoize ennu--read-post (filename last-modified)
+  (ennu-with-file-contents filename
+    (let ((metadata (org-export-get-environment 'ennu-html))
+          (export (apply-partially 'org-export-with-backend 'ennu-html)))
+      (seq-do (lambda (key)
+                (unless (plist-member metadata key)
+                  (user-error "Metadata %s not specified" key)))
+              ennu-mandatory-metadata)
+      (let ((links (org-element-map (org-element-parse-buffer) 'link
+                     (lambda (link)
+                       (pcase link
+                         (`(link ,properties . ,_)
+                          (let ((link-type (org-element-property :type link)))
+                            (when (member link-type (list "image" "static" "video"))
+                              (cons link-type (org-element-property :path link))))))))))
+        (ennu-make-post
+         :filename filename
+         :author (funcall export (first (plist-get metadata :author)))
+         :date (org-timestamp-to-time (first (plist-get metadata :date)))
+         :language (plist-get metadata :language)
+         :links links
+         :summary (funcall export (first (plist-get metadata :summary)))
+         :tags (plist-get metadata :filetags)
+         :thumbnail (or (plist-get metadata :thumbnail)
+                        (seq-some (lambda (link)
+                                    (pcase link
+                                      (`("image" . ,path) path)
+                                      (`("video" . ,path) (ennu-video-poster path))))
+                                  links))
+         :title (funcall export (first (plist-get metadata :title)))
+         :translation-group (or (plist-get metadata :translation-group)
+                                (file-name-base filename)))))))
+
+(defvar ennu-mandatory-metadata
+  (list :title :date))
+
 (defmacro ennu-with-file-contents (file &rest body)
   "Create a temporary buffer, insert contents of FILE into that
 buffer and evaluate BODY. The value returned is the value of the
@@ -26,10 +84,10 @@ last form in BODY."
 
 (defun ennu-publish-post (post)
   ;; TODO: Tangle post
-  `((,post)
-    (,(ennu--org-output-filename post))
+  `((,(ennu-post-filename post))
+    (,(ennu--org-output-filename (ennu-post-filename post)))
     ,(lambda (output-file)
-       (ennu-with-file-contents post
+       (ennu-with-file-contents (ennu-post-filename post)
          (org-export-to-file 'ennu-html output-file))
        ;; (when-let (tangle-dir (or (plist-get posts-plist :valai-tangle-directory)
        ;;                           valai-tangle-directory))
@@ -61,14 +119,6 @@ last form in BODY."
     (`(,poster . ,_) poster)
     (`() (user-error "Poster for %s not found" video))))
 
-(defun ennu-post-thumbnail (post)
-  (or (plist-get (ennu-post-metadata post) :thumbnail)
-      (seq-some (lambda (link)
-                  (pcase link
-                    (`("image" . ,path) path)
-                    (`("video" . ,path) (ennu-video-poster path))))
-                (ennu-post-links post))))
-
 (defun ennu-add-tongue-suffix (filename tongue)
   (pcase tongue
     ("en" filename)
@@ -88,7 +138,7 @@ last form in BODY."
 (defun ennu-publish-index (filename-prefix tongue title posts-per-page posts)
   (let* ((number-of-pages (ceiling (length posts) posts-per-page))
          (page-numbers (number-sequence 1 number-of-pages)))
-    `(,posts
+    `(,(seq-map 'ennu-post-filename posts)
       ,(cons (ennu-add-tongue-suffix (format "%s.html" filename-prefix) tongue)
              (seq-map (apply-partially 'ennu-index-filename filename-prefix tongue "html")
                       page-numbers))
@@ -100,23 +150,22 @@ last form in BODY."
               (insert (format "#+LANGUAGE: %s\n" tongue))
               (insert "#+OPTIONS: num:nil toc:nil\n\n")
               (seq-do (lambda (post)
-                        (let ((metadata (ennu-post-metadata post)))
-                          (insert (format "* [[post:%s]]\n" (file-name-base post)))
-                          (insert (format-time-string
-                                   "/%b %e, %Y/\n\n"
-                                   (plist-get metadata :date)))
-                          (when-let ((thumbnail (ennu-post-thumbnail post)))
-                            (insert (format "[[thumbnail:%s]]\n\n" thumbnail)))
-                          (when-let ((summary (plist-get metadata :summary)))
-                            (insert summary)
-                            (insert "\n\n"))
-                          (when-let ((tags (plist-get metadata :filetags)))
-                            (insert "Tags: ")
-                            (insert
-                             (string-join (seq-map (apply-partially 'format "[[tag:%s]]")
-                                                   tags)
-                                          ", "))
-                            (insert "\n\n"))))
+                        (insert (format "* [[post:%s]]\n" (file-name-base (ennu-post-filename post))))
+                        (insert (format-time-string
+                                 "/%b %e, %Y/\n\n"
+                                 (ennu-post-date post)))
+                        (when-let ((thumbnail (ennu-post-thumbnail post)))
+                          (insert (format "[[thumbnail:%s]]\n\n" thumbnail)))
+                        (when-let ((summary (ennu-post-summary post)))
+                          (insert summary)
+                          (insert "\n\n"))
+                        (when-let ((tags (ennu-post-tags post)))
+                          (insert "Tags: ")
+                          (insert
+                           (string-join (seq-map (apply-partially 'format "[[tag:%s]]")
+                                                 tags)
+                                        ", "))
+                          (insert "\n\n")))
                       posts)
               (unless (= page-number 1)
                 (insert (format "[[./%s][Newer posts]]\n"
@@ -142,7 +191,7 @@ last form in BODY."
   (format-time-string "%Y-%m-%dT%H:%M:%SZ" date))
 
 (defun ennu-publish-feed (feed-file title rights posts)
-  `(,posts
+  `(,(seq-map 'ennu-post-filename posts)
     (,feed-file)
     ,(lambda (output-file)
        (with-temp-file output-file
@@ -151,7 +200,7 @@ last form in BODY."
            `(feed :xmlns "http://www.w3.org/2005/Atom"
                   (id ,(ennu--absolute-uri ""))
                   (title ,title)
-                  (updated ,(ennu--atom-date (plist-get (ennu-post-metadata (first posts)) :date)))
+                  (updated ,(ennu--atom-date (ennu-post-date (first posts))))
                   (link :rel "self" :href ,(ennu--absolute-uri feed-file))
                   (generator
                    ,(format "Emacs %d.%d Org-mode %s ennu %s"
@@ -160,21 +209,20 @@ last form in BODY."
                   ,@(seq-map 'ennu--feed-entry posts))))))))
 
 (defun ennu--feed-entry (post)
-  (let* ((metadata (ennu-post-metadata post))
-         (lang (plist-get metadata :lang))
-         (link (ennu--absolute-uri (ennu--org-output-filename post))))
+  (let ((link (ennu--absolute-uri (ennu--org-output-filename
+                                   (ennu-post-filename post)))))
     `(entry (id ,link)
-            (title :xml:lang ,lang ,(plist-get metadata :title))
-            (updated ,(ennu--atom-date (plist-get metadata :date)))
+            (title :xml:lang ,(ennu-post-language post) ,(ennu-post-title post))
+            (updated ,(ennu--atom-date (ennu-post-date post)))
             (author
-             (name ,(plist-get metadata :author))
+             (name ,(ennu-post-author post))
              (email ,user-mail-address))
-            (content :type "html" :xml:lang ,lang
-                     ,(ennu-with-file-contents post
+            (content :type "html" :xml:lang ,(ennu-post-language post)
+                     ,(ennu-with-file-contents (ennu-post-filename post)
                         (org-export-as 'ennu-html nil nil t)))
             (link :rel "alternate" :href ,link)
             ,@(seq-map (lambda (tag) `(category :term ,tag))
-                       (plist-get metadata :filetags)))))
+                       (ennu-post-tags post)))))
 
 (defun ennu-setting (property)
   (pcase property
@@ -233,50 +281,6 @@ last form in BODY."
 (defun ennu-publish-copy (file)
   `((,file) (,file) ,(apply-partially 'copy-file file)))
 
-(defun ennu-post-links (post)
-  (ennu-with-file-contents post
-    (org-element-map (org-element-parse-buffer) 'link
-      (lambda (link)
-        (pcase link
-          (`(link ,properties . ,_)
-           (let ((link-type (org-element-property :type link)))
-             (when (member link-type (list "image" "static" "video"))
-               (cons link-type (org-element-property :path link))))))))))
-
-(defun ennu-plist-map-to-plist (function plist)
-  "Apply FUNCTION to each key-value pair of PLIST and return the
-result as a plist with keys being the keys in PLIST and the
-values being the values returned by FUNCTION. FUNCTION is called
-with two arguments -- the key and the value."
-  (seq-mapcat
-   (pcase-lambda (`(,key ,value))
-     (list key (funcall function key value)))
-   (seq-partition plist 2)))
-
-(defun ennu-post-metadata (post)
-  (ennu--post-metadata-memoized
-   post (file-attribute-modification-time
-         (file-attributes post))))
-
-(defmemoize ennu--post-metadata-memoized (post last-modified)
-  (ennu-with-file-contents post
-    (let ((metadata (org-export-get-environment 'ennu-html))
-          (export (apply-partially 'org-export-with-backend 'ennu-html)))
-      (seq-do (lambda (key)
-                (unless (plist-member metadata key)
-                  (user-error "Metadata %s not specified" key)))
-              ennu-mandatory-metadata)
-      (ennu-plist-map-to-plist
-       (lambda (key value)
-         (pcase key
-           ((or :author :summary :title)
-            (funcall export (first (plist-get metadata key))))
-           (:date (org-timestamp-to-time (first (plist-get metadata :date))))))
-       metadata))))
-
-(defvar ennu-mandatory-metadata
-  (list :title :date))
-
 (defun newest-file (files)
   (pcase files
     (`(,head . ,tail)
@@ -318,20 +322,9 @@ with two arguments -- the key and the value."
                       (seq-map 'file-name-directory absolute-output-files)))
              (apply publish absolute-output-files))))))))
 
-(defun ennu--later-post (post1 post2)
-  (time-less-p (plist-get (ennu-post-metadata post2) :date)
-               (plist-get (ennu-post-metadata post1) :date)))
-
 (defun ennu-publish-static-file (file)
   `((,file) (,file) ,(apply-partially 'copy-file file)))
 
-(defun ennu-posts (posts-directory)
-  (sort (file-expand-wildcards
-         (concat (file-name-as-directory
-                  (ennu-setting :posts-directory))
-                 "*.org"))
-        'ennu--later-post))
-
 (defun ennu-publish-link (link)
   (pcase link
     (`("image" . ,path)
@@ -346,12 +339,6 @@ with two arguments -- the key and the value."
      (ennu-publish-copy (ennu--expand-relative (ennu-video-poster path)
                                                (ennu-setting :images-directory))))))
 
-(defun ennu-post-tags (post)
-  (plist-get (ennu-post-metadata post) :filetags))
-
-(defun ennu-post-tongue (post)
-  (plist-get (ennu-post-metadata post) :language))
-
 (defmacro ennu-with-current-directory (directory &rest body)
   "Change to DIRECTORY, evaluate BODY and restore the current
 working directory. The value returned is the value of the last
@@ -412,7 +399,7 @@ as keys. Keys are compared using `equal'."
              (seq-map
               (pcase-lambda (`(,tongue . ,posts))
                 (ennu-publish-index "index" tongue blog-title posts-per-page posts))
-              (seq-group-by 'ennu-post-tongue posts))
+              (seq-group-by 'ennu-post-language posts))
              (seq-mapcat
               (pcase-lambda (`(,tag . ,posts))
                 (seq-map
@@ -420,7 +407,7 @@ as keys. Keys are compared using `equal'."
                    (ennu-publish-index
                     (ennu--expand-relative tag (ennu-setting :tag-directory))
                     tongue tag posts-per-page posts))
-                 (seq-group-by 'ennu-post-tongue posts)))
+                 (seq-group-by 'ennu-post-language posts)))
               (ennu-many-to-many-group-by 'ennu-post-tags posts))
              ;; Publish links
              (seq-map 'ennu-publish-link