#lang racket/base

(provide message-struct
         assertion-struct
         (struct-out observe)
         (struct-out seal)
         (struct-out inbound)
         (struct-out outbound)
         strong-gensym)

(require (only-in net/base64 base64-encode))
(require (only-in racket/random crypto-random-bytes))
(require (only-in racket/string string-trim))

;; Thin veneers over `struct` for declaring intent.
(define-syntax-rule (assertion-struct item ...) (struct item ... #:prefab))
(define-syntax-rule (message-struct item ...) (struct item ... #:prefab))

(assertion-struct observe (specification))

;; Seals are used by protocols to prevent routing from examining
;; internal structure of values.
(struct seal (contents) ;; NB. Neither transparent nor prefab
  #:methods gen:custom-write
  [(define (write-proc s port mode)
     (fprintf port "#{~v}" (seal-contents s)))])

(struct inbound (assertion) #:prefab)
(struct outbound (assertion) #:prefab)

(define (strong-gensym [head 'g] #:random-bytes [count 16])
  (string->symbol
   (format "~a~a"
           head
           (string-trim (bytes->string/latin-1
                         (base64-encode (crypto-random-bytes count) #""))
                        #px"=+"))))
