From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp10.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms9.migadu.com with LMTPS id eFNbKZXahGT4kQAASxT56A (envelope-from ) for ; Sat, 10 Jun 2023 22:18:29 +0200 Received: from aspmx1.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp10.migadu.com with LMTPS id cIgMKJXahGRBAwEAG6o9tA (envelope-from ) for ; Sat, 10 Jun 2023 22:18:29 +0200 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 5950246FA3 for ; Sat, 10 Jun 2023 22:18:29 +0200 (CEST) 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 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1686428309; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post; bh=cNvjEq03wjpAy3lWvVhDgDHhzq2HA6LEsIfBijDnZHM=; b=UvGdSuWou4j2TJq+s/nmdE0J88FRqQ4tI0U29pmgKgvE8QJw7Ngg8zn10/+VsCh4qs+vcD xZcvn0r2Jj3MhqQBSxELJZV90IztH74QzoHJB6oM3DXqaifIZ/rP9hKtaEt3DVeVeEurii aG2nyoBjHwMaWN+q1vgmo7vNO/RzS8phZAr0nHEQiGSWzdODeYk2VbxAAJOB2diimlwbIu xYtCc+gWiCCS6V9QpwdjaaQfEYNfMbZl1CaQ89CSGLB6HA8pAvFJfW4PCDOo/dvqjpLzO5 HPHU+dstbO0DJvPeP8gRBNzjfJB0rajxbaq9bSJihhSfJJ+YRo3UIzrwJV/WqQ== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=none; dmarc=none; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" ARC-Seal: i=1; s=key1; d=yhetil.org; t=1686428309; a=rsa-sha256; cv=none; b=TfGQygnctmU2cdf4OEqlATo81BKa++hR+d6qhLBqdQMKJL9D91jpdaX9m9RRYKpsYD5RRI ctPv+Nmq9vSrTcvBIvVNwfjbI6MMlTVEvdkcxRkB+CuIUDsjPBj6//X2CbM+Ks6HfQlKhV idnHYOn043jPpkBzi6UbNlvMsSwKD1NzCOof6UZUNWrA7Ty5ZyT4K1y121EFID1/cCULcx czhzmIMIlLYzLJ66qYxhtJrToY0MCgAAmkDp8s+R4t1vV+QrU375BYQ1vfrvrYIjAZ0HoS ohTuCI367od9QeqkO0t9Bm2sQgwhn/AFgLJr9BWw79F/qGB2lbppEtRiQ9b0oA== X-Migadu-Scanner: scn1.migadu.com X-Migadu-Spam-Score: -2.21 Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=none; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" X-Migadu-Queue-Id: 5950246FA3 X-Spam-Score: -2.21 X-TUID: Gid8SWwXebG0 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) -- 2.39.2