~subsetpark/bagatto

26e05cd80b470000d604e57a68bd52e91adb2a4b — Zach Smith 11 months ago 6ac2d6e
Factor out error handling
2 files changed, 82 insertions(+), 64 deletions(-)

M src/core.janet
A src/error.janet
M src/core.janet => src/core.janet +38 -64
@@ 1,39 1,24 @@
(import path)

(import src/util)
(import src/error)

(defn- struct->table [s]
  (->> (or s @{}) (kvs) (splice) (table)))

(defn- apply-error
  [err f args-type ret-type]
  (error (string/format "%s;\nExpected %s function signature:\n(defn %s %s\n %s)"
                        err
                        (case ret-type :p "path" :c "contents")
                        (or (disasm f :name) "f")
                        (case args-type :d "[data]" :di "[data item]")
                        (case ret-type :p `"foo/bar/..."` :c `"<html>..."`))))


(defn- maybe-apply [f args args-type ret-type]
  (if (function? f)
    (try
      (f ;args)
      ([err fib] (apply-error err f args-type ret-type)))
      ([err fib] (error/path-content-error err f args-type ret-type)))
    f))

(defn- set-defaults [spec]
  (table/setproto (struct->table spec)
                  (struct->table (dyn :bagatto-defaults))))

(defn- attrs-error
  [err attrs-f]
  (error (string err
                 ";\n"
                 "Required parse function signature:\n"
                 "(defn " (or (disasm attrs-f :name) "f") " [src attrs]\n"
                 " ...\n"
                 " attrs)")))

(defn load-data [data-spec]
  ```
  First phase of main business logic. `data-spec` contains a


@@ 57,55 42,49 @@
  ```
  (def res @{})
  
  (defn make-attrs [filename &opt file-contents attrs-f]
    (default attrs-f (fn [_ x] x))
  (defn make-attrs [filename &opt file-contents parser]
    (default parser (fn [_ x] x))
    (try
      (->> @{:path filename :src file-contents}
           (attrs-f file-contents))
      ([err fib] (attrs-error err attrs-f))))
           (parser file-contents))
      ([err fib] (error/attrs-error err parser))))
  
  (loop [[entry spec] :pairs data-spec]
    (let [with-defaults (set-defaults spec)
          transform-f (or (spec :transform) identity)
          data (match with-defaults
                 
                 ({:src loader :attrs attrs-f} (fiber? loader))
                 ({:src loader :attrs parser} (fiber? loader))
                 (-> (seq [loader-out :generate loader]
                         (match loader-out
                           [filename file-contents]
                           (make-attrs filename file-contents attrs-f)
                           filename
                           (make-attrs filename)))
                    (transform-f))
                          (match loader-out
                            [filename file-contents]
                            (make-attrs filename file-contents parser)
                            filename
                            (make-attrs filename)))
                     (transform-f))
                 
                 ({:src loader :attrs attrs-f} (function? loader))
                 ({:src loader :attrs parser} (function? loader))
                 (let [[filename file-contents] (loader)]
                   (make-attrs filename file-contents attrs-f))
                   (make-attrs filename file-contents parser))
                 
                 ({:src path :attrs attrs-f} (string? path))
                 ({:src path :attrs parser} (string? path))
                 (let [file-contents (slurp path)]
                   (make-attrs path file-contents attrs-f))
                   (make-attrs path file-contents parser))
                 
                 {:attrs attrs}
                 attrs
                 
                 _
                 (error (string "Received invalid data spec: "
                                (string/format "%q" with-defaults)
                                "\n\n"
                                "Specification can be one of the following:\n"
                                "{:src (loader|path) :attrs attrs-f}\n"
                                "{:attrs attrs}\n")))]
                 _ (error/data-error with-defaults))]
      (put res entry data)))
  res)

(defn produce-writer-specs [site data]
  ``
  ```
  Second phase of main business logic. `site` contains a specification
  for generating a website and `data` is all the source data we have to
  do it with. Here we generate a new writer fiber for each file in the
  website.
  ``
  ```
  (def writers @[])
  
  (defn push-writer [type path contents]


@@ 119,41 98,36 @@
      (match with-defaults
        
        {:each site-selector
         :path path-f
         :contents contents-f}
         :path path-generator
         :contents renderer}
        (loop [item :in (data site-selector)]
          (if-let [_should-read (filter data item)
                   path (maybe-apply path-f [data item] :di :p)
                   contents (maybe-apply contents-f [data item] :di :c)]
                   path (maybe-apply path-generator [data item] :di :p)
                   contents (maybe-apply renderer [data item] :di :c)]
            (push-writer :write path contents)))
        
        {:each site-selector
         :path path-f}
         :path path-generator}
        (loop [item :in (data site-selector)]
          (if-let [_should-read (filter data item)
                   path (maybe-apply path-f [data item] :di :p)]
            (push-writer :copy (item :path) path)))
                   from (item :path)
                   to (maybe-apply path-generator [data item] :di :p)]
            (push-writer :copy from to)))
        
        {:path path-f
         :contents contents-f}
        (if-let [path (maybe-apply path-f [data] :d :p)
                 contents (maybe-apply contents-f [data] :d :c)]
        {:path path-generator
         :contents renderer}
        (if-let [path (maybe-apply path-generator [data] :d :p)
                 contents (maybe-apply renderer [data] :d :c)]
          (push-writer :write path contents))
        
        {:some site-selector
         :path path-f}
         :path path-generator}
        (if-let [item (data site-selector)
                 path (maybe-apply path-f [data] :d :p)]
          (push-writer :copy (item :path) path))
                 from (item :path)
                 to (maybe-apply path-generator [data] :d :p)]
          (push-writer :copy from to))

        _ (error (string "Received invalid site spec: "
                         (string/format "%q" with-defaults)
                         "\n\n"
                         "Specification can be one of the following:\n"
                         "{:each site-selector :path path-f :contents contents-f}\n"
                         "{:each site-selector :path path-f}\n"
                         "{:path path-f :contents contents-f}\n"
                         "{:some site-selector :path path-f}"))))) 
        _ (error/site-error with-defaults)))) 
        
  writers)


A src/error.janet => src/error.janet +44 -0
@@ 0,0 1,44 @@
(defn- f-name [f] (or (disasm f :name) "f"))

(defn path-content-error
  [err f args-type ret-type]
  (error (string/format
          "%s;\nExpected %s function signature:\n(defn %s %s\n %s)"
          err
          (case ret-type :p "path" :c "contents")
          (f-name f)
          (case args-type :d "[data]" :di "[data item]")
          (case ret-type :p `"foo/bar/..."` :c `"<html>..."`))))

(defn attrs-error
  [err attrs-f]
  (error (string/format
          "%s;\nRequired parse function signature:\n(defn %s [src attrs]\n ...\n attrs)"
          err
          (or (disasm attrs-f :name) "f"))))

(defn data-error
  [spec]
  (error (string/format
          ```
          Received invalid data spec: %q

          Specification can be one of the following:
          {:src (loader|path) :attrs parser)}
          {:attrs attrs}
          ```
          spec)))

(defn site-error
  [spec]
  (error (string/format
          ```
          Received invalid site spec: %q

          Specification can be one of the following:
          {:each site-selector :path path-generator :contents renderer} (write)
          {:each site-selector :path path-generator} (copy)
          {:path (path|path-generator) :contents (contents|renderer)} (write)
          {:some site-selector :path (path|path-generator)} (copy)
          ```
          spec)))