guile-pastebin/modules/pastebin/data.scm

data.scm

1
;;; SPDX-FileCopyrightText: 2021 Li Ian-Xue (b4283) <b4283@pm.me>
2
;;; SPDX-FileCopyrightText: 2025 Evgeny Pisemsky <mail@pisemsky.site>
3
;;;
4
;;; SPDX-License-Identifier: GPL-3.0-only
5
6
(define-module (pastebin data))
7
8
(use-modules (srfi srfi-9)
9
             (srfi srfi-1)
10
             (ice-9 ftw)
11
             (ice-9 i18n)
12
             (ice-9 regex)
13
             (ice-9 textual-ports))
14
15
(export <pb-data>
16
        <pb-entry>
17
        pb-entry-id
18
        pb-entry-id-valid?
19
        pb-entry-text
20
        pb-data-open
21
        pb-data-close
22
        call-with-dir-as-pb-data
23
        pb-data-get-top
24
        pb-data-new-entry
25
        pb-get-file-path)
26
27
(define-record-type <pb-data>
28
  (make-pb-data dir)
29
  pb-data?
30
  (dir pb-data-dir set-pb-data-dir!))
31
32
(define-record-type <pb-entry>
33
  (make-pb-entry id text)
34
  pb-entry?
35
  (id pb-entry-id set-pb-entry-id!)
36
  (text pb-entry-text set-pb-entry-text!))
37
38
(define (pb-entry-id-valid? id)
39
  (and (= (string-length id) 5)
40
       (string-match "[0-9A-Za-z]{5}" id)
41
       #t))
42
43
;; input: dir: string
44
;; output: <pb-data>
45
(define (pb-data-open dir)
46
  (make-pb-data dir))
47
48
(define (pb-data-close pb-data) #t)
49
50
(define (call-with-dir-as-pb-data dir p)
51
  (let ((pb-data (pb-data-open dir)))
52
    (let ((R (p pb-data)))
53
      (pb-data-close pb-data)
54
      R)))
55
56
(define (pb-get-file-path pb-data filename)
57
  (string-append (pb-data-dir pb-data) "/" filename))
58
59
;; input: <pb-data>, integer
60
;; output: list of <pb-entry>
61
(define (pb-data-get-top pb-data n)
62
  (map
63
   (lambda (filename)
64
     (make-pb-entry
65
      filename
66
      (call-with-input-file (pb-get-file-path pb-data filename)
67
        (lambda (port)
68
          (get-string-all port)))))
69
   (let ((file-ls (list-files pb-data)))
70
     (reverse (take-right file-ls (min n (length file-ls)))))))
71
72
;; input: <pb-data>, text
73
;; output: <pb-entry>
74
(define (pb-data-new-entry pb-data text)
75
  (let* ((next-filename (get-next-filename pb-data))
76
         (path (pb-get-file-path pb-data next-filename)))
77
    (unless (file-exists? path)
78
      (call-with-output-file path
79
        (lambda (port)
80
          (put-string port text))))
81
    (make-pb-entry next-filename text)))
82
83
(define (list-files pb-data)
84
  (scandir (pb-data-dir pb-data)
85
           pb-entry-id-valid?
86
           (lambda (s1 s2)
87
             (string-locale<? s1 s2 (make-locale LC_ALL "C")))))
88
89
(define (get-next-filename pb-data)
90
  (let ((entries (list-files pb-data)))
91
    (if (null? entries)
92
        "00000"
93
        (get-next-5digit (last entries)))))
94
95
(define (get-next-5digit str)
96
  (let A ((add #t)
97
          (char-int-ls (reverse (map char->integer (string->list str))))
98
          (result '()))
99
    (if (null? char-int-ls)
100
        (list->string (map integer->char result))
101
        (if add
102
            (let* ((hd (car char-int-ls)) (tl (cdr char-int-ls)))
103
              (if (= hd #x7A)
104
                  (A #t tl (cons #x30 result))
105
                  (A #f tl (cons (case hd
106
                                   ((#x39) #x41)
107
                                   ((#x5A) #x61)
108
                                   (else (1+ hd))) result))))
109
            (A #f '() (append (reverse char-int-ls) result))))))
110