aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/program/aedifex/repository.lux
blob: 351d1c066fcd4d49a133e119108d112fdfd6ef96 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
(.module:
  [lux #*
   ["." host (#+ import:)]
   [abstract
    [monad (#+ do)]]
   [control
    ["." io (#+ IO)]
    ["." try (#+ Try)]
    ["." exception (#+ exception:)]
    [concurrency
     ["." promise (#+ Promise)]
     ["." stm]]]
   [data
    ["." binary (#+ Binary)]
    ["." text
     ["%" format (#+ format)]]
    [number
     ["n" nat]]]
   [tool
    [compiler
     ["." version]
     ["." language #_
      ["#/." lux #_
       ["#" version]]]]]
   [world
    [net (#+ URL)
     ["." uri (#+ URI)]]]]
  ["." / #_
   ["#." identity (#+ Identity)]
   ["/#" // #_
    ["#." artifact (#+ Artifact)
     ["#/." extension (#+ Extension)]]]])

(type: #export Address
  URL)

(signature: #export (Repository !)
  (: (-> URI (! (Try Binary)))
     download)
  (: (-> URI Binary (! (Try Any)))
     upload))

(def: #export (async repository)
  (-> (Repository IO) (Repository Promise))
  (structure
   (def: (download uri)
     (promise.future (\ repository download uri)))

   (def: (upload uri content)
     (promise.future (\ repository upload uri content)))
   ))

(signature: #export (Simulation s)
  (: (-> URI s (Try [s Binary]))
     on-download)
  (: (-> URI Binary s (Try s))
     on-upload))

(def: #export (mock simulation init)
  (All [s] (-> (Simulation s) s (Repository Promise)))
  (let [state (stm.var init)]
    (structure
     (def: (download uri)
       (stm.commit
        (do {! stm.monad}
          [|state| (stm.read state)]
          (case (\ simulation on-download uri |state|)
            (#try.Success [|state| output])
            (do !
              [_ (stm.write |state| state)]
              (wrap (#try.Success output)))
            
            (#try.Failure error)
            (wrap (#try.Failure error))))))

     (def: (upload uri content)
       (stm.commit
        (do {! stm.monad}
          [|state| (stm.read state)]
          (case (\ simulation on-upload uri content |state|)
            (#try.Success |state|)
            (do !
              [_ (stm.write |state| state)]
              (wrap (#try.Success [])))
            
            (#try.Failure error)
            (wrap (#try.Failure error))))))
     )))

(import: java/lang/String)

(import: java/lang/AutoCloseable
  ["#::."
   (close [] #io #try void)])

(import: java/io/InputStream)

(import: java/io/OutputStream
  ["#::."
   (flush [] #io #try void)
   (write [[byte]] #io #try void)])

(import: java/net/URLConnection
  ["#::."
   (setDoOutput [boolean] #io #try void)
   (setRequestProperty [java/lang/String java/lang/String] #io #try void)
   (getInputStream [] #io #try java/io/InputStream)
   (getOutputStream [] #io #try java/io/OutputStream)])

(import: java/net/HttpURLConnection
  ["#::."
   (setRequestMethod [java/lang/String] #io #try void)
   (getResponseCode [] #io #try int)])

(import: java/net/URL
  ["#::."
   (new [java/lang/String])
   (openConnection [] #io #try java/net/URLConnection)])

(import: java/io/BufferedInputStream
  ["#::."
   (new [java/io/InputStream])
   (read [[byte] int int] #io #try int)])

(exception: #export (no-credentials {address Address})
  (exception.report
   ["Address" (%.text address)]))

(exception: #export (deployment-failure {code Int})
  (exception.report
   ["Code" (%.int code)]))

(def: #export (uri artifact extension)
  (-> Artifact Extension URI)
  (format (//artifact.uri artifact) extension))

(def: buffer-size
  (n.* 512 1,024))

(def: user-agent
  (format "LuxAedifex/" (version.format language/lux.version)))

(structure: #export (remote identity address)
  (All [s] (-> (Maybe Identity) Address (Repository IO)))

  (def: (download uri)
    (do {! (try.with io.monad)}
      [connection (|> (format address uri)
                      java/net/URL::new
                      java/net/URL::openConnection)
       #let [connection (:coerce java/net/HttpURLConnection connection)]
       _ (java/net/HttpURLConnection::setRequestMethod "GET" connection)
       _ (java/net/URLConnection::setRequestProperty "User-Agent" ..user-agent connection)
       input (|> connection
                 java/net/URLConnection::getInputStream
                 (\ ! map (|>> java/io/BufferedInputStream::new)))
       #let [buffer (binary.create ..buffer-size)]]
      (loop [output (\ binary.monoid identity)]
        (do !
          [bytes-read (java/io/BufferedInputStream::read buffer +0 (.int ..buffer-size) input)]
          (case bytes-read
            -1 (do !
                 [_ (java/lang/AutoCloseable::close input)]
                 (wrap output))
            _ (if (n.= ..buffer-size bytes-read)
                (recur (\ binary.monoid compose output buffer))
                (do !
                  [chunk (\ io.monad wrap (binary.slice 0 (.nat bytes-read) buffer))]
                  (recur (\ binary.monoid compose output chunk)))))))))

  (def: (upload uri content)
    (case identity
      #.None
      (\ io.monad wrap (exception.throw ..no-credentials [address]))
      
      (#.Some [user password])
      (do (try.with io.monad)
        [connection (|> (format address uri)
                        java/net/URL::new
                        java/net/URL::openConnection)
         #let [connection (:coerce java/net/HttpURLConnection connection)]
         _ (java/net/HttpURLConnection::setRequestMethod "PUT" connection)
         _ (java/net/URLConnection::setDoOutput true connection)
         _ (java/net/URLConnection::setRequestProperty "Authorization" (/identity.basic-auth user password) connection)
         stream (java/net/URLConnection::getOutputStream connection)
         _ (java/io/OutputStream::write content stream)
         _ (java/io/OutputStream::flush stream)
         _ (java/lang/AutoCloseable::close stream)
         code (java/net/HttpURLConnection::getResponseCode connection)]
        (case code
          +201 (wrap [])
          _ (\ io.monad wrap (exception.throw ..deployment-failure [code]))))))
  )