Skip to content

Instantly share code, notes, and snippets.

@kaffein
Created December 4, 2018 14:58
Show Gist options
  • Save kaffein/94e7bffab0e59ed585753ac58953c26c to your computer and use it in GitHub Desktop.
Save kaffein/94e7bffab0e59ed585753ac58953c26c to your computer and use it in GitHub Desktop.
Yetibot's Scala command refactoring
(ns yetibot.commands.scala
(:require
[taoensso.timbre :refer [info warn error]]
[yetibot.core.hooks :refer [cmd-hook]]
[clj-http.client :as client]
[clojure.string :as s]
[cheshire.core :as json]
[kvlt.core :as kvlt]
[clojure.core.async :as async]))
(def endpoint "https://scastie.scala-lang.org/api/")
(def sbt-config-extra "scalacOptions ++= Seq(\"-deprecation\", \"-encoding\", \"UTF-8\", \"-feature\", \"-unchecked\")")
(def scala-version "2.12.6")
(def platform "Jvm")
(defn make-body
[scala-str]
(json/generate-string {:_isWorksheetMode true
:code scala-str
:target {:scalaVersion scala-version
:$type platform}
:libraries []
:librariesFromList []
:sbtConfigExtra sbt-config-extra
:sbtPluginsConfigExtra ""
:isShowingInUserProfile false}))
(defn try-scala
"Submits the Scala expression to the evaluation API and returns a
Server-Sent Events core.async channel for retrieving the result."
[expr]
(info "scala:" expr)
(as-> (str endpoint "run") v
(client/post v {:content-type :json
:accept :json
:body (make-body expr)})
(:body v)
(json/parse-string v true)
(:base64UUID v)
(str endpoint "progress-sse/" v)
(kvlt/event-source! v)))
(defn extract-success
"Extracts success evaluation info from the SSE-message data."
[{user-output :userOutput instrumentations :instrumentations}]
(cond
(seq user-output) (get-in user-output [:line])
(seq instrumentations) (->> instrumentations
(map (fn [{{v :v className :className} :render}]
(format "%s: %s" v className)))
(s/join "\n"))))
(defmulti format-result
"Returns a dispatch-value based on the SSE data received from the evaluation API."
(fn [{:keys [compilationInfos isTimeout]}]
(cond
(true? isTimeout) :eval-timeout
(seq compilationInfos) :eval-runtime-error
:else :eval-success)))
(defmethod format-result :eval-timeout
[_]
(str "The expression timed out"))
(defmethod format-result :eval-runtime-error
[data]
(->> data
:compilationInfos
(map :message)
(s/join "\n")))
(defmethod format-result :eval-success
[data]
(extract-success data))
(defn extract-result
"Extracts the Scala evaluation result from the core.async channel provided
as argument."
[chan]
(let [out-chan (async/chan)]
(async/go-loop []
(when-let [v (async/<! chan)]
(let [data (json/parse-string (:data v) true)]
(when-let [result (format-result data)]
(async/>! out-chan result)))
(recur)))
out-chan))
(defn scala-cmd
"scala <expression> # evaluate a scala expression"
{:yb/cat #{:repl}}
[{expr :match}]
(let [out-chan (-> expr
try-scala
extract-result)
result (async/<!! out-chan)]
(async/close! out-chan)
result))
(cmd-hook #"scala"
#".*" scala-cmd)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment