write.rkt
#lang racket
(provide (all-defined-out))

(define (write-tnetstring val)
  (cond 
   [(void? val) (write-bytes #"0:~")]

   [(bytes? val) (write* val #",")]

   [(boolean? val)
    (write-bytes (if val #"4:true!" #"5:false!"))]
   
   [(list? val)
    (let ([body (to-bytes (lambda ()
			    (for-each write-tnetstring val)))])
      (write* body #"]"))]

   [(hash? val)
    (let ([body (to-bytes (lambda ()
			    (for ([(key val) (in-dict val)])
                                 (unless (bytes? key)
                                    (error "keys must be bytestrings"))
				 (write-tnetstring key)
				 (write-tnetstring val))))])
      (write* body #"}"))]
   
   [(integer? val) (write* (number->bytes val) #"#")]

   [(rational? val)
    (write* (number->bytes (if (exact? val)
			       (exact->inexact val)
			       val))
	    #"^")]

   [else (error (format "Can't write value: ~a" val))]))


(define (write* body type)
  (define n (number->bytes (bytes-length body)))
  (write-bytes (bytes-append n #":" body type)))

(define (to-bytes fn)
  (define b (open-output-bytes))
  (parameterize ([current-output-port b])
    (fn))
  (get-output-bytes b))

(define (number->bytes n)
  (string->bytes/latin-1 (number->string n)))