(module openlibrary (available-keys isbn->alist database-url api-path) (import chicken scheme) (use data-structures srfi-1 intarweb http-client uri-common json) (define database-url (make-parameter "http://openlibrary.org")) (define api-path (make-parameter '( "" "api" "books"))) (define-constant user-keys '(title authors publisher publishing-date number-of-pages cover-urls isbn-numbers)) (define available-keys user-keys) ; now this needs explanation ; ((foo (bar))) means we have a list of alists with key foo but want the bar values ; ( foo (bar baz)) means we have an alist of an alist but want keys bar and baz ; foo means we want the value of alist key foo (define result-keys '(title ((authors (name))) ((publishers (name))) publish_date number_of_pages cover (identifiers (isbn_10 isbn_13)))) (define (make-query isbn) (update-uri (uri-reference (database-url)) path: (api-path) query: `((bibkeys . ,(string-append "ISBN:" isbn)) (jscmd . "data") (format . "json")))) (define (search isbn) (let-values (((content uri response) (with-input-from-request (make-query isbn) #f (lambda () (json-read (current-input-port)))))) (close-connection! uri) (and (not (= (vector-length content) 0)) content))) (define (entry->alist v) (cond ((list? v) (map entry->alist v)) ((vector? v) (entry->alist (vector->list v))) ((atom? v) v) (else (cons (entry->alist (car v)) (entry->alist (cdr v)))))) (define (tentatively f l #!optional (result '())) (or (and l (not (null? l)) (f l)) result)) (define (clean-helper x l) (cond ((null? l) '()) ((not x) #f) ((and (list? x) (pair? (cdr x)) (list? (cadr x))) (map (cut clean-helper <> (alist-ref (symbol->string (car x)) l equal?)) (cdr x))) ((and (list? x) (list? (car x))) (map (cut clean-helper <> (tentatively car (alist-ref (symbol->string (caar x)) l equal?))) (cdar x))) ((list? x) (filter identity (map (lambda (x) (alist-ref (symbol->string x) l equal?)) x))) (else (alist-ref (symbol->string x) l equal?)))) (define (clean l entries) (reverse (filter (lambda (x) (member (car x) entries equal?)) (fold (lambda (x y s) (cons (cons y x) s)) '() (map (lambda (x) (clean-helper x l)) result-keys) user-keys)))) (define (isbn->alist isbn #!optional (interesting-entries user-keys)) (or (and-let* ((search-result (search isbn)) (entries (entry->alist search-result)) (filtered-entries (clean (tentatively cdar entries) interesting-entries))) filtered-entries) '())))