forked from nova/jmonkey-test
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
5.3 KiB
193 lines
5.3 KiB
package wow.doge.mygame.state
|
|
|
|
import javax.script.ScriptEngine
|
|
import javax.script.ScriptEngineManager
|
|
|
|
import akka.actor.typed.ActorRef
|
|
import akka.actor.typed.Behavior
|
|
import akka.actor.typed.LogOptions
|
|
import akka.actor.typed.scaladsl.ActorContext
|
|
import akka.actor.typed.scaladsl.Behaviors
|
|
import ammonite.Main
|
|
import ammonite.main.Defaults
|
|
import ammonite.runtime.Storage.Folder
|
|
import ammonite.util.Res.Success
|
|
import cats.implicits._
|
|
import com.softwaremill.tagging._
|
|
import com.typesafe.scalalogging.Logger
|
|
import groovy.util.GroovyScriptEngine
|
|
import org.slf4j.event.Level
|
|
|
|
object ScriptActor {
|
|
|
|
/**
|
|
* aka script representation
|
|
*/
|
|
sealed trait ScriptTag
|
|
type ScriptObject = Any @@ ScriptTag
|
|
|
|
trait Kotlin
|
|
type KotlinScriptEngine = ScriptEngine @@ Kotlin
|
|
|
|
final case class Error(reason: String)
|
|
sealed trait Command
|
|
final case class CompileAny(
|
|
path: os.Path,
|
|
result: ActorRef[Either[Error, ScriptObject]]
|
|
) extends Command
|
|
|
|
final case class CompileAll(
|
|
paths: Seq[os.Path],
|
|
result: ActorRef[Map[os.Path, Either[Error, Any]]]
|
|
) extends Command
|
|
|
|
lazy val defaultScalaRunner =
|
|
ammonite
|
|
.Main(
|
|
storageBackend = new Folder(
|
|
// os.pwd / "target"
|
|
Defaults.ammoniteHome,
|
|
isRepl = false
|
|
)
|
|
)
|
|
|
|
lazy val defaultKotlinRunner: KotlinScriptEngine = {
|
|
val manager = new ScriptEngineManager()
|
|
val engine = manager.getEngineByExtension("main.kts")
|
|
engine.taggedWith[Kotlin]
|
|
}
|
|
|
|
lazy val defaultGroovyRunner: GroovyScriptEngine =
|
|
new GroovyScriptEngine(os.pwd.toString)
|
|
|
|
def apply(
|
|
scalaRunner: Main = defaultScalaRunner,
|
|
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner,
|
|
groovyRunner: GroovyScriptEngine = defaultGroovyRunner
|
|
): Behavior[ScriptActor.Command] =
|
|
Behaviors.logMessages(
|
|
LogOptions()
|
|
.withLevel(Level.TRACE)
|
|
.withLogger(
|
|
Logger[ScriptActor].underlying
|
|
),
|
|
Behaviors.setup(ctx =>
|
|
new ScriptActor(
|
|
scalaRunner,
|
|
kotlinRunner,
|
|
groovyRunner,
|
|
ctx
|
|
).receiveMessage
|
|
)
|
|
)
|
|
|
|
sealed trait ScriptType
|
|
case object ScalaType extends ScriptType
|
|
case object KotlinType extends ScriptType
|
|
case object GroovyType extends ScriptType
|
|
|
|
def determineScriptType(path: os.Path): Either[Error, ScriptType] =
|
|
path.toString match {
|
|
case s if s.endsWith(".sc") => Right(ScalaType)
|
|
case s if s.endsWith(".main.kts") => Right(KotlinType)
|
|
case s if s.endsWith(".groovy") => Right(GroovyType)
|
|
case _ => Left(Error("Unknown script type"))
|
|
}
|
|
|
|
def runScala(path: os.Path, scalaRunner: ammonite.Main): Either[Error, Any] =
|
|
scalaRunner
|
|
.runScript(
|
|
path,
|
|
Seq.empty
|
|
)
|
|
._1 match {
|
|
case ammonite.util.Res.Exception(t, msg) => Left(Error(msg))
|
|
|
|
case Success(obj) => Right(obj)
|
|
|
|
case _ => Left(Error("Failed to run script"))
|
|
}
|
|
|
|
def runKotlin(
|
|
path: os.Path,
|
|
kotlinRunner: KotlinScriptEngine
|
|
): Either[Error, Any] =
|
|
Either
|
|
.catchNonFatal(kotlinRunner.eval(os.read(path)))
|
|
.leftMap(t => Error(t.getMessage()))
|
|
|
|
def runGroovy(
|
|
path: os.Path,
|
|
groovyRunner: GroovyScriptEngine
|
|
): Either[Error, Any] =
|
|
Either
|
|
.catchNonFatal(groovyRunner.run(path.relativeTo(os.pwd).toString(), ""))
|
|
.leftMap(t => Error(t.getMessage()))
|
|
|
|
def ensureReturnedObjectNotNull(scriptObject: Any): Either[Error, Any] =
|
|
Either.fromOption(Option(scriptObject), Error("unknown object"))
|
|
|
|
}
|
|
|
|
class ScriptActor(
|
|
val scalaRunner: ammonite.Main,
|
|
val kotlinRunner: ScriptActor.KotlinScriptEngine,
|
|
val groovyRunner: GroovyScriptEngine,
|
|
context: ActorContext[ScriptActor.Command]
|
|
) {
|
|
import ScriptActor._
|
|
|
|
def receiveMessage: Behavior[Command] =
|
|
Behaviors.receiveMessage {
|
|
case CompileAny(path, requester) =>
|
|
context.log.debug(s"Received $path")
|
|
val res = getScript(path)
|
|
context.log.debug(s"result = $res")
|
|
requester ! res
|
|
Behaviors.same
|
|
|
|
case CompileAll(paths, requester) =>
|
|
context.log.debug(s"Received $paths")
|
|
requester ! compileAll(paths)
|
|
Behaviors.same
|
|
}
|
|
|
|
def getScript(path: os.Path): Either[Error, ScriptObject] =
|
|
determineScriptType(path) match {
|
|
case Right(ScalaType) =>
|
|
runScala(path, scalaRunner)
|
|
.flatMap(ensureReturnedObjectNotNull)
|
|
.map(_.taggedWith[ScriptTag])
|
|
case Right(KotlinType) =>
|
|
runKotlin(path, kotlinRunner)
|
|
.flatMap(ensureReturnedObjectNotNull)
|
|
.map(_.taggedWith[ScriptTag])
|
|
case Right(GroovyType) =>
|
|
runGroovy(path, groovyRunner)
|
|
.flatMap(ensureReturnedObjectNotNull)
|
|
.map(_.taggedWith[ScriptTag])
|
|
case l @ Left(err) => l.map(_.taggedWith[ScriptTag])
|
|
}
|
|
|
|
type LOL = Map[os.Path, Either[wow.doge.mygame.state.ScriptActor.Error, Any]]
|
|
|
|
def compileAll(
|
|
paths: Seq[os.Path]
|
|
): LOL = {
|
|
@annotation.tailrec
|
|
def loop(
|
|
paths: Seq[os.Path],
|
|
scriptsMap: Map[
|
|
os.Path,
|
|
Either[wow.doge.mygame.state.ScriptActor.Error, Any]
|
|
]
|
|
): LOL = {
|
|
paths match {
|
|
case head :: next => loop(next, scriptsMap + (head -> getScript(head)))
|
|
case Nil => scriptsMap
|
|
}
|
|
}
|
|
loop(paths, Map.empty)
|
|
}
|
|
|
|
}
|