(import scheme (chicken base) (chicken pathname) (chicken file) (chicken io) (chicken string) (chicken process-context) srfi-1 srfi-13 srfi-14) (define (dir-has-.env? dir) (file-exists? (make-pathname dir ".env"))) (define (split-env-line line) (let* ((definition (string-split line "=")) (key (car definition)) (value (string-trim-both (cadr definition) (list->char-set (list #\") char-set:whitespace)))) (cons key value))) (define (read-env-line port) "Read an env-definition line from PORT and return as a cons cell" (let ((line (read-line port))) (if (eq? line #!eof) #!eof (split-env-line line)))) (define (load-dir-.env dir) "Load a .env file present in `.dir` and return its environment definitions as a alist" (let ((.env-file (conc dir "/.env"))) (if (file-exists? .env-file) (with-input-from-file .env-file (lambda () (read-list (current-input-port) read-env-line))) '()))) (define (path-parts path) (call-with-values (lambda () (decompose-directory path)) (lambda (origin base elts) (if (not (eq? origin #f)) (cons origin elts) (if (not (eq? base #f)) (cons base elts) elts))))) (define (path-parents path) (fold (lambda (item acc) (if (> (length acc) 0) (if (string=? (car acc) "/") (cons (conc "/" item) acc) (cons (conc (car acc) "/" item) acc)) (list item))) '() (path-parts path))) (define (.env-union alist2 alist1) (lset-union (lambda (a b) (string=? (car a) (car b))) alist2 alist1)) (let ((parents (path-parents (current-directory)))) (fold .env-union '() (reverse (map load-dir-.env parents))))