#lang scheme/base
(require "base.ss"
"depend.ss"
"request.ss"
"response.ss"
"cookie.ss"
"convert.ss"
)
(define-struct *path (base virtual full mtime))
(define-struct script (inner path)
#:property prop:procedure 0)
(define (script-mtime s)
(*path-mtime (script-path s)))
(provide/contract
(struct *path ((base path-string?)
(virtual path-equiv?)
(full path-string?)
(mtime exact-nonnegative-integer?)))
(struct script ((inner procedure?)
(path *path?)))
(script-mtime (-> script? exact-nonnegative-integer?))
)
(define (path->segments path)
(define (helper segments)
(filter (lambda (segment)
(not (equal? segment "")))
segments))
(helper (if (url? path)
(map path/param-path (url-path path))
(regexp-split #px"\\/"
(if (path? path)
(path->string path)
path)))))
(define (segments->partial-path segments base default orig)
(define (helper rest path default)
(cond ((null? rest)
(raise-http-not-found! orig))
((file-exists? (build-path path (car rest))) ($pathinfo (cdr rest)) (build-path path (car rest)))
((not (directory-exists? (build-path path (car rest))))
(raise-http-not-found! orig))
((file-exists? (build-path path (car rest) default))
($pathinfo (cdr rest)) (build-path path (car rest) default))
(else
(helper (cdr rest) (build-path path (car rest)) default))))
(helper segments base default))
(define (segments->path segments base default partial? orig)
(let ((script (apply build-path base segments)))
(cond ((file-exists? script) script)
((and (directory-exists? script)
(file-exists? (build-path script default)))
(build-path script default))
(partial?
(segments->partial-path segments base default orig))
(else
(raise-http-not-found! orig)))))
(define (resolve-path path base default (partial? #t))
(segments->path (path->segments path) base default partial? path))
(define (build-script-path server path (partial? #t))
(let ((full (resolve-path path (shp-base server) (shp-default server) partial?)))
(make-*path (shp-base server)
path
full
(mtime full))))
(provide/contract
(build-script-path (->* (shp? path-equiv?)
(boolean?)
*path?))
)
(define (require-modules! terms)
(define (require! module)
(namespace-require module))
(define (helper listof-modules)
(for-each (lambda (modules)
(for-each require! modules))
listof-modules))
(helper (map cdr (filter require-exp? terms))))
(define (require-exp? term)
(and (pair? term) (equal? (car term) 'require)))
(define (args-exp? term)
(and (pair? term) (equal? (car term) ':args)))
(define (terms->args terms)
(define (helper args)
(cond ((null? args) '()) ((not (null? (cdr args))) (error 'filter-args "multiple args statement: ~a" args))
(else (cdr (car args)))))
(helper (filter args-exp? terms)))
(define (api-args-exp? term)
(and (pair? term) (equal? (car term) ':api-args)))
(define (terms->api-args terms)
(define (helper args)
(cond ((null? args) '()) ((not (null? (cdr args))) (error 'filter-api-args "multiple args statement: ~a" args))
(else (cdr (car args)))))
(helper (filter api-args-exp? terms)))
(define (terms->exps terms)
(let ((exps (filter (lambda (exp)
(and (not (require-exp? exp))
(not (args-exp? exp))
(not (api-args-exp? exp))))
terms)))
(if (null? exps)
'("") exps)))
(define (evaluate-terms terms path namespace)
(define (build-webcall args path exps)
(eval `(call! ,args
(parameterize ((__PATH__ ,path))
. ,exps))))
(parameterize ((current-namespace namespace))
(require-modules! terms) (let ((args (terms->args terms))
(api-args (terms->api-args terms))
(exps (terms->exps terms))
(path (path->string path)))
(if (null? api-args) (eval `(lambda ,args
(parameterize ((__PATH__ ,path))
. ,exps))
namespace)
(build-webcall api-args path exps)))))
(define (evaluate-script path namespace)
(evaluate-terms (file->values path) path namespace))
(define (build-script/*path shp path)
(make-script (evaluate-script (*path-full path) (shp-namespace shp))
path))
(define (build-script shp path (partial? #t))
(build-script/*path shp (build-script-path shp path partial?)))
(define (ensure-script-handler! shp path)
(let* ((p (build-script-path shp path))
(full (*path-full p)))
(let ((it (registry-ref (shp-handlers shp) full #f)))
(cond ((or (not it) (> (*path-mtime p) (script-mtime it)))
(registry-set! (shp-handlers shp) full (build-script/*path shp p))
(registry-ref (shp-handlers shp) full
(lambda ()
(raise-http-not-found! path))))
(else it)))))
(define (include! path . args)
(let ((script (ensure-script-handler! ($server) path)))
(apply script args)))
(provide/contract
(require-modules! (-> any/c any))
(build-script/*path (-> shp? *path? script?))
(build-script (->* (shp? path-equiv?)
(boolean?)
script?))
(ensure-script-handler! (-> shp? path-equiv? script?))
(include! (->* (path-equiv?)
()
#:rest (listof any/c)
any))
)