Précédent: , Monter: Définir des services   [Table des matières][Index]


12.18.5 Configurations complexes

Certains programmes peuvent avoir des fichiers ou des formats de configuration complexes, et pour rendre plus simple la création de liaisons Scheme pour ces fichiers de configuration, vous pouvez utiliser les fonction auxiliaires définies dans le modules (gnu services configuration).

L’utilitaire principal est la macro define-configuration, que vous utiliserez pour définir un type d’enregistrement Scheme (voir Record Overview dans GNU Guile Reference Manual). L’enregistrement Scheme sera sérialisé en un fichier de configuration par des sérialiseurs, qui sont des procédures qui prennent une valeur Scheme et renvoient une G-expression (voir G-Expressions), ce qui devrait, une fois sérialisé sur le disque, renvoyer une chaine. Plus de détails se trouvent plus bas.

Macro :define-configuration name clause1 clause2 …

Create a record type named name that contains the fields found in the clauses.

Une clause peut avoir l’une des formes suivantes :

(field-name
 (type default-value)
 documentation)

(field-name
 (type default-value)
 documentation
 (serializer serializer))

(nom-du-champ
 (type)
 documentation)

(field-name
 (type)
 documentation
 (serializer serializer))

(field-name
 (type)
 documentation
 (sanitizer sanitizer)

(field-name
 (type)
 documentation
 (sanitizer sanitizer)
 (serializer serializer))

nom-du-champ est un identifiant qui dénote le nom du champ dans l’enregistrement généré.

type est le type de valeur correspondant à nom-du-champ ; comme Guile n’est pas typé, une procédure de prédicat — type? — sera appelée avec la valeur correspondant au champ pour s’assurer que la valeur est du type correct. Cela signifie que si type est package, alors une procédure nommée package? sera appliquée sur la valeur pour s’assurer que c’est bien un objet <package>.

valeur-par-défaut est la valeur par défaut correspondant au champ ; si aucune n’est spécifiée, l’utilisateur ou l’utilisatrice doit fournir une valeur à la création d’un objet de ce type d’enregistrement.

documentation est une chaine formatée avec la syntaxe de Texinfo qui fournit une description de ce que ce champ de configuration signifie.

sanitizer is a procedure which takes one argument, a user-supplied value, and returns a “sanitized” value for the field. If no sanitizer is specified, a default sanitizer is used, which raises an error if the value is not of type type.

An example of a sanitizer for a field that accepts both strings and symbols looks like this:

(define (sanitize-foo value)
  (cond ((string? value) value)
        ((symbol? value) (symbol->string value))
        (else (error "bad value"))))

sérialiseur est le nom d’une procédure qui prend deux arguments, le premier est le nom du champ, et le second est la valeur correspondant au champ. La procédure devrait renvoyer une chaine ou une G-expression (voir G-Expressions) qui représente le contenu qui sera sérialisé dans le fichier de configuration. Si aucun n’est spécifié, une procédure nommée serialize-type sera utilisée.

Une simple procédure de sérialisation pourrait ressembler à ceci :

(define (serialize-boolean field-name value)
  (let ((value (if value "true" "false")))
    #~(string-append #$field-name #$value)))

Dans certains cas plusieurs enregistrements de configuration différents peuvent être définis dans le même fichier, mais leurs sérialiseurs pour le même type peuvent devoir être différents, à cause de formats de configuration différents. Par exemple, la procédure serialize-boolean pour le service Getmail doit être différente de celle pour le service Transmission. Pour faciliter cette situation, on peut spécifier un préfixe pour sérialiseur en utilisant le littéral prefix dans la forme define-configuration. Cela signifie qu’on n’a pas à spécifier manuellement un sérialiseur personnalisé pour chaque champ.

(define (toto-serialize-string field-name value)
  )

(define (bar-serialize-string field-name value)
  )

(define-configuration toto-configuration
  (label
   (string)
   "Le nom de l'étiquette.")
  (prefix toto-))

(define-configuration titi-configuration
  (ip-address
   (string)
   "L'adresse IPv4 de cet appareil.")
  (prefix titi-))

Cependant, dans certains cas vous ne voudrez pas sérialiser les valeurs de l’enregistrement. Pour cela, vous pouvez utiliser le littéral no-serialization. Il y a aussi la macro define-configuration/no-serialization qui est un raccourci.

;; Rien ne sera sérialisé sur le disque.
(define-configuration toto-configuration
  (field
   (string "test")
   "De la documentation.")
  (no-serialization))

;; Comme au-dessus.
(define-configuration/no-serialization titi-configuration
  (field
   (string "test")
   "De la documentation."))
Macro :define-maybe type

Parfois un champ ne devrait pas être sérialisé si l’utilisateur ou l’utilisatrice de spécifie par de valeur. Pour cela, vous pouvez utiliser la macro define-maybe pour définir un « type peut-être » ; si la valeur d’un type peut-être n’est pas spécifiée, ou est indiquée à %unset-value, elle n’est pas sérialisée.

Lorsque vous définissez un « type peut-être », le sérialiseur correspondant au type normal sera utilisé par défaut. Par exemple, un champ de type maybe-string sera sérialisé avec la procédure serialize-string par défaut, vous pouvez évidemment changer cela en spécifiant une procédure de sérialisation personnalisée. De la même manière, le type de la valeur doit être une chaine, ou la valeur doit rester non spécifiée.

(define-maybe string)

(define (serialize-string field-name value)
  )

(define-configuration tata-configuration
  (name
   ;; Si c'est une chaine, la procédure « serialize-string » sera utilisée
   ;; pour sérialiser la chaine.  Sinon ce champ n'est pas sérialisé
   maybe-string
   "Le nom de ce module."))

Comme avec define-configuration, on peut indiquer un préfixe pour le nom de sérialiseur en utilisant le littéral prefix.

(define-maybe integer
  (prefix tata-))

(define (tata-serialize-integer field-name value)
  )

Il y a aussi le littéral no-serialization, qui s’il est utilisé indique qu’aucun sérialiseur ne sera défini pour le « type peut-être », indépendamment de si la valeur est indiquée ou non. define-maybe/no-serialization est un raccourci pour spécifier le littéral no-serialization.

(define-maybe/no-serialization symbol)

(define-configuration/no-serialization test-configuration
  (mode
   maybe-symbol
   "Docstring."))
Fonction :maybe-value-set? value

Prédicat pour vérifier si l’utilisateur ou l’utilisatrice a spécifié la valeur d’un champ peut-être.

Fonction :serialize-configuration configuration fields

Return a G-expression that contains the values corresponding to 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.

Fonction :empty-serializer field-name value

Un sérialiseur qui renvoie juste une chaine vide. La procédure serialize-package est un alias pour cette procédure.

Une fois que vous avez défini un enregistrement de configuration, vous voudrez aussi sans doute le documenter pour que d’autres personnes sachent comment l’utiliser. Pour vous aider, il y a deux procédure qui sont documentées plus bas.

Fonction :generate-documentation documentation documentation-name

Generate a Texinfo fragment from the docstrings in 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.

sous-documentation est un tuple (nom-de-champ nom-de-configuration). nom-de-champ est le nom du champ qui prend un autre enregistrement de configuration comme valeur, et nom-de-configuration est le nom de cet enregistrement de configuration.

sous-documentation n’est requis que s’il y a des enregistrements de configuration imbriqués. Par exemple, l’enregistrement getmail-configuration (voir Services de courriels) accepte un enregistrement getmail-configuration-file dans l’un de ses champs rcfile, donc la documentation de getmail-configuration-file est imbriquée dans getmail-configuration.

(generate-documentation
  `((getmail-configuration ,getmail-configuration-fields
     (rcfile getmail-configuration-file))
    )
  'getmail-configuration)

nom-de-documentation devrait être un symbole et devrait être le nom de l’enregistrement de configuration.

Fonction :configuration->documentation 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.

Actuellement, il n’y a pas de manière automatique de générer la documentation pour les enregistrements de configuration et les ajouter au manuel. Au lieu de cela, chaque fois que vous faites un changement dans les docstrings d’un enregistrement de configuration, vous devez appeler manuellement generate-documentation ou configuration->documentation et coller la sortie dans le fichier doc/guix.texi.

Ci-dessous se trouve un exemple d’un type d’enregistrement créé avec define-configuration et compagnie.

(use-modules (gnu services)
             (guix gexp)
             (gnu services configuration)
             (srfi srfi-26)
             (srfi srfi-1))

;; Transforme les noms de champs, qui sont des symboles Scheme, en chaines
(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)
   "Le nom du contact."
   serialize-contact-name)
  (phone-number
   maybe-integer
   "Le numéro de téléphone de la personne.")
  (email
   maybe-string
   "L'adresse de courriel de la personne.")
  (married?
   (boolean)
   "Indique si la personne est mariée."))

(define-configuration contacts-list-configuration
  (name
   (string)
   "Le nom du propriétaire de cette liste de contacts.")
  (email
   (string)
   "L'adresse de courriel du propriétaire.")
  (contacts
   (list-of-contact-configurations '())
   "Une liste d'enregistrements @code{contact-configuation} qui contiennent
des informations sur tous vos contacts."))

Une configuration de liste de contacts pourrait alors être créée de cette manière :

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

Après la sérialisation de la configuration sur le disque, le fichier qui en résulte ressemble à ceci :

[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

Précédent: Services Shepherd, Monter: Définir des services   [Table des matières][Index]