changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/lib/net/fetch.lisp

changeset 123: a4ed30cbe083
parent: 4ba88cac5bc7
child: f85a2fac1ed7
author: ellis <ellis@rwest.io>
date: Sun, 24 Dec 2023 18:19:33 -0500
permissions: -rw-r--r--
description: data testing, added ical and vcard formats
1 (in-package :net/fetch)
2 
3 (define-condition invalid-path-error (error)
4  ((text :initarg :text :reader text)))
5 
6 (defun download (url output)
7  (multiple-value-bind (content-or-stream status header tk stream must-close status-string)
8  (drakma:http-request url :want-stream t :external-format-out :utf-8)
9  (values
10  (let ((val
11  (if (= status 200)
12  (with-open-file (file output
13  :direction :output
14  :if-does-not-exist :create
15  :if-exists :supersede
16  :element-type '(unsigned-byte 8))
17  (do ((b (read-byte stream nil nil) (read-byte stream nil nil)))
18  ((not b))
19  (write-byte b file))
20  output
21  )
22  nil)))
23  (when must-close (close stream))
24  (or content-or-stream tk header)
25  val)
26  status
27  status-string)))
28 
29 (defun split-file-path (path)
30  (let ((pos-last-slash (1+ (position #\/ path :from-end t))))
31  (list (subseq path 0 pos-last-slash)
32  (subseq path pos-last-slash))))
33 
34 (defun split-uri-string (uri-string)
35  (let ((pu (puri:parse-uri uri-string)))
36  (cons (puri:uri-host pu) (split-file-path (puri:uri-path pu)))))
37 
38 (defun condition-path (path)
39  "Abuse puri:parse-uri to strip possible get args from path"
40  (let ((p (puri:parse-uri path))) (puri:uri-path p)))
41 
42 (defun is-file (path)
43  (handler-case (probe-file path)
44  (type-error (e) #+sbcl (declare (ignore e)) (error 'invalid-path-error
45  :text (format nil "Invalid path: ~A" path)))))
46 
47 (defun %fetch (url-or-path &key (cache t)
48  (dir "vega/")
49  (flush nil))
50  (cond
51  ((is-file (condition-path url-or-path)) (condition-path url-or-path))
52  ((is-file (condition-path (concatenate 'string dir url-or-path)))
53  (condition-path (concatenate 'string dir url-or-path)))
54  ((puri:parse-uri url-or-path)
55  (let* ((tmp-pathname (split-uri-string url-or-path))
56  (file-pathstring (format nil "~{~A~^~}" (if dir (cons dir tmp-pathname) tmp-pathname)))
57  (file-pathname (ensure-directories-exist
58  file-pathstring)))
59  (if flush
60  (when (is-file file-pathname) (delete-file file-pathname))
61  (if (and cache (probe-file file-pathname))
62  (values file-pathname 200 "OK")
63  (handler-case (download url-or-path file-pathname)
64  (drakma:parameter-error ()
65  (values nil 404 "Parameter Error")
66  ))))))
67  (t (values nil 404 "Not file of url"))))
68 
69 (defun fetch (url-or-path
70  &key
71  (dir "vega/")
72  (external-format :utf-8)
73  (cache t)
74  (stream nil)
75  (flush nil))
76  "Fetch file from ~url-or-location~ if not cached in ~dir~
77 stores the file in the location specified by dir if url or file is url the file
78 is stored in ~dir~/~uri-host~/~uri-path~.
79 
80 Note that it is important to ensure that dir and subdir if used end in a /
81 
82 -return: path to file or stream if :stream parameter is passed
83 -arguments:
84  - url-or-path: <string> pathname or url string identifying file to be fetched.
85  - stream: resuests that fetch returns a stream
86  - cache: <T|NIL> if T looks for file in -dir and uses that as source if NIL then the a fresh copy of the file is fetched
87  - dir: location to store fetched file, default location is in the sample directory in the top level of the clml source tree.
88  - flush: if T fetch does not download the file it deletes the existing file.
89 "
90  (let ((fetched-path (%fetch url-or-path :dir dir :cache cache :flush flush)))
91  (if (not fetched-path)
92  nil
93  (if stream
94  (open fetched-path :direction :input :external-format external-format)
95  fetched-path))))