summaryrefslogtreecommitdiff
path: root/src/weepinbell/storage/edn_storage.clj
blob: 06e857a78b274b348372991b2fa2ed78ef0b0844 (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
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
95
96
97
98
99
100
101
102
103
104
105
(ns weepinbell.storage.edn-storage
  "EDN file-based storage middle layer.
  Entry function is use-store, which makes a valid storage vtable."
  (:require [fast-edn.core :as edn]
            [clojure.java.io :as io]
            [weepinbell.utils.url :refer [get-domain get-target get-path url=]]
            [weepinbell.storage.spec]
            [clojure.string :refer [join]]))

(defn unload-data
  "Write to data file, replacing everything."
  [datasource data]
  (when-let [data-with-string (map pr-str data)]
    (spit datasource (join "\n" data-with-string))))

;; predicates used for filtering webmention
(defn- st-pair-equal-pred [source target wm]
  (let [wm-source (get-in wm [:webmention :source])
        wm-target (get-in wm [:webmention :target])]
    (and (some? source) (some? target)
         (some? wm-source) (some? wm-target)
         (url= source wm-source)
         (url= target wm-target))))
(defn- id-equal-pred [id wm]
  (= id (get-in wm [:webmention :id])))
(defn- get-predicate [source target]
  (if target
    #(st-pair-equal-pred source target %)
    #(id-equal-pred source %)))
(defn- domain-equal [domain wm]
  (= domain
     (-> wm get-target get-domain)))
(defn- path-equal [path wm]
  (= path
     (-> wm get-target get-path)))
;; end predicates

(defn with-webmentions
  "Opens the data file and applies function f to a lazy sequence of parsed Webmentions."
  [datasource f]
  (with-open [r (io/reader datasource)]
    (->> (line-seq r)
         (map edn/read-string)
         f)))

(defn get-wm
  ([datasource id] (get-wm datasource id nil))
  ([datasource source target]
   (let [pred (get-predicate source target)]
     (with-webmentions datasource
       #(first (filter pred %))))))

(defn delete-wm
  ([datasource id] (delete-wm datasource id nil))
  ([datasource source target]
   (let [pred (get-predicate source target)]
     (with-webmentions datasource
       #(->> %
             (remove pred)
             (unload-data datasource))))))

(defn get-wms-by-domain
  ([datasource domain]
   (get-wms-by-domain datasource domain nil))
  ([datasource domain path]
   (with-webmentions datasource
     #(cond->> %
        true         (filter (partial domain-equal domain))
        (some? path) (filter (partial path-equal path))
        true         (sort-by (fn [x] (:modified (:webmention x))))
        true         vec))))

(defn first-line-empty? [path]
  (with-open [rdr (io/reader path)]
    (let [line (line-seq rdr)]
      (empty? line))))

(defn append-wm
  "Append an accepted webmention to .edn data file."
  [datasource data]
  (let [newline? (if (first-line-empty? datasource) "" "\n")
        line (str newline? (pr-str data))]
    (spit datasource line :append true)))

(defn update-wm
  "Update an existing webmention"
  ([datasource id new-data]
   (delete-wm datasource id)
   (append-wm datasource new-data))
  ([datasource source target new-data]
   (delete-wm datasource source target)
   (append-wm datasource new-data)))

;; make storage vtable
(def file-exists? #(.exists (io/file %)))
(defn use-store [datasource]
  (when (not (file-exists? datasource))
    (io/make-parents datasource)
    (spit datasource ""))
  {:append (partial append-wm datasource)
   :update (partial update-wm datasource)
   :delete (partial delete-wm datasource)
   :get    (partial get-wm datasource)
   :get-by-domain (partial get-wms-by-domain datasource)})