unofficial mirror of guix-patches@gnu.org 
 help / color / mirror / code / Atom feed
blob 4f83cce13a7083e94f067fc736e943b26afaa173 5768 bytes (raw)
name: gnu/services/configuration/generic-ini.scm 	 # note: path name is non-authoritative(*)

  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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
 
;;; 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-9)
  #:use-module (srfi srfi-171)
  #:use-module (srfi srfi-171 meta)
  #:use-module (ice-9 match)
  #:export (ini-entry?
            list-of-ini-entries?

            ini-entries
            ini-entries?
            entries

            serialize-ini-configuration
            generic-ini-serialize-string
            generic-ini-serialize-boolean
            generic-ini-serialize-ini-entry
            generic-ini-serialize-list-of-ini-entries))

;;;
;;; 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.
;; For serializing procedures that have to return multiple entries at once,
;; such as encountered when synthesizing configuration from a record object
;; or “escape hatch fields”, it must wrap the result by calling ‘ini-entries’
;; with a list of INI-entries as described above.
;; This is implemented as a constructor for a SRFI-9 record type named
;; “<ini-entries>”.
;;
;; 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’.
;;

;; A “bag” holding multiple ini-entries.
(define-record-type <ini-entries>
  (ini-entries val)
  ini-entries?
  (val entries))

(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 selective version of ‘tconcatenate’ but for ‘<ini-entries>’ objects only.
(define (tconcatenate-ini-entries reducer)
  (case-lambda
    (() '())
    ((result) (reducer result))
    ((result input)
     (if (ini-entries? input)
         (list-reduce (preserving-reduced reducer) result (entries input))
         (reducer result input)))))

;; 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 (compose (base-transducer config)
                                   tconcatenate-ini-entries)
                          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))))

\f
;;;
;;; Serializers
;;;

;; These are “gratuitous” serializers that can be readily used by
;; using the literal (prefix generic-ini-) within define-configuration.

;; Notes: field-name-transform can be used to “uglify” a field-name,
;; e.g. want-ipv6?  ->  want_ipv6
(define* (generic-ini-serialize-string field-name value #:key section
                                       (field-name-transform identity))
  (list section (field-name-transform field-name) value))

(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")))

(define (generic-ini-serialize-ini-entry field-name value)
  value)

(define (generic-ini-serialize-list-of-ini-entries field-name value)
  (ini-entries value))

debug log:

solving 4f83cce13a ...
found 4f83cce13a in https://yhetil.org/guix-patches/e0204bcb23b5a842683ff7929f59af4a3e06f5da.1687816734.git.mirai@makinata.eu/

applying [1/1] https://yhetil.org/guix-patches/e0204bcb23b5a842683ff7929f59af4a3e06f5da.1687816734.git.mirai@makinata.eu/
diff --git a/gnu/services/configuration/generic-ini.scm b/gnu/services/configuration/generic-ini.scm
new file mode 100644
index 0000000000..4f83cce13a

Checking patch gnu/services/configuration/generic-ini.scm...
Applied patch gnu/services/configuration/generic-ini.scm cleanly.

index at:
100644 4f83cce13a7083e94f067fc736e943b26afaa173	gnu/services/configuration/generic-ini.scm

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).