Previous: Сервисы Shepherd, Up: Создание служб [Contents][Index]
Some programs might have rather complex configuration files or formats, and
to make it easier to create Scheme bindings for these configuration files,
you can use the utilities defined in the (gnu services configuration)
module.
The main utility is the define-configuration
macro, which you will
use to define a Scheme record type (see Record Overview in GNU
Guile Reference Manual). The Scheme record will be serialized to a
configuration file by using serializers, which are procedures that
take some kind of Scheme value and returns a G-expression
(see G-Expressions), which should, once serialized to the disk, return a
string. More details are listed below.
name
thatcontains the fields found in the clauses.
A clause can have one of the following forms:
(field-name (type default-value) documentation) (field-name (type default-value) documentation serializer) (field-name (type) documentation) (field-name (type) documentation serializer)
field-name is an identifier that denotes the name of the field in the generated record.
type is the type of the value corresponding to field-name; since
Guile is untyped, a predicate procedure—type?
—will be
called on the value corresponding to the field to ensure that the value is
of the correct type. This means that if say, type is package
,
then a procedure named package?
will be applied on the value to make
sure that it is indeed a <package>
object.
default-value is the default value corresponding to the field; if none is specified, the user is forced to provide a value when creating an object of the record type.
documentation is a string formatted with Texinfo syntax which should provide a description of what setting this field does.
serializer is the name of a procedure which takes two arguments, the
first is the name of the field, and the second is the value corresponding to
the field. The procedure should return a string or G-expression
(see G-Expressions) that represents the content that will be serialized
to the configuration file. If none is specified, a procedure of the name
serialize-type
will be used.
A simple serializer procedure could look like this:
(define (serialize-boolean field-name value) (let ((value (if value "true" "false"))) #~(string-append #$field-name #$value)))
In some cases multiple different configuration records might be defined in
the same file, but their serializers for the same type might have to be
different, because they have different configuration formats. For example,
the serialize-boolean
procedure for the Getmail service would have to
be different for the one for the Transmission service. To make it easier to
deal with this situation, one can specify a serializer prefix by using the
prefix
literal in the define-configuration
form. This means
that one doesn’t have to manually specify a custom serializer for
every field.
(define (foo-serialize-string field-name value) …) (define (bar-serialize-string field-name value) …) (define-configuration foo-configuration (label (string) "The name of label.") (prefix foo-)) (define-configuration bar-configuration (ip-address (string) "The IPv4 address for this device.") (prefix bar-))
However, in some cases you might not want to serialize any of the values of
the record, to do this, you can use the no-serialization
literal.
There is also the define-configuration/no-serialization
macro which
is a shorthand of this.
;; Nothing will be serialized to disk. (define-configuration foo-configuration (field (string "test") "Some documentation.") (no-serialization)) ;; The same thing as above. (define-configuration/no-serialization bar-configuration (field (string "test") "Some documentation."))
Sometimes a field should not be serialized if the user doesn’t specify a
value. To achieve this, you can use the define-maybe
macro to define
a “maybe type”; if the value of a maybe type is set to the
disabled
, it will not be serialized.
When defining a “maybe type”, the corresponding serializer for the regular
type will be used by default. For example, a field of type
maybe-string
will be serialized using the serialize-string
procedure by default, you can of course change this by specifying a custom
serializer procedure. Likewise, the type of the value would have to be a
string, unless it is set to the disabled
symbol.
(define-maybe string) (define (serialize-string field-name value) …) (define-configuration baz-configuration (name ;; Nothing will be serialized by default. If set to a string, the ;; `serialize-string' procedure will be used to serialize the string. (maybe-string 'disabled) "The name of this module."))
Like with define-configuration
, one can set a prefix for the
serializer name by using the prefix
literal.
(define-maybe integer (prefix baz-)) (define (baz-serialize-interger field-name value) …)
There is also the no-serialization
literal, which when set means that
no serializer will be defined for the “maybe type”, regardless of its
value is disabled
or not. define-maybe/no-serialization
is a
shorthand for specifying the no-serialization
literal.
(define-maybe/no-serialization symbol) (define-configuration/no-serialization test-configuration (mode (maybe-symbol 'disabled) "Docstring."))
the fields of configuration, a record that has been generated by
define-configuration
. The G-expression can then be serialized to
disk by using something like mixed-text-file
.
fields Type-check fields, a list of field names of
configuration, a configuration record created by
define-configuration
.
A serializer that just returns an empty string. The
serialize-package
procedure is an alias for this.
Once you have defined a configuration record, you will most likely also want to document it so that other people know to use it. To help with that, there are two procedures, both of which are documented below.
documentation, a list of (label fields
sub-documentation ...)
. label should be a symbol and should be
the name of the configuration record. fields should be a list of all
the fields available for the configuration record.
sub-documentation is a (field-name
configuration-name)
tuple. field-name is the name of the field
which takes another configuration record as its value, and
configuration-name is the name of that configuration record.
sub-documentation is only needed if there are nested configuration
records. For example, the getmail-configuration
record (see Почтовые сервисы) accepts a getmail-configuration-file
record in one of its
rcfile
field, therefore documentation for
getmail-configuration-file
is nested in getmail-configuration
.
(generate-documentation `((getmail-configuration ,getmail-configuration-fields (rcfile getmail-configuration-file)) …) 'getmail-configuration)
documentation-name should be a symbol and should be the name of the configuration record.
configuration-symbol Take configuration-symbol, the symbol
corresponding to the name used when defining a configuration record with
define-configuration
, and print the Texinfo documentation of its
fields. This is useful if there aren’t any nested configuration records
since it only prints the documentation for the top-level fields.
As of right now, there is no automated way to generate documentation for
configuration records and put them in the manual. Instead, every time you
make a change to the docstrings of a configuration record, you have to
manually call generate-documentation
or
configuration->documentation
, and paste the output into the
doc/guix.texi file.
Below is an example of a record type created using
define-configuration
and friends.
(use-modules (gnu services) (guix gexp) (gnu services configuration) (srfi srfi-26) (srfi srfi-1)) ;; Turn field names, which are Scheme symbols into strings (define (uglify-field-name field-name) (let ((str (symbol->string field-name))) ;; field? -> is-field (if (string-suffix? "?" str) (string-append "is-" (string-drop-right str 1)) str))) (define (serialize-string field-name value) #~(string-append #$(uglify-field-name field-name) " = " #$value "\n")) (define (serialize-integer field-name value) (serialize-string field-name (number->string value))) (define (serialize-boolean field-name value) (serialize-string field-name (if value "true" "false"))) (define (serialize-contact-name field-name value) #~(string-append "\n[" #$value "]\n")) (define (list-of-contact-configurations? lst) (every contact-configuration? lst)) (define (serialize-list-of-contact-configurations field-name value) #~(string-append #$@(map (cut serialize-configuration <> contact-configuration-fields) value))) (define (serialize-contacts-list-configuration configuration) (mixed-text-file "contactrc" #~(string-append "[Owner]\n" #$(serialize-configuration configuration contacts-list-configuration-fields)))) (define-maybe integer) (define-maybe string) (define-configuration contact-configuration (name (string) "The name of the contact." serialize-contact-name) (phone-number (maybe-integer 'disabled) "The person's phone number.") (email (maybe-string 'disabled) "The person's email address.") (married? (boolean) "Whether the person is married.")) (define-configuration contacts-list-configuration (name (string) "The name of the owner of this contact list.") (email (string) "The owner's email address.") (contacts (list-of-contact-configurations '()) "A list of @code{contact-configuation} records which contain information about all your contacts."))
A contacts list configuration could then be created like this:
(define my-contacts (contacts-list-configuration (name "Alice") (email "alice@example.org") (contacts (list (contact-configuration (name "Bob") (phone-number 1234) (email "bob@gnu.org") (married? #f)) (contact-configuration (name "Charlie") (phone-number 0000) (married? #t))))))
After serializing the configuration to disk, the resulting file would look like this:
[owner] name = Alice email = alice@example.org [Bob] phone-number = 1234 email = bob@gnu.org is-married = false [Charlie] phone-number = 0 is-married = true
Previous: Сервисы Shepherd, Up: Создание служб [Contents][Index]