{-# OPTIONS --cubical-compatible --safe #-}
module Reflection.External where
import Agda.Builtin.Reflection.External as Builtin
open import Data.Nat.Base using (ℕ; suc; zero; NonZero)
open import Data.List.Base using (List; _∷_; [])
open import Data.Product.Base using (_×_; _,_)
open import Data.String.Base as String using (String; _++_)
open import Data.Sum.Base using (_⊎_; inj₁; inj₂; [_,_])
open import Data.Unit.Base using (⊤; tt)
open import Function.Base using (case_of_; _$_; _∘_)
open import Reflection hiding (name)
CmdName = String
StdIn = String
StdErr = String
StdOut = String
data ExitCode : Set where
exitSuccess : ExitCode
exitFailure : (n : ℕ) {n≢0 : NonZero n} → ExitCode
record CmdSpec : Set where
constructor cmdSpec
field
name : CmdName
args : List String
input : StdIn
record Result : Set where
constructor result
field
exitCode : ExitCode
output : StdOut
error : StdErr
toExitCode : ℕ → ExitCode
toExitCode zero = exitSuccess
toExitCode (suc n) = exitFailure (suc n)
quoteExitCode : ExitCode → Term
quoteExitCode exitSuccess =
con (quote exitSuccess) []
quoteExitCode (exitFailure n) =
con (quote exitFailure) (vArg (lit (nat n)) ∷ hArg (con (quote tt) []) ∷ [])
quoteResult : Result → Term
quoteResult (result exitCode output error) =
con (quote result) $ vArg (quoteExitCode exitCode)
∷ vArg (lit (string output))
∷ vArg (lit (string error))
∷ []
unsafeRunCmdTC : CmdSpec → TC Result
unsafeRunCmdTC c = do
(exitCode , (stdOut , stdErr))
← Builtin.execTC (CmdSpec.name c) (CmdSpec.args c) (CmdSpec.input c)
pure $ result (toExitCode exitCode) stdOut stdErr
macro
unsafeRunCmd : CmdSpec → Term → TC ⊤
unsafeRunCmd c hole = unsafeRunCmdTC c >>= unify hole ∘ quoteResult
showCmdSpec : CmdSpec → String
showCmdSpec c = String.unwords $ CmdSpec.name c ∷ CmdSpec.args c
private
userError : ∀ {a} {A : Set a} → CmdSpec → StdOut × StdErr → TC A
userError c (stdout , stderr) = typeError (strErr errMsg ∷ [])
where
errMsg : String
errMsg = String.unlines
$ ("Error while running command '" ++ showCmdSpec c ++ "'")
∷ ("Input:\n" ++ CmdSpec.input c)
∷ ("Output:\n" ++ stdout)
∷ ("Error:\n" ++ stderr)
∷ []
runCmdTC : CmdSpec → TC StdOut
runCmdTC c = do
r ← unsafeRunCmdTC c
let debugPrefix = ("user." ++ CmdSpec.name c)
case Result.exitCode r of λ
{ exitSuccess → do
debugPrint (debugPrefix ++ ".stderr") 10 (strErr (Result.error r) ∷ [])
pure $ Result.output r
; (exitFailure n) → do
userError c (Result.output r , Result.error r)
}
macro
runCmd : CmdSpec → Term → TC ⊤
runCmd c hole = runCmdTC c >>= unify hole ∘ lit ∘ string