From patchwork Sat Jun 10 20:10:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Bruno Victal X-Patchwork-Id: 50830 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id E3F7927BBEA; Sat, 10 Jun 2023 21:18:30 +0100 (BST) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-2.9 required=5.0 tests=BAYES_00,MAILING_LIST_MULTI, SPF_HELO_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id 5CBCF27BBE2 for ; Sat, 10 Jun 2023 21:18:29 +0100 (BST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q852D-0000e5-22; Sat, 10 Jun 2023 16:18:05 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q852B-0000dG-7H for guix-patches@gnu.org; Sat, 10 Jun 2023 16:18:03 -0400 Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1q852A-00062f-V2 for guix-patches@gnu.org; Sat, 10 Jun 2023 16:18:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1q852A-0006zh-QZ for guix-patches@gnu.org; Sat, 10 Jun 2023 16:18:02 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#63985] [PATCH RFC v2 5/5] services: configuration: New generic-ini module. Resent-From: Bruno Victal Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Sat, 10 Jun 2023 20:18:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 63985 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 63985@debbugs.gnu.org Cc: Bruno Victal Received: via spool by 63985-submit@debbugs.gnu.org id=B63985.168642822726810 (code B ref 63985); Sat, 10 Jun 2023 20:18:02 +0000 Received: (at 63985) by debbugs.gnu.org; 10 Jun 2023 20:17:07 +0000 Received: from localhost ([127.0.0.1]:36384 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1q851G-0006yJ-Si for submit@debbugs.gnu.org; Sat, 10 Jun 2023 16:17:07 -0400 Received: from smtpm3.myservices.hosting ([185.26.105.234]:57996) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1q851C-0006xe-E3 for 63985@debbugs.gnu.org; Sat, 10 Jun 2023 16:17:04 -0400 Received: from mail1.netim.hosting (unknown [185.26.106.173]) by smtpm3.myservices.hosting (Postfix) with ESMTP id C0C96210D0 for <63985@debbugs.gnu.org>; Sat, 10 Jun 2023 22:17:01 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by mail1.netim.hosting (Postfix) with ESMTP id CF18D8009C; Sat, 10 Jun 2023 22:11:43 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at mail1.netim.hosting Received: from mail1.netim.hosting ([127.0.0.1]) by localhost (mail1-2.netim.hosting [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id fIACzOTXktnh; Sat, 10 Jun 2023 22:11:43 +0200 (CEST) Received: from guix-nuc.home.arpa (unknown [10.192.1.83]) (Authenticated sender: lumen@makinata.eu) by mail1.netim.hosting (Postfix) with ESMTPSA id 160B68009B; Sat, 10 Jun 2023 22:11:43 +0200 (CEST) From: Bruno Victal Date: Sat, 10 Jun 2023 21:10:56 +0100 Message-Id: <59632edef13e9ed3f922e67142a6288e995c5bef.1686427611.git.mirai@makinata.eu> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches Implements a ‘serialize-ini-configuration’ procedure for serializing record-types defined with define-configuration into generic INI files. * gnu/services/configuration/generic-ini.scm: New module. * tests/services/configuration/generic-ini.scm: Add tests for new module. * Makefile.am: Register tests. * gnu/local.mk: Register module. --- Makefile.am | 1 + gnu/local.mk | 1 + gnu/services/configuration/generic-ini.scm | 129 +++++++++++++++++++ tests/services/configuration/generic-ini.scm | 103 +++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 gnu/services/configuration/generic-ini.scm create mode 100644 tests/services/configuration/generic-ini.scm diff --git a/Makefile.am b/Makefile.am index ab901df757..8dc9a3438b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -552,6 +552,7 @@ SCM_TESTS = \ tests/services.scm \ tests/services/file-sharing.scm \ tests/services/configuration.scm \ + tests/services/configuration/generic-ini.scm \ tests/services/lightdm.scm \ tests/services/linux.scm \ tests/services/telephony.scm \ diff --git a/gnu/local.mk b/gnu/local.mk index ce16d37e2b..a05d70aff3 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -670,6 +670,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/cgit.scm \ %D%/services/ci.scm \ %D%/services/configuration.scm \ + %D%/services/configuration/generic-ini.scm \ %D%/services/cuirass.scm \ %D%/services/cups.scm \ %D%/services/databases.scm \ diff --git a/gnu/services/configuration/generic-ini.scm b/gnu/services/configuration/generic-ini.scm new file mode 100644 index 0000000000..88d145bc50 --- /dev/null +++ b/gnu/services/configuration/generic-ini.scm @@ -0,0 +1,129 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2023 Bruno Victal +;;; +;;; 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 . + +(define-module (gnu services configuration generic-ini) + #:use-module (gnu services configuration) + #:use-module (guix gexp) + #:use-module (srfi srfi-171) + #:use-module (ice-9 match) + #:export (ini-entry? + list-of-ini-entries? + + serialize-ini-configuration)) + +;;; +;;; Generic INI serializer +;;; + + +;;; +;;; 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- 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. +;; 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’. +;; + +(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 “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 (base-transducer config) 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)))) + +;; FIXME:RFC: +;; generic-ini- prefixed serializing procs? +;; (perhaps prefixed as generic-ini: ?) +;; Example procedures: +;; +(define* (generic-ini-serialize-string field-name value #:key section) + (list section field-name value)) + +;; field-name-transform can be used to “uglify” a field-name, +;; e.g. want-ipv6? -> want_ipv6 +(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"))) + + +;;; FIXME: delete this before inclusion, these are notes for the first RFC. +;;; +;;; Left out for now (but readily extendable): +;;; * Custom leading (presumed to be whitespace) characters for entries +;;; à la gitconfig, mostly pretty-printing purposes +;;; * Configurable delimiter (\n, \r\n, \0, ...) +;;; * Configurable Key-value separator (this is usually =) diff --git a/tests/services/configuration/generic-ini.scm b/tests/services/configuration/generic-ini.scm new file mode 100644 index 0000000000..3bdf0e12c2 --- /dev/null +++ b/tests/services/configuration/generic-ini.scm @@ -0,0 +1,103 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2023 Bruno Victal +;;; +;;; 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 . + +(define-module (tests services configuration generic-ini) + #:use-module (gnu services configuration) + #:use-module (gnu services configuration generic-ini) + #:use-module (guix diagnostics) + #:use-module (guix gexp) + #:use-module (guix store) + #:autoload (guix i18n) (G_) + #:use-module (srfi srfi-34) + #:use-module (srfi srfi-64) + #:use-module (srfi srfi-71)) + +;;; Tests for the (gnu services configuration generic-ini) module. + +(test-begin "generic-ini serializer") + + +(define expected-output "\ +[ranch] +shepherd=Emma + +[shed] +enabled=true +capacity=50 +production=wool +") + + +;;; +;;; Serializers +;;; +(define (strip-trailing-?-character field-name) + "Drop rightmost '?' character" + (let ((str (symbol->string field-name))) + (if (string-suffix? "?" str) + (string->symbol (string-drop-right str 1)) + field-name))) + +(define* (serialize-string field-name value #:key section) + (list section field-name value)) + +(define* (serialize-number field-name value #:key section) + (list section field-name (number->string value))) + +(define* (serialize-boolean field-name value #:key section) + (list section (strip-trailing-?-character field-name) + (if value "true" "false"))) + + +;;; +;;; Record-type definition +;;; + +(define-configuration foo-configuration + (production + (string "wool") + "Lorem Ipsum …" + (serializer-options '(#:section shed))) + + (capacity + (number 50) + "Lorem Ipsum …" + (serializer-options '(#:section shed))) + + (enabled? + (boolean #t) + "Lorem Ipsum …" + (serializer-options '(#:section shed))) + + (shepherd + (string "Emma") + "Lorem Ipsum …" + (serializer-options '(#:section ranch)))) + +(test-equal "Well-formed INI output from serialize-ini" + expected-output + ;; Serialize the above into a string, properly resolving any potential + ;; nested G-Exps as well. + (let* ((serialized-ini + (serialize-ini-configuration (foo-configuration) + foo-configuration-fields)) + (lowered conn (with-store store + ((lower-gexp serialized-ini) store)))) + (eval (lowered-gexp-sexp lowered) (current-module)))) + +(test-end)