guile-fediverse/activitypub/collection.scm

collection.scm

1
;;;; Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu>
2
;;;; 
3
;;;; This library is free software; you can redistribute it and/or
4
;;;; modify it under the terms of the GNU Lesser General Public
5
;;;; License as published by the Free Software Foundation; either
6
;;;; version 3 of the License, or (at your option) any later version.
7
;;;; 
8
;;;; This library is distributed in the hope that it will be useful,
9
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
;;;; Lesser General Public License for more details.
12
;;;; 
13
;;;; You should have received a copy of the GNU Lesser General Public
14
;;;; License along with this library; if not, write to the Free Software
15
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
(define-module (activitypub collection)
18
  #:use-module ((activitystreams vocabulary) #:prefix as:)
19
  #:use-module ((activitypub vocabulary) #:prefix as:)
20
  #:use-module (activitystreams ontology)
21
  #:use-module (ice-9 match)
22
  #:use-module (srfi srfi-9)
23
  #:export (alist->collection
24
            make-collection
25
            collection?
26
            collection-element-ref
27
            collection-first
28
            collection-last
29
            collection-size
30
31
            collection
32
            collection-page))
33
34
(define-record-type <collection>
35
  (make-collection element-ref first last size)
36
  collection?
37
  (element-ref collection-element-ref)
38
  (first       collection-first)
39
  (last        collection-last)
40
  (size        collection-size))
41
42
(define (delimited-collection-ref collection start number)
43
  (let loop ((result '()) (id (or start (collection-first collection))))
44
    (let ((element ((collection-element-ref collection) id)))
45
      (if (or (not element) (= (length result) number))
46
          (reverse result)
47
          (loop (cons id result) (assoc-ref element "next"))))))
48
49
(define (previous-page collection start number)
50
  (let loop ((id start) (num 0))
51
    (let ((prev (assoc-ref ((collection-element-ref collection) id) "prev")))
52
      (if (or (not prev) (= num number))
53
          id
54
          (loop prev (+ num 1))))))
55
56
(define (collection-ref collection id)
57
  ((collection-element-ref collection) id))
58
59
(define (page-id collection-id start-id)
60
  (string-append collection-id "?page=true&min_id=" start-id))
61
62
(define (collection-page collection start size collection-id)
63
  (let* ((result (delimited-collection-ref collection start size))
64
         (content (map (lambda (el) (assoc-ref el "content"))
65
                       (map (collection-element-ref collection) result))))
66
    (if (null? result)
67
        (make-as-document
68
          (list as:OrderedCollectionPage)
69
          (list (cons as:id (page-id collection-id start))
70
                (cons as:partOf collection-id)
71
                (cons as:items '())))
72
        (let* ((last (pk (car (reverse result))))
73
               (prev-id (previous-page collection start size))
74
               (next-id (assoc-ref ((collection-element-ref collection) last) "next")))
75
          (make-as-document
76
            (list as:OrderedCollectionPage)
77
            (append
78
              (list (cons as:id (page-id collection-id start))
79
                    (cons as:partOf collection-id)
80
                    (cons as:prev (page-id collection-id prev-id))
81
                    (cons as:items content))
82
              (if next-id
83
                (list (cons as:next (page-id collection-id next-id)))
84
                '())))))))
85
86
(define (collection collection collection-id)
87
  (make-as-document
88
    (list as:OrderedCollection)
89
    (list (cons as:id collection-id)
90
          (cons as:totalItems (collection-size collection))
91
          (cons as:first (page-id collection-id (collection-first collection)))
92
          (cons as:last (page-id collection-id (collection-last collection))))))
93
94
(define (alist->collection lst)
95
  (define (alist->collection-alist lst)
96
    (let loop ((result '()) (lst lst) (prev #f))
97
      (match lst
98
        (() result)
99
        (((id . val) lst ...)
100
         (loop (cons (cons id `(("content" . ,val) ("prev" . ,prev)))
101
                     (if prev
102
                         (cons
103
                           (cons* prev (cons "next" id) (cdr (car result)))
104
                           (cdr result))
105
                         result))
106
               lst
107
               id)))))
108
109
  (let ((lst (alist->collection-alist lst)))
110
    (make-collection
111
      (lambda (id)
112
        (assoc-ref lst id))
113
      (car (car (reverse lst)))
114
      (car (car lst))
115
      (length lst))))
116