~subsetpark/bagatto

ref: d4f43c82fb988f1e77bb64c61c157d507741fb3d bagatto/src/core.janet -rw-r--r-- 3.1 KiB
d4f43c82 — Zach Smith Use wait-for-fibs 4 months ago
                                                                                
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
(import spork/path)
(import spork/temple)

(import /src/util)
(import /src/error)
(import /src/threads)
(import /src/loaders)
(import /src/generators)
(import /src/writers)
(import /src/env)

(def bagatto
  ```
  An environment populated by the "stdlib" we want to expose to
  template and index module authors.
  ```
  (require "/bagatto-require"))

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

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

(defn load-data
  ```
  First phase of main business logic. `data-spec` contains a
  specification of all the sources necessary to generate our `attrs`
  entries, which are the data structures to be used in generating the
  site.

  A data specification can be in one of four formats:
  
  1. A simple struct with only an :attrs field. The attrs generated
  will simply be the value at `:attrs`.
  2. A struct with a :src that's a string path to a file relative from
  the current directory, and an :attrs function that will be called on
  the contents of the file.
  3. A struct with a :src that's a 0-arity function that will return a
  [filename file-contents] tuple, and an :attrs function that will be
  called on the file-contents.
  4. A struct with a :src that returns a fiber which will yield some
  finite number of [filename file-contents] tuples, and an :attrs
  function that will be called on each file-contents.
  ```
  [data-spec env]
  (let [data-pairs (pairs data-spec)
        jobs (seq [[spec-name spec] :in data-pairs]
               (threads/print "Reading data spec " spec-name "...")
               (setdyn :error-context {:spec-name spec-name})
               (loaders/from-spec (set-defaults spec) spec-name))]

    (threads/distribute-gather jobs)))

(defn produce-writer-specs
  ```
  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 specification (a tuple
  of path and contents) for each file in the website.
  ```
  [site data env]
  (let [site-pairs (pairs site)
        jobs (seq [[spec-name spec] :in site-pairs]
               (threads/print "Reading site spec " spec-name "...")
               (generators/from-spec (set-defaults spec)
                                     spec-name
                                     data))]

    (let [segments (-> (threads/distribute-gather jobs) (values))]
      (array/concat ;segments))))

(defn evaluate-writer-specs
  ```
  Third phase of business logic : given a list of writer specs, render
  them into new files.
  ```
  [output-dir writer-specs]

  (let [spec-count (length writer-specs)
        handler (writers/write-handler output-dir @{})
        supervisor (ev/chan spec-count)
        fibers (map |(ev/go (fn [] (handler $)) nil supervisor) writer-specs)]

    (printf "Writing %d output specs..." spec-count)

    (repeat spec-count
      (let [[sig fiber] (ev/take supervisor)]
        (unless (= sig :ok)
          (each f fibers (ev/cancel f "sibling canceled"))
          (propagate (fiber/last-value fiber) fiber)))))

  (printf "Done writing."))