Files
penpot/backend/src/app/srepl.clj
Andrey Antukh 283eb0419c ♻️ Refactor time related namespaces
Mainly removes the custom app.util.time namespace
from frontend and backend and normalize all to use
the app.common.time namespace
2025-08-01 11:20:01 +02:00

144 lines
4.2 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.srepl
"Server Repl."
(:refer-clojure :exclude [read-line])
(:require
[app.common.exceptions :as ex]
[app.common.json :as json]
[app.common.logging :as l]
[app.common.time :as ct]
[app.config :as cf]
[app.srepl.cli :as cli]
[app.srepl.main]
[app.util.locks :as locks]
[clojure.core :as c]
[clojure.core.server :as ccs]
[clojure.main :as cm]
[integrant.core :as ig]))
(defn- repl-init
[]
(ccs/repl-init)
(in-ns 'app.srepl.main))
(defn user-repl
[]
(cm/repl
:init repl-init
:read ccs/repl-read))
(defn- ex->data
[cause phase]
(let [data (ex-data cause)
explain (ex/explain data)]
(cond-> {:phase phase
:code (get data :code :unknown)
:type (get data :type :unknown)
:hint (or (get data :hint) (ex-message cause))}
(some? explain)
(assoc :explain explain))))
(defn read-line
[]
(if-let [line (c/read-line)]
(try
(l/dbg :hint "decode" :data line)
(json/decode line :key-fn json/read-kebab-key)
(catch Throwable _cause
(l/warn :hint "unable to decode data" :data line)
nil))
::eof))
(defn json-repl
[]
(let [lock (locks/create)
out *out*
out-fn
(fn [m]
(locks/locking lock
(binding [*out* out]
(l/warn :hint "write" :data m)
(println (json/encode m :key-fn json/write-camel-key)))))
tapfn
(fn [val]
(out-fn {:tag :tap :val val}))]
(binding [*out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil true)
*err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil true)]
(try
(add-tap tapfn)
(loop []
(when (try
(let [data (read-line)
tpoint (ct/tpoint)]
(l/dbg :hint "received" :data (if (= data ::eof) "EOF" data))
(try
(when-not (= data ::eof)
(when-not (nil? data)
(let [result (cli/exec data)
elapsed (tpoint)]
(l/warn :hint "result" :data result)
(out-fn {:tag :ret
:val (if (instance? Throwable result)
(Throwable->map result)
result)
:elapsed (inst-ms elapsed)})))
true)
(catch Throwable cause
(let [elapsed (tpoint)]
(out-fn {:tag :ret
:err (ex->data cause :eval)
:elapsed (inst-ms elapsed)})
true))))
(catch Throwable cause
(out-fn {:tag :ret
:err (ex->data cause :read)})
true))
(recur)))
(finally
(remove-tap tapfn))))))
;; --- State initialization
(defmethod ig/assert-key ::server
[_ params]
(assert (int? (::port params)) "expected valid port")
(assert (string? (::host params)) "expected valid host"))
(defmethod ig/expand-key ::server
[[type :as k] v]
{k (assoc v ::flag (keyword (str (name type) "-server")))})
(defmethod ig/init-key ::server
[[type _] {:keys [::flag ::port ::host] :as cfg}]
(when (contains? cf/flags flag)
(l/inf :hint "initializing repl server"
:name (name type)
:port port
:host host)
(let [accept (case type
::prepl 'app.srepl/json-repl
::urepl 'app.srepl/user-repl)
params {:address host
:port port
:name (name type)
:accept accept}]
(ccs/start-server params)
(assoc params :type type))))
(defmethod ig/halt-key! ::server
[_ params]
(some-> params :name ccs/stop-server))