#lang scheme/base
(require "depend.ss"
"path.ss"
)
(define (make-temp-path (base (temp-path)))
(build-path base (format "~a.~a.~a.~a"
(uuid->string (make-uuid))
(current-seconds)
(current-milliseconds)
(symbol->string (gensym)))))
(define source/c
(or/c false/c path-string? bytes? input-port? string? (-> output-port? any)))
(define (make-source-filler source)
(lambda (out)
(cond ((and (path-string? source)
(file-exists? source))
(call-with-input-file source (curryr copy-port out)))
((input-port? source)
(copy-port source out))
((bytes? source)
(write-bytes source out))
((string? source)
(write-string source out))
((procedure? source)
(source out))
((not source) (void))
(else
(error 'build-temporary-file "unsupported source: ~a" source)))))
(define temp-file-onclose/c (-> path-string? thunk?))
(define (default-onclose path) void)
(define (build-output-temp-file path onclose)
(let ((out (open-output-file path)))
(make-output-port path
always-evt
(make-default-write out)
(make-default-close-output-port out (onclose path)))))
(define (open-output-temp-file #:base (dir (temp-path))
#:close (onclose default-onclose))
(build-output-temp-file (make-temp-path dir) onclose))
(define (make-temp-file #:source (source #f)
#:base (dir (temp-path)))
(call-with-output-port (open-output-temp-file #:base dir)
(lambda (out)
((make-source-filler source) out)
(object-name out))))
(define-struct input-temp-file (inner path)
#:property prop:input-port 0)
(define (build-input-temp-file path onclose)
(let ((in (open-input-file path)))
(make-input-temp-file
(make-input-port path
(make-default-read in)
(make-default-peek in)
(make-default-close-input-port in (onclose path)))
path)))
(define (default-input-temp-file-onclose path)
(lambda ()
(delete-file path)))
(define (open-input-temp-file #:source (source #f)
#:base (dir (temp-path))
#:close (onclose default-input-temp-file-onclose))
(build-input-temp-file (make-temp-file #:source source #:base dir) onclose))
(define (call-with-input-temp-file proc
#:source (source #f)
#:base (dir (temp-path))
#:close
(onclose default-input-temp-file-onclose))
(call-with-input-port (open-input-temp-file #:source source
#:base dir
#:close onclose)
proc))
(define (temp-file-length t)
(with-handlers ((exn? (lambda (e) 0)))
(file-size (input-temp-file-path t))))
(input-port-length-registry-set! input-temp-file? temp-file-length)
(provide/contract
(make-temp-path (->* ()
(path-string?)
path-string?))
(open-output-temp-file (->* ()
(#:base path-string?
#:close temp-file-onclose/c)
output-port?))
(make-temp-file (->* ()
(#:source source/c
#:base path-string?)
path-string?))
(open-input-temp-file (->* ()
(#:source source/c
#:base path-string?
#:close temp-file-onclose/c)
input-temp-file?))
(call-with-input-temp-file (->* ((-> input-port? any))
(#:source source/c
#:base path-string?
#:close temp-file-onclose/c)
any))
(input-temp-file? (-> any/c any))
(input-temp-file-path (-> input-temp-file? path-string?))
)