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
;;; Copyright (c) 2026 Nicholas Vermeulen
;;; SPDX-License-Identifier: AGPL-3.0-or-later
;; pkg.lisp — a registry-less package manager (Phase 5.2, pure Lisp).
;;
;; A PACKAGE is any git repository with a `package.lisp` manifest at its
;; root — one alist:
;; ((name "mylib") (version "0.1.0") (main "mylib.lisp")
;; (deps ("https://github.com/user/otherlib" ...))) ; deps optional
;;
;; Install = `git clone` into ~/.rusty/packages/<name>. Any URL git
;; understands works — https, ssh, file:// — so there is no central
;; registry to stand up or trust (the Go-modules lesson); a hosted index
;; can layer on later without changing this format.
;;
;; (pkg-install url [tag]) clone (optionally at a tag) + install deps
;; (pkg-load name) load an installed package's main file
;; (require-package url [tag]) install-if-missing, then load
;; (pkg-list) / (pkg-remove name)
(define (pkg-root)
(string-append (shell "printf $HOME") "/.rusty/packages"))
(define (pkg-dir name) (string-append (pkg-root) "/" name))
(define (pkg-manifest-path dir) (string-append dir "/package.lisp"))
(define (pkg-installed? name) (file-exists? (pkg-dir name)))
(define (pkg-read-manifest dir)
(eval-string (string-append "(quote " (file-read (pkg-manifest-path dir)) ")")))
(define (pkg-get m key default)
(let ((hit (assoc key m))) (if hit (cadr hit) default)))
(define (pkg-strip-git s)
(let ((n (string-length s)))
(if (and (>= n 4) (equal? (substring s (- n 4) n) ".git"))
(substring s 0 (- n 4))
s)))
(define (pkg-url-name url)
(let ((parts (string-split url "/")))
(pkg-strip-git (nth parts (- (length parts) 1)))))
(define (pkg-install url . opt)
(let ((name (pkg-url-name url)))
(if (pkg-installed? name)
(list 'already-installed name)
(begin
(dir-create (pkg-root))
(shell (format "git clone --quiet --depth 1 ~a ~a ~a"
(if (null? opt) "" (format "--branch ~a" (car opt)))
url (pkg-dir name)))
(if (not (file-exists? (pkg-manifest-path (pkg-dir name))))
(begin
(shell (format "rm -rf ~a" (pkg-dir name)))
(error (format "pkg-install: ~a is not a Rusty package (no package.lisp manifest)" url)))
(let ((m (pkg-read-manifest (pkg-dir name))))
(for-each (lambda (dep) (pkg-install dep))
(pkg-get m 'deps '()))
(list 'installed (pkg-get m 'name name)
(pkg-get m 'version "?"))))))))
(define (pkg-load name)
(if (not (pkg-installed? name))
(error (format "pkg-load: ~a is not installed" name))
(let ((m (pkg-read-manifest (pkg-dir name))))
(load (string-append (pkg-dir name) "/"
(pkg-get m 'main (string-append name ".lisp"))))
(list 'loaded (pkg-get m 'name name) (pkg-get m 'version "?")))))
(define (require-package url . opt)
(let ((name (pkg-url-name url)))
(when (not (pkg-installed? name))
(apply pkg-install (cons url opt)))
(pkg-load name)))
(define (pkg-list)
(if (file-exists? (pkg-root))
(map (lambda (n)
(let ((m (pkg-read-manifest (pkg-dir n))))
(list (pkg-get m 'name n) (pkg-get m 'version "?"))))
(dir-list (pkg-root)))
'()))
(define (pkg-remove name)
(if (pkg-installed? name)
(begin (shell (format "rm -rf ~a" (pkg-dir name)))
(list 'removed name))
(list 'not-installed name)))