#lang racket
(provide (all-defined-out))
(struct osc-message (address args) #:prefab)
(struct osc-bundle (timestamp elements) #:prefab)
(define osc-element? (or/c osc-message? osc-bundle?))
(define (osc-value? v)
(or (int32? v) (int64? v) (osc-date? v) (float32? v) (osc-double? v) (no-nul-bytes? v) (osc-symbol? v) (blob? v) (osc-char? v) (osc-color? v) (osc-midi? v) (boolean? v) (null? v)
(osc-inf? v) (osc-array? v) ))
(define (uint32? n)
(and (exact-integer? n)
(<= 0 n #xffffffff)))
(define osc-date? (or/c (list/c uint32? uint32?)
'now))
(define (int32? i)
(and (exact-integer? i)
(<= #x-80000000 i #x7fffffff)))
(define (int64? i)
(match i
[(list 'h (? exact-integer? n))
(<= #x-8000000000000000 n #x7fffffffffffffff)]
[other #f]))
(define (float32? f)
(and (number? f)
(inexact-real? f)))
(define (osc-double? f)
(match f
[(list 'd (? inexact-real? f)) #t]
[other #f]))
(define (blob? v)
(match v
[(list 'blob (? bytes? v))
(< (bytes-length v) #x7fffffff)]
[else #f]))
(define (no-nul-bytes? v)
(and (bytes? v)
(not (regexp-match #px#"\0" v))))
(define (osc-symbol? v)
(match v
[(list 'S (? no-nul-bytes? v)) #t]
[else #f]))
(define (osc-char? v)
(match v
[(list 'c (? byte? ch)) #t]
[other #f]))
(define (osc-color? v)
(match v
[(list 'r (? 4bytes? c)) #t]
[else #f]))
(define (osc-midi? v)
(match v
[(list 'm (? 4bytes? msg)) #t]
[else #f]))
(define (osc-inf? v)
(eq? v 'infinitum))
(define (osc-array? v)
(match v
[(list 'arr (? (listof osc-value?) v)) #t]
[else #f]))
(define (4bytes? v)
(and (bytes? v) (= (bytes-length v) 4)))
(module+ test
(require rackunit)
(check-equal? (no-nul-bytes? #"abc") #t)
(check-equal? (no-nul-bytes? #"abc\0def") #f)
(check-equal? (blob? '(blob #"272oue3")) #t)
(check-equal? (blob? '(blob 14)) #f)
(check-equal? (int32? 342) #t)
(check-equal? (int32? 32324212142) #f)
(check-equal? (int32? -24) #t))