/
filesystem.clj
106 lines (88 loc) · 3 KB
/
filesystem.clj
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
106
(ns kaleidoscope.clj.persistence.filesystem
(:require [taoensso.timbre :as log]
[clojure.string :as string]
[kaleidoscope.clj.persistence.filesystem :as fs])
(:import java.security.MessageDigest
java.math.BigInteger)
(:refer-clojure :exclude [get]))
(defprotocol DistributedFileSystem
"A distributed Filesystem. This has properties that are different from
a normal filesystem.
For example, in a distributed filesystem we want to be able to build caching
into the filesystem. When we ask for a file, we also want to be able to say
'Please don't actually send me the contents of the file if I have the most
recent, up-to-date version'"
(ls [_ path options] "Like the unix `ls` command. Returns a collection of metadata from a path, without the contents")
(get-file [_ path options] "Retrieve a single file")
(put-file [_ path input-stream metadata] "Put a file"))
(defn folder?
[url]
(string/ends-with? (str url) "/"))
(defn canonical-url
[url]
(if (and (string/starts-with? url "/")
(< 1 (count url)))
(subs url 1)
url))
;; Change contract so this returns :content and :metadata
(defn get
([filesystem path]
(get filesystem path {}))
([filesystem path options]
(let [uri (canonical-url path)]
(log/infof "Looking up content at path %s in Filesystem %s" uri filesystem)
(cond
(folder? uri) (ls filesystem uri options)
:else (get-file filesystem uri options)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Representing objects in the filesystem.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn object?
[x]
(= ::object (type x)))
(defn object-content
[fs-object]
{:pre [(object? fs-object)]}
(get-in fs-object [:content]))
(defn object-version
[fs-object]
{:pre [(object? fs-object)]}
(get-in fs-object [:version]))
(defn object-metadata
[fs-object]
{:pre [(object? fs-object)]}
(get-in fs-object [:metadata]))
(defn object
"An object in the distributed Filesystem.
All objects must have a version"
[x]
{:pre [(:version x)]}
(with-meta x {:type ::object}))
(def not-modified-response
"There is an asymmetry in a distributed filesystem - when we try to get a
resource, we don't always want the full resource back because it would take
longer to send over the wire.
So we may have multiple ways to represent a resource - we can represent the
raw resource, or we can say 'You already have the most recent version, use
your copy'"
(object {:version ::not-modified}))
(def does-not-exist-response
(object {:version ::does-not-exist}))
(defn not-modified?
[x]
(= ::not-modified (object-version x)))
(defn does-not-exist?
[x]
(= ::does-not-exist (object-version x)))
(defn md5
[s]
(->> s
str
.getBytes
(.digest (MessageDigest/getInstance "MD5"))
(BigInteger. 1)
(format "%032x")))
(comment
(does-not-exist? does-not-exist-response)
(file-metadata (object {:FOO "Bar"}))
)