Hi Sébastien,
Sébastien Gendre <[email protected]> writes:
Hello everyone,
I was trying to understand how to use `define-configuration`
with some
practice and couldn't understand what goes wrong.
It took for example to write a `luanti-game-configuration` that
could,
when serialized, generate a configuration file for the Luanti
server. I
only started with 1 parameter.
When I try to serialize a configuration instance to a file, the
content
of this file is empty.
Here is the code defining my `luanti-game-configuration`:
;; -*- mode: scheme; -*-
;; Luanti game configuration
(use-modules (gnu services configuration)
(guix gexp))
;; Define the maybe string
(define-maybe string)
Note that this expects a procedure named `serialize-string', which
causes a warning because it doesn’t exist.
;; Define serializer functions
(define (serialize-luanti-admin-name field-name value)
"Serialize the Luanti admin name field for the config
file."
(let ((field-real-name "name"))
#~(string-append #$field-real-name " = " #$value)))
Since you’re only building strings, you don’t need a gexp, nor do
you need the let binding. Here’s a simplified version:
(define (serialize-luanti-admin-name _ value)
"Serialize the Luanti admin name field for the config file."
(string-append "name = " #$value))
;; Define the Luanti Game configuration
(define-configuration luanti-game-configuration
(admin-name
maybe-string
"Username of the administrator."
(serializer serialize-luanti-admin-name)))
This is mixing an optional predicate with a required serializer,
which won’t work right. While it’s boilerplatey, I find that the
best approach is to make one type per field:
(define admin-name? string?)
(define (serialize-admin-name field-name value) "do the right
thing here")
Then use `define-maybe' and the predicate & serializer it expands
to:
(define-maybe admin-name)
(define-configuration luanti-game-configuration
(admin-name maybe-admin-name "Username of the
administrator."))
Within the configuration system, "type" covers both the value
*and* how it’s handled, which can cause problems when you have two
fields which take string, but need different serialize-string
handling -- like one needing to be quoted, or one representing an
INI-style [section] and another a key = value pair. Starting here
gives you clean separation you can merge later, if you like.
;; Define Luanti game configuration file generator
(define (minetest.conf config)
"Return the Luanti config file."
(plain-file "minetest.conf"
(with-output-to-string
(lambda ()
(serialize-configuration
config
luanti-game-configuration-fields)))))
This is the crux of your problem. The `with-output-to-string'
procedure captures any value *printed* and returns it as a string.
Your lambda doesn’t print anything, because
`serialize-configuration' doesn’t print anything, it returns a
value. Since there’s no output, `with-output-to-string' returns
an empty string, and `plain-file' puts that in your minetest.conf.
Whoops.
The procedure should be:
;; Define Luanti game configuration file generator
(define (minetest.conf config)
"Return the Luanti config file."
(mixed-text-file
"minetest.conf"
(serialize-configuration config
luanti-game-configuration-fields)))
Note that if your serializers use gexps, you will need to build
the mixed-text-file to see anything. If you’ve used the (guix)
module in your Guile REPL, you can do this with:
,build (minetest.conf ...)
On a closing note, I’ll share that I’ve also struggled a lot with
the configuration system in Guix, both in its documentation and
implementation. Implementation-wise, the system feels both
barebones and verbose. I’ve run into some stuff with unsatisfying
expressions, like fields whose names aren’t needed, config
subsections, and configurations spanning multiple files. These
all require workarounds which feel more like subverting the system
than using it as intended.
On the documentation side, the Guix manual is an invaluable
reference, but a lot of it feels like just that -- a reference.
It has great "what" and "how" information, but in my opinion,
sorely lacks the contextual "why" and "when" to effectively apply
it.
-- Ian