Liking cljdoc? Tell your friends :D

graal-spec-tools

CircleCI codecov

A Clojure library for spec-driven interop with GraalVM Polyglot runtime.

[org.clojars.zharinov/graal-spec-tools "0.1.1"]

Disclaimer: consider it alpha-quality until 1.0.0

Usage

(require '[graal-spec-tools.core :as gst])

;; Only for samples below
(def context 
  (-> (org.graalvm.polyglot.Context/newBuilder 
        (into-array String ["js"]))
    (.build)))
(def bindings (.getBindings context "js"))

Data interop

(require '[spec-tools.data-spec :as ds])

(def spec 
  (ds/spec
    {:name ::person
     :spec {:id                   integer?
            :boss                 boolean?
            (ds/req :name)        string?
            (ds/opt :description) string?
            :aliases              [(ds/or {:maps    {:alias string?}
                                           :strings string?})]
            :orders               [{:id          int?
                                    :description string?}]
            :languages            #{keyword?}
            :address              (ds/maybe
                                    {:street string?
                                     :zip    string?})}}))

(def value {:id          42
            :boss        true
            :name        "Liisa"
            :description "Liisa is a valid boss"
            :aliases     [{:alias "Lissu"} "Liisu"]
            :orders      [{:id 1, :description "cola"}
                          {:id 2, :description "kebab"}]
            :languages   #{:clj :cljs}
            :address     {:street "Amurinkatu 2"
                          :zip    "33210"}})

(gst/encode spec value)
;; => #graal-proxy[{:id 42, ...}]

(.putMember bindings "value" (gst/encode spec value))

(= value
   (gst/decode spec
     (.eval context "js"
       "const x = JSON.stringify(global.value, null, 2)
        console.log(x)
        JSON.parse(x)")))
;; => true

Function interop

(require '[clojure.spec.alpha :as s])
(require '[spec-tools.spec :as spec])

(def args-spec
  (s/cat
    :x spec/integer?
    :y spec/string?))

(def ret-spec boolean?)

(def fn-spec
  (gst/fspec
    {:name ::sample-fn
     :args args-spec
     :ret  ret-spec}))

(def context 
  (-> (org.graalvm.polyglot.Context/newBuilder 
        (into-array String ["js"]))
    (.build)))

(.putMember bindings "fn" 
  (gst/encode fn-spec 
    (fn [x y] (= (str x) y))))

(.eval context "js" "fn(42, '42')")
;; => #graal[true]

(let [fn-js    "(x, y) => x == y"
      fn-graal (.eval context "js" fn-js)
      fn-clj   (gst/decode fn-spec fn-graal)]
  (fn-clj 42 "foo"))
;; => false

Higher-order functions

(defn set-timeout [callback timeout-ms]
  (Thread/sleep timeout-ms)
  (callback))

(.putMember bindings "setTimeout"
  (gst/encode 
    (gst/fspec
      {:name ::set-timeout
       :args (s/cat
               :callback   (gst/fspec
                             {:name ::callback})
               :timeout-ms (s/or
                             :zero spec/zero?
                             :pos spec/pos-int?))})
    set-timeout))

(.eval context "js" "setTimeout(() => { console.log('hello') }, 1000)")
;; After 1 second, will print 'hello' somewhere

Known issues

License

Copyright © 2019 Sergei Zharinov

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close