#lang scheme/base
(require scheme/contract
scheme/file
scheme/match
scheme/port
srfi/13/string
srfi/26/cut
(planet ryanc/scripting/read))
(define (make-directory-tree tree)
(define (tree-fold seed tree)
(define (list->path head rest)
(apply build-path (reverse (cons head rest))))
(match tree
[(? string? here)
(make-directory* (list->path here seed))]
[(list) (void)]
[`(,(? string? head) (,children ...) . ,rest)
(make-directory* (list->path head seed))
(tree-fold (cons head seed) children)
(tree-fold seed rest)]
[`(,(? string? here) . ,rest)
(make-directory* (list->path here seed))
(tree-fold seed rest)]))
(tree-fold null tree))
(define (make-non-conflicting-path path filename)
(build-path path (make-non-conflicting-filename path filename)))
(define (make-non-conflicting-filename path filename)
(define (stem->stem-and-index stem)
(let loop ([stem stem] [index-string ""])
(if (char-numeric? (string-ref stem (sub1 (string-length stem))))
(loop (string-drop-right stem 1)
(string-append index-string (string-take-right stem 1)))
(values stem
(if (= (string-length index-string) 0)
1
(string->number index-string))))))
(if (file-exists? (build-path path filename))
(let* ([pos (string-index-right filename #\.)]
[stem (if pos (string-take filename pos) filename)]
[extension (if pos (string-drop filename pos) "")])
(let-values ([(stem index)
(stem->stem-and-index stem)])
(let loop ([index index])
(let ([filename
(string-append
stem
(number->string index)
extension)])
(if (file-exists? (build-path path filename))
(loop (add1 index))
filename)))))
filename))
(define (read-file->string path)
(let ([in (open-input-file path)]
[out (open-output-string)])
(let loop ()
(let ([buf (read-string 1024 in)])
(unless (eof-object? buf)
(display buf out)
(loop))))
(close-input-port in)
(get-output-string out)))
(define (concatenate-files des srcs)
(with-output-to-file des
(cut copy-port (apply input-port-append
#t
(map open-input-file srcs))
(current-output-port))))
(provide/contract
[make-directory-tree (-> any/c void?)] [make-non-conflicting-filename (-> (or/c path? string?) string? string?)]
[make-non-conflicting-path (-> (or/c path? string?) string? path?)]
[read-file->string (-> (or/c path? string?) string?)]
[concatenate-files (-> (or/c path? string?) (listof (or/c path? string?)) void?)])