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
| | ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services configuration generic-ini)
#:use-module (gnu services configuration)
#:use-module (guix gexp)
#:use-module (srfi srfi-171)
#:use-module (ice-9 match)
#:export (ini-entry?
list-of-ini-entries?
serialize-ini-configuration))
;;;
;;; Generic INI serializer
;;;
\f
;;;
;;; Predicates
;;;
;; This is the same format used in SRFI-233 but without comment support.
(define ini-entry?
(match-lambda
(((? symbol?) (? symbol?) (? string?)) #t)
(_ #f)))
(define list-of-ini-entries?
(list-of ini-entry?))
;;
;; Overall design document
;;
;; This module implements a generic INI serializer for a record-type defined
;; using define-configuration.
;; It expects that the serialize-<type> procedures return a list with
;; three elements of the form:
;; (list section key value)
;; Where ‘section’ and ‘key’ are symbols and ‘value’ is a string.
;; The fields within define-configuration do not have to be ordered in,
;; any way whatsoever as the ‘serialize-ini’ will group them up automatically.
;; This implies that no assumptions should be made regarding the order of the
;; values in the serializied INI output.
;;
;; Additional notes:
;; Q: Why not replace rcons with string-append and forego the ungexp-splice?
;; A: The transduction happens outside of the G-Exp while the final string-append
;; takes place in the G-Exp.
;;
;; Debugging tips: Open a REPL and try one transducer at a time from
;; ‘ini-transducer’.
;;
(define (add-section-header partition)
(let ((header (caar partition)))
(cons (list header)
partition)))
(define serializer
(match-lambda
((section)
#~(format #f "[~a]~%" '#$section))
((section key value)
#~(format #f "~a=~a~%" '#$key #$value))
;; Used for the newline between sections.
('*section-separator* "\n")))
(define ini-transducer
(compose (tpartition car)
(tmap add-section-header)
(tadd-between '(*section-separator*))
tconcatenate
(tmap serializer)))
;; A “first-pass” serialization is performed and sorted in order
;; to group up the fields by “section” before passing through the
;; transducer.
(define (serialize-ini-configuration config fields)
(let* ((srfi-233-IR
;; First pass: “serialize” into a (disordered) list of
;; SRFI-233 entries.
(list-transduce (base-transducer config) rcons fields))
(comparator (lambda (x y)
;; Sort the SRFI-233 entries by section.
(string<=? (symbol->string (car x))
(symbol->string (car y)))))
(sorted-entries (sort srfi-233-IR comparator)))
#~(string-append
#$@(list-transduce ini-transducer rcons sorted-entries))))
;; FIXME:RFC:
;; generic-ini- prefixed serializing procs?
;; (perhaps prefixed as generic-ini: ?)
;; Example procedures:
;;
(define* (generic-ini-serialize-string field-name value #:key section)
(list section field-name value))
;; field-name-transform can be used to “uglify” a field-name,
;; e.g. want-ipv6? -> want_ipv6
(define* (generic-ini-serialize-boolean field-name value #:key section
(field-name-transform identity))
(list section (field-name-transform field-name)
(if value "true" "false")))
;;; FIXME: delete this before inclusion, these are notes for the first RFC.
;;;
;;; Left out for now (but readily extendable):
;;; * Custom leading (presumed to be whitespace) characters for entries
;;; à la gitconfig, mostly pretty-printing purposes
;;; * Configurable delimiter (\n, \r\n, \0, ...)
;;; * Configurable Key-value separator (this is usually =)
|