utils: Add ini-file.

* modules/rosenthal/utils/file.scm (ini-file): New procedure.
* modules/rosenthal/services/monitoring.scm (grafana-shepherd)
* modules/rosenthal/services/web.scm (forgejo-shepherd-service): Use it.
* modules/rosenthal/utils/serializers/ini.scm: Delete module.
This commit is contained in:
Hilton Chain 2026-02-25 14:27:14 +08:00
parent 0b14f2b0aa
commit d6fac7ef25
No known key found for this signature in database
GPG Key ID: ACC66D09CA528292
4 changed files with 27 additions and 173 deletions

View File

@ -6,8 +6,8 @@
#:use-module (guix gexp)
#:use-module (guix modules)
#:use-module (guix records)
#:use-module (rosenthal utils file)
#:use-module (rosenthal utils predicates)
#:use-module (rosenthal utils serializers ini)
#:use-module (rosenthal utils serializers yaml)
;; Guix System
#:use-module (gnu system shadow)
@ -99,7 +99,7 @@
(file-like grafana-bin)
"")
(config
ini-config
gexp
"")
(postgresql-password-file
string
@ -158,13 +158,7 @@
(define grafana-shepherd
(match-record-lambda <grafana-configuration>
(grafana config shepherd-provision shepherd-requirement auto-start?)
(let ((config-file
(computed-file "grafana.ini"
(with-extensions (list guile-ini guile-lib guile-smc)
#~(begin
(use-modules (srfi srfi-26) (ini))
(call-with-output-file #$output
(cut scm->ini '#$config #:port <>)))))))
(let ((config-file (ini-file "grafana.ini" config)))
(list (shepherd-service
(provision shepherd-provision)
(requirement `(loopback postgresql user-processes

View File

@ -6,8 +6,8 @@
#:use-module (guix gexp)
#:use-module (guix modules)
#:use-module (guix records)
#:use-module (rosenthal utils file)
#:use-module (rosenthal utils predicates)
#:use-module (rosenthal utils serializers ini)
#:use-module (rosenthal utils serializers yaml)
;; Guix System
#:use-module (gnu system privilege)
@ -281,7 +281,7 @@ reload its configuration file."))
(list-of-file-likes (list git git-lfs))
"@code{git} and extension packages to install.")
(config
ini-config
gexp
"")
(postgresql-password-file
string
@ -331,13 +331,7 @@ reload its configuration file."))
(define forgejo-shepherd-service
(match-record-lambda <forgejo-configuration>
(forgejo config)
(let ((config-file
(computed-file "forgejo.ini"
(with-extensions (list guile-ini guile-lib guile-smc)
#~(begin
(use-modules (srfi srfi-26) (ini))
(call-with-output-file #$output
(cut scm->ini '#$config #:port <>)))))))
(let ((config-file (ini-file "forgejo.ini" config)))
(list (shepherd-service
(documentation "Run Forgejo.")
(provision '(forgejo))

View File

@ -6,9 +6,13 @@
#:use-module (ice-9 textual-ports)
;; Utilities
#:use-module (guix gexp)
;; Guix packages
#:use-module (gnu packages guile-xyz)
#:export (computed-substitution-with-inputs
file-content
hidden-desktop-entry))
hidden-desktop-entry
ini-file))
;; XXX: substitute* doesn't fully support Unicode:
;; https://codeberg.org/guix/guix/src/commit/a88d6a45e422cede96d57d7a953439dc27c6a50c/guix/build/utils.scm#L964
@ -45,3 +49,19 @@
(substitute* #$output
(("^\\[Desktop Entry\\].*" all)
(string-append all "NoDisplay=true\n")))))))
;;;
;;; Serializers.
;;;
;; https://github.com/artyom-poptsov/guile-ini
(define (ini-file name exp)
"Return file-like object NAME, serialized from G-expression EXP in INI
format."
(computed-file name
(with-extensions (list guile-ini guile-lib guile-smc)
#~(begin
(use-modules (srfi srfi-26) (ini))
(call-with-output-file #$output
(cut scm->ini #$exp #:port <>))))))

View File

@ -1,154 +0,0 @@
;;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Copyright © Copyright © 2022 Andrew Tropin <andrew@trop.in>
(define-module (rosenthal utils serializers ini)
;; Guile builtins
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
;; Utilities
#:use-module (guix diagnostics)
#:use-module (guix gexp)
#:use-module (guix packages)
#:use-module (guix ui)
#:use-module (rosenthal utils serializers)
;; Guix System - services
#:use-module (gnu services configuration)
#:export (ini-serialize
ini-print
ini-merge
ini-append
serialize-ini-config
ini-config?))
(define ini-config? list?)
(define sample-ini
`((global ((daemon)
(log . file)))
(http ((host . 127.0.0.1)
(port . 1234)))))
(define (format-ini-section section)
(match section
('global "")
(name (format #f "[~a]\n" (symbol->string name)))))
(define* (ini-serialize
config
#:key
(equal-string " = ")
(format-ini-section format-ini-section))
"For global properties use global section, the properties will be added to the
beginning of the list before any section, this behavior can be adjusted with
FORMAT-INI-SECTION argument.
@lisp
`((global ((daemon)
(log . file)))
(http ((host . 127.0.0.1)
(port . 1234))))
@end lisp
would yeld
@example
@end example
"
(define (serialize-ini-term term)
(match term
(#t "true")
(#f "false")
((? symbol? e) (symbol->string e))
((? number? e) (number->string e))
((? string? e) (object->string e))
((lst ...)
(raise (formatted-message
(G_ "INI term should be a non-list value (string, \
boolean, number, symbol, or gexp). Provided term is:\n ~a") lst)))
(e e)))
(define (serialize-ini-properties properties)
(unless (alist? properties)
(raise (formatted-message
(G_ "INI properties should be an alist, \
but provided value is:\n ~a") properties)))
(append-map
(match-lambda
((? gexp? e)
(list e "\n"))
((k)
(list (serialize-ini-term k) equal-string "\n"))
((k . v)
(list (serialize-ini-term k) equal-string
(serialize-ini-term v) "\n")))
properties))
(define (serialize-ini-section section)
(match section
((name properties)
(append
(list (format-ini-section name))
(serialize-ini-properties properties)))
(e
(raise (formatted-message
(G_ "INI section should be a list containing a section name as \
the first element and alist of properties as the second, but provided value \
is:\n~a") e)))))
;; TODO: serialize global section before all other sections.
(append-map
(lambda (expr)
(append
(match expr
((? gexp? e) (list e))
(e (serialize-ini-section e)))
(list "\n")))
config))
(define (ini-merge ini1 ini2)
"Combine to INIs. Naive quadratic implementation, which can be rediculously
slow."
(define keys-to-merge
(fold
(match-lambda*
(((k v) acc) (if (assoc-ref ini2 k) (cons k acc) acc))
(((? gexp? e) acc) acc))
'() ini1))
(define enriched-ini1
(fold-right
(match-lambda*
(((k v) acc)
(cons
(cons k (list (append v (car (or (assoc-ref ini2 k) '(()))))))
acc))
(((? gexp? e) acc) (cons e acc)))
'()
ini1))
(define stripped-ini2
(remove (match-lambda
((k . v) (memq k keys-to-merge))
(e #f))
ini2))
(append enriched-ini1 stripped-ini2))
(define (ini-append x acc)
(ini-merge acc x))
(define serialize-ini-config ini-serialize)
;; (display
;; (merge-ini
;; '((section1 ((k1 . v1)))
;; (section2 ((k4 . v4))))
;; '((section1 ((k2 . v2)
;; (k3 . v3)))
;; (section3 ((k5 . v5))))))
;; (cdr '(a ((k . v) (k2 . v2))))
(define (ini-print ini)
"Prints generated INI, useful for debugging."
(display (apply string-append (ini-serialize ini))))