(module persistent-struct mzscheme
(require-for-syntax
(lib "list.ss" "srfi" "1")
(lib "cut.ss" "srfi" "26"))
(require-for-syntax
(planet "debug.ss" ("untyped" "unlib.plt" 2))
(planet "syntax.ss" ("untyped" "unlib.plt" 2)))
(require (lib "contract.ss")
(lib "plt-match.ss")
(lib "struct.ss")
(all-except (lib "list.ss" "srfi" "1") any))
(require (file "base.ss")
(file "era.ss")
(file "transaction.ss")
(file "type.ss"))
(provide define-persistent-struct
provide-persistent-struct
define/provide-persistent-struct
make-persistent-struct)
(define make-persistent-struct
(case-lambda
[(name field/type-pairs)
(make-persistent-struct name field/type-pairs null null)]
[(name field/type-pairs pipelines)
(make-persistent-struct name field/type-pairs pipelines null)]
[(name field/type-pairs pipelines properties)
(when (member prop:entity (map car properties))
(raise-exn exn:fail:snooze
(format "You cannot specify prop:entity in the properties argument: ~a" properties)))
(let*-values
( [(the-entity)
(make-entity name
(make-dummy-constructor name) (make-dummy-getter name) (make-dummy-setter name) null null null null null)]
[(type constructor predicate accessor mutator)
(make-struct-type
name #f (+ (length field/type-pairs) 2) 0 (void) (cons (cons prop:entity the-entity) properties)
#f)]
[(the-struct-type) type]
[(the-constructor)
(lambda field-values
(if (= (length field-values)
(length field/type-pairs))
(apply constructor (cons* #f #f field-values)) (raise-exn exn:fail:contract:arity
(format "Expected ~a arguments, received ~a" (length field/type-pairs) field-values))))]
[(the-predicate)
predicate]
[(indexed-fields)
(map (lambda (field index)
(list field index))
(map car (cons* '(id id) '(revision revision) field/type-pairs))
(iota (+ (length field/type-pairs) 2) 0))]
[(the-accessor)
(lambda (s field)
(let ([position (cadr (assoc field indexed-fields))])
(apply (make-struct-field-accessor accessor position field)
(list s))))]
[(the-mutator)
(lambda (s field value)
(let ([position (cadr (assoc field indexed-fields))])
(if (and (in-transaction?)
(roll-back-persistent-structs?))
(let ([old-value (the-accessor s field)])
(begin0 (apply (make-struct-field-mutator mutator position field)
(list s value))
(record-delta! s field old-value)))
(apply (make-struct-field-mutator mutator position field)
(list s value)))))]
[(attribute-fields)
(cons* (make-attribute 'id type:id)
(make-attribute 'revision type:revision)
(map make-attribute
(map car field/type-pairs)
(map cadr field/type-pairs)))])
(set-entity-constructor! the-entity constructor)
(set-entity-getter! the-entity accessor)
(set-entity-setter! the-entity mutator)
(set-entity-fields! the-entity attribute-fields)
(for-each (match-lambda
[(list 'save value)
(set-entity-save-pipeline! the-entity value)]
[(list 'insert value)
(set-entity-insert-pipeline! the-entity value)]
[(list 'update value)
(set-entity-update-pipeline! the-entity value)]
[(list 'delete value)
(set-entity-delete-pipeline! the-entity value)]
[(list key value)
(raise-exn exn:fail:snooze
(format "Unrecognised pipeline ~a: \"~a\": should be one of: save, insert, update or delete."
(entity-name the-entity)
key))])
pipelines)
(values the-struct-type
the-constructor
the-entity
the-predicate
the-accessor
the-mutator))]))
(begin-for-syntax
(define (field-names->names stx before after name field-names)
(map (lambda (field-name)
(make-syntax-symbol stx before name '- field-name after))
field-names))
(define (def-accessor-names the-accessor accessor-names field-names)
(map (lambda (accessor-name position fname)
#`(define #,accessor-name
(lambda (s)
(#,the-accessor s (quote #,fname)))))
accessor-names
(iota (length accessor-names) 2)
field-names))
(define (def-mutator-names the-mutator mutator-names field-names)
(map (lambda (mutator-name position fname)
#`(define #,mutator-name
(lambda (s v)
(#,the-mutator s
(quote #,fname)
v))))
mutator-names
(iota (length mutator-names) 2)
field-names))
)
(define-syntax (define-persistent-struct stx)
(syntax-case stx ()
[(_ name
([field-name field-type] ...))
#'(_ name
([field-name field-type] ...)
()
())]
[(_ name
([field-name field-type] ...)
([pipeline-name pipeline] ...))
#'(_ name
([field-name field-type] ...)
([pipeline-name pipeline] ...)
())]
[(_ name
([field-name field-type] ...)
([pipeline-name pipeline] ...)
([property property-value] ...))
(let* ([field-names (syntax->list #'(field-name ...))]
[field-types (syntax->list #'(field-type ...))]
[field-accessors (field-names->names #'name '|| '|| #'name field-names)]
[field-mutators (field-names->names #'name 'set- '! #'name field-names)])
(with-syntax ([struct-type (make-syntax-symbol #'name 'struct: #'name)]
[constructor (make-syntax-symbol #'name 'make- #'name)]
[constructor/defaults (make-syntax-symbol #'name 'make- #'name '/defaults)]
[predicate (make-syntax-symbol #'name #'name '?)]
[entity (make-syntax-symbol #'name 'entity: #'name)]
[accessor (make-syntax-symbol #'name #'name '-ref)]
[mutator (make-syntax-symbol #'name #'name '-set!)]
[id-accessor (make-syntax-symbol #'name #'name '-id)]
[revision-accessor (make-syntax-symbol #'name #'name '-revision)])
#`(begin (begin (define-values (struct-type constructor entity predicate accessor mutator)
(make-persistent-struct 'name
(list (list 'field-name field-type) ...)
(list (list 'pipeline-name pipeline) ...)
(list (cons property property-value) ...)))
(define id-accessor get-id)
(define revision-accessor get-revision)
#,@(def-accessor-names #'accessor field-accessors field-names)
#,@(def-mutator-names #'mutator field-mutators field-names)
(define (constructor/defaults . args)
(apply make-persistent-struct/defaults (cons entity args)))
(define-syntaxes (name)
(let ([certify (syntax-local-certifier #t)])
(list-immutable #'struct-type
#'constructor
#'predicate
(list-immutable #,@(map (lambda (accessor)
(with-syntax ([accessor accessor])
#`(certify #'accessor)))
(reverse field-accessors)))
(list-immutable #,@(map (lambda (mutator)
(with-syntax ([mutator mutator])
#`(certify #'mutator)))
(reverse field-mutators)))
#t)))))))]))
(define-syntax (provide-persistent-struct stx)
(define (field-names->names before after name field-names)
(map (lambda (field-name)
(make-syntax-symbol stx before name '- field-name after))
(syntax-object->datum field-names)))
(syntax-case stx ()
[(_ name ((field-name field-type) ...))
(with-syntax
([struct-type (make-syntax-symbol stx 'struct: #'name)]
[constructor (make-syntax-symbol stx 'make- #'name)]
[constructor/defaults (make-syntax-symbol stx 'make- #'name '/defaults)]
[predicate (make-syntax-symbol stx #'name '?)]
[entity (make-syntax-symbol stx 'entity: #'name)]
[id-accessor (make-syntax-symbol stx #'name '-id)]
[revision-accessor (make-syntax-symbol stx #'name '-revision)]
[(field-accessor ...) (field-names->names '|| '|| #'name #'(field-name ...))]
[(field-mutator ...) (field-names->names 'set- '! #'name #'(field-name ...))])
#'(provide name
struct-type
constructor
constructor/defaults
entity
predicate
id-accessor
revision-accessor
field-accessor ...
field-mutator ...))]))
(define-syntax (define/provide-persistent-struct stx)
(syntax-case stx ()
[(_ name
([field-name field-type] ...))
#'(_ name
([field-name field-type] ...)
()
())]
[(_ name
([field-name field-type] ...)
([pipeline-name pipeline] ...))
#'(_ name
([field-name field-type] ...)
([pipeline-name pipeline] ...)
())]
[(_ name
([field-name field-type] ...)
([pipeline-name pipeline] ...)
([property property-value] ...))
#'(begin (define-persistent-struct name
([field-name field-type] ...)
([pipeline-name pipeline] ...)
([property property-value] ...))
(provide-persistent-struct name
([field-name field-type] ...)))]))
(define (make-dummy-constructor struct-name)
(lambda args
(raise-exn exn:fail:snooze
"make-persistent-struct didn't overwrite the dummy constructor in ~a" struct-name)))
(define (make-dummy-getter struct-name)
(lambda args
(raise-exn exn:fail:snooze
"make-persistent-struct didn't overwrite the dummy getter in ~a" struct-name)))
(define (make-dummy-setter struct-name)
(lambda args
(raise-exn exn:fail:snooze
"make-persistent-struct didn't overwrite the dummy setter in ~a" struct-name)))
)