#lang racket/base

(provide gen:preservable
         preservable?
         ->preserve
         preservable/c)

(require preserves)
(require racket/generic)
(require racket/set)
(require racket/dict)

(define-generics preservable
  (->preserve preservable)
  #:fast-defaults ([void? (define (->preserve preservable) preservable)] ;; !
                   [boolean? (define (->preserve preservable) preservable)]
                   [number? (define (->preserve preservable) preservable)]
                   [string? (define (->preserve preservable) preservable)]
                   [bytes? (define (->preserve preservable) preservable)]
                   [symbol? (define (->preserve preservable) preservable)]
                   [null? (define (->preserve preservable) preservable)]
                   [pair?
                    (define/generic *->preserve ->preserve)
                    (define (->preserve preservable) (map *->preserve preservable))]
                   [hash?
                    (define/generic *->preserve ->preserve)
                    (define (->preserve preservable)
                      (for/hash [((k v) (in-hash preservable))]
                        (values (*->preserve k) (*->preserve v))))])
  #:defaults ([float? (define (->preserve preservable) preservable)]
              [embedded? (define (->preserve preservable) preservable)]
              [record?
               (define/generic *->preserve ->preserve)
               (define (->preserve preservable)
                 (record (*->preserve (record-label preservable))
                         (map *->preserve (record-fields preservable))))]
              [set?
               (define/generic *->preserve ->preserve)
               (define (->preserve preservable)
                 (for/set [(v (in-set preservable))]
                   (*->preserve v)))]
              [dict?
               (define/generic *->preserve ->preserve)
               (define (->preserve preservable)
                 (for/hash [((k v) (in-dict preservable))]
                   (values (*->preserve k) (*->preserve v))))]))
