json.scm 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2014 David Thompson <davet@gnu.org>
  3. ;;; Copyright © 2015, 2016 Eric Bavier <bavier@member.fsf.org>
  4. ;;; Copyright © 2018, 2019, 2023 Ludovic Courtès <ludo@gnu.org>
  5. ;;; Copyright © 2020 Ricardo Wurmus <rekado@elephly.net>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (guix import json)
  22. #:use-module (json)
  23. #:use-module (guix http-client)
  24. #:use-module (guix import utils)
  25. #:use-module (guix import print)
  26. #:use-module (ice-9 match)
  27. #:use-module (ice-9 rdelim)
  28. #:use-module (srfi srfi-1)
  29. #:use-module (srfi srfi-2)
  30. #:use-module (srfi srfi-26)
  31. #:use-module (srfi srfi-34)
  32. #:export (json-fetch
  33. json->code
  34. json->scheme-file))
  35. (define* (json-fetch url
  36. #:key
  37. (http-fetch http-fetch)
  38. (timeout 10)
  39. ;; Note: many websites returns 403 if we omit a
  40. ;; 'User-Agent' header.
  41. (headers `((user-agent . "GNU Guile")
  42. (Accept . "application/json"))))
  43. "Return a representation of the JSON resource URL (a list or hash table), or
  44. #f if URL returns 403 or 404. HEADERS is a list of HTTP headers to pass in
  45. the query. HTTP-FETCH is called to perform the request: for example, to
  46. enable caching, supply 'http-fetch/cached'."
  47. (guard (c ((and (http-get-error? c)
  48. (let ((error (http-get-error-code c)))
  49. (or (= 403 error)
  50. (= 404 error))))
  51. #f))
  52. (let* ((port (http-fetch url #:timeout timeout #:headers headers))
  53. (result (json->scm port)))
  54. (close-port port)
  55. result)))
  56. (define (json->code file-name)
  57. "Read FILE-NAME containing one ore more JSON package definitions and return
  58. a list of S-expressions, or return #F when the JSON is invalid."
  59. (catch 'json-invalid
  60. (lambda ()
  61. (let ((json (json-string->scm
  62. (with-input-from-file file-name read-string))))
  63. (match json
  64. (#(packages ...)
  65. ;; To allow definitions to refer to one another, collect references
  66. ;; to local definitions and tell alist->package to ignore them.
  67. (second
  68. (memq #:result
  69. (fold
  70. (lambda (pkg names+result)
  71. (match names+result
  72. ((#:names names #:result result)
  73. (list #:names
  74. (cons (assoc-ref pkg "name") names)
  75. #:result
  76. (append result
  77. (list
  78. (package->code (alist->package pkg names))
  79. (string->symbol (assoc-ref pkg "name"))))))))
  80. (list #:names '()
  81. #:result '())
  82. packages))))
  83. (package
  84. (list (package->code (alist->package json))
  85. (string->symbol (assoc-ref json "name")))))))
  86. (const #f)))
  87. (define (json->scheme-file file)
  88. "Convert the FILE containing a JSON package definition to a Scheme
  89. representation and return the new file name (or #F on error)."
  90. (and-let* ((sexprs (json->code file))
  91. (file* (let* ((tempdir (or (getenv "TMPDIR") "/tmp"))
  92. (template (string-append tempdir "/guix-XXXXXX"))
  93. (port (mkstemp! template)))
  94. (close-port port)
  95. template)))
  96. (call-with-output-file file*
  97. (lambda (port)
  98. (write '(use-modules (gnu)
  99. (guix)
  100. ((guix licenses) #:prefix license:))
  101. port)
  102. (for-each (cut write <> port) sexprs)))
  103. file*))