serialization.scm 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. ;;; Disarchive
  2. ;;; Copyright © 2020, 2021 Timothy Sample <samplet@ngyro.com>
  3. ;;;
  4. ;;; This file is part of Disarchive.
  5. ;;;
  6. ;;; Disarchive is free software: you can redistribute it and/or modify
  7. ;;; it under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation, either version 3 of the License, or
  9. ;;; (at your option) any later version.
  10. ;;;
  11. ;;; Disarchive is distributed in the hope that it will be useful,
  12. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with Disarchive. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (disarchive serialization)
  19. #:use-module (gcrypt base64)
  20. #:use-module (ice-9 match)
  21. #:use-module (rnrs bytevectors)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (srfi srfi-2)
  24. #:use-module (srfi srfi-9)
  25. #:use-module (srfi srfi-26)
  26. #:export (<serializer>
  27. make-serializer
  28. make-record-serializer
  29. serialize
  30. deserialize
  31. serdeser))
  32. ;;; Commentary:
  33. ;;;
  34. ;;; This module provides a simple declaritive interface for make
  35. ;;; record serializers and deserializers.
  36. ;;;
  37. ;;; Code:
  38. (define-record-type <serializer>
  39. (make-serializer serialize deserialize)
  40. serializer?
  41. (serialize serializer-serialize)
  42. (deserialize serializer-deserialize))
  43. (define (resolve-serializer serializer)
  44. "If SERIALIZER is a promise, force it; otherwise, return it as-is.
  45. This is useful for recursive data structures."
  46. (if (promise? serializer) (force serializer) serializer))
  47. (define (serialize serializer obj defaults)
  48. "Serialize OBJ using SERIALIZER. If any component of OBJ matches
  49. its counterpart in DEFAULTS, it will be omitted from the result."
  50. (let ((serializer (resolve-serializer serializer)))
  51. (if (and obj serializer)
  52. ((serializer-serialize serializer) obj defaults)
  53. (list obj))))
  54. (define (deserialize serializer sexp defaults)
  55. "Deserialize SEXP using SERIALIZER. Any missing component will be
  56. filled in from DEFAULTS."
  57. (let ((serializer (resolve-serializer serializer)))
  58. (match sexp
  59. ((#f) #f)
  60. (_ (if (and sexp serializer)
  61. ((serializer-deserialize serializer) sexp defaults)
  62. (car sexp))))))
  63. (define* (serdeser serializer obj #:optional defaults)
  64. "Serialize and then deserialize OBJ using SERIALIZER with
  65. DEFAULTS."
  66. (deserialize serializer (serialize serializer obj defaults) defaults))
  67. (define* (make-record-serializer constructor specs
  68. #:key elide-first-field?)
  69. "Create a record serializer for a record type with constructor
  70. CONSTRUCTOR according to SPECS, which provides a specification for
  71. each field of the record. A field specification is a three-element
  72. list containing a name, accessor, and serializer. The value of SPECS
  73. must be a list of field specifications. If ELIDE-FIRST-FIELD? is set,
  74. then the first field will be serialized without a name if possible."
  75. (make-serializer
  76. (lambda (rec defaults)
  77. (let loop ((specs specs) (acc '()) (first? #t))
  78. (match specs
  79. (() (reverse acc))
  80. (((name accessor serializer) . specs-rest)
  81. (let ((value (accessor rec))
  82. (default (and defaults (accessor defaults))))
  83. (if (equal? value default)
  84. (loop specs-rest acc #f)
  85. (let* ((serial-value (serialize serializer value default))
  86. (field (if (and elide-first-field? first?
  87. (match serial-value
  88. (((? (negate pair?))) #t)
  89. (_ #f)))
  90. (car serial-value)
  91. (cons name serial-value))))
  92. (loop specs-rest (cons field acc) #f))))))))
  93. (lambda (sexp defaults)
  94. (let loop ((sexp sexp) (specs specs) (acc '()) (first? #t))
  95. (match specs
  96. (() (apply constructor (reverse acc)))
  97. (((name accessor serializer) . specs-rest)
  98. (match sexp
  99. ((((? (cut eq? <> name)) . serial-value) . sexp-rest)
  100. (let* ((default (and defaults (accessor defaults)))
  101. (value (deserialize serializer serial-value default)))
  102. (loop sexp-rest specs-rest (cons value acc) #f)))
  103. ((and (? (const elide-first-field?))
  104. (? (const first?))
  105. ((? (negate pair?) serial-value) . sexp-rest))
  106. (let ((value (deserialize serializer (list serial-value)
  107. (and defaults (accessor defaults)))))
  108. (loop sexp-rest specs-rest (cons value acc) #f)))
  109. (_ (loop sexp specs-rest
  110. (cons (and defaults (accessor defaults)) acc)
  111. #f)))))))))