fix EventEnv stackTrack problem, new method for GivenContext

This commit is contained in:
A.C.Sukazyo Eyre 2023-12-22 21:30:03 +08:00
parent 6b961a3de3
commit 9574dd299b
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
3 changed files with 106 additions and 29 deletions

View File

@ -8,7 +8,7 @@ object MornyConfiguration {
val MORNY_CODE_STORE = "https://github.com/Eyre-S/Coeur-Morny-Cono"
val MORNY_COMMIT_PATH = "https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s"
val VERSION = "2.0.0-alpha5"
val VERSION = "2.0.0-alpha6"
val VERSION_DELTA: Option[String] = None
val CODENAME = "guanggu"

View File

@ -6,7 +6,6 @@ import cc.sukazyo.messiva.utils.StackUtils
import com.pengrad.telegrambot.model.Update
import scala.collection.mutable
import scala.reflect.{classTag, ClassTag}
class EventEnv (
@ -29,11 +28,11 @@ class EventEnv (
//noinspection UnitMethodIsParameterless
def setEventOk: Unit =
_status += State.OK(StackUtils.getStackTrace(1)(1))
_status += State.OK(StackUtils.getStackTrace(1).head)
//noinspection UnitMethodIsParameterless
def setEventCanceled: Unit =
_status += State.CANCELED(StackUtils.getStackTrace(1)(1))
_status += State.CANCELED(StackUtils.getStackTrace(1).head)
def state: State|Null =
_status.lastOption match

View File

@ -1,9 +1,55 @@
package cc.sukazyo.cono.morny.util
import cc.sukazyo.cono.morny.util.GivenContext.ContextNotGivenException
import scala.annotation.targetName
import scala.collection.mutable
import scala.reflect.{classTag, ClassTag}
object GivenContext {
class ContextNotGivenException extends NoSuchElementException
}
/** A mutable collection that can store(provide) any typed value and read(use/consume) that value by type.
*
* ## Simple Guide
* {{{
* val cxt = GivenContext()
* class BaseClass {}
* class MyImplementation extends BaseClass {}
*
*
* cxt.provide(true) // this provides a Boolean
* cxt.provide[BaseClass](new MyImplementation()) // although this object is of type MyImplementation, but it is stored
* // as BaseClass so you can (and actually can only) read it using BaseClass
* cxt << "string"
* cxt << classOf[Int] -> 1 // you can also manually set the stored type using this method
*
*
* cxt >> { (i: Int) => println(i) } || { println("no Int data in the context") }
* val bool =
* cxt.use[String, Boolean] { s => println(s); true } || { false } // when using .use, the return value must declared
* cxt.consume[String] { s => println(s) } // you can use .consume if you don't care the return
* // and this will return a cxt.ConsumeResult[Any]
* val cxtResultOpt = // use toOption if you do not want fallback calculation
* cxt.use[Int, String](int => s"int: $int").toOption // this returns Option[String]
* val cxtResultOpt2 =
* cxt >> { (int: Int) => s"int: $int" } |? // this returns Option[String] too
* // cxt >> { (int: Int) => cxt >> { (str: String) => { str + int } } } |? // this below is not good to use due to .flatUse
* // is not supported yet. It will return a
* // cxt.ConsumeResult[Option[String]] which is very bad
*
* try { // for now, you can use this way to use multiple data
* val int = cxt.use[Int] // this returns CxtOption[Int] which is Either[ContextNotGivenException, Int]
* .toTry.get
* val str = cxt >> classOf[String] match // this >> returns the same with the .use above
* case Right(s) => s
* case Left(err) => throw err // this is ContextNotGivenException
* val bool = cxt >!> classOf[Boolean] // the easier way to do the above
* } catch case e: ContextNotGivenException => // if any of the above val is not available, it will catch the exception
* e.printStackTrace()
* }}}
*/
class GivenContext {
private type ImplicitsMap [T <: Any] = mutable.HashMap[Class[?], T]
@ -11,19 +57,31 @@ class GivenContext {
private val variables: ImplicitsMap[Any] = mutable.HashMap.empty
private val variablesWithOwner: ImplicitsMap[ImplicitsMap[Any]] = mutable.HashMap.empty
def provide (i: Any): Unit =
variables += (i.getClass -> i)
def << (i: Any): Unit =
this.provide(i)
def provide [T: ClassTag] (i: T): Unit =
variables += (classTag[T].runtimeClass -> i)
def << [T: ClassTag] (is: (Class[T], T)): Unit =
val (_, i) = is
this.provide[T](i)
def << [T: ClassTag] (i: T): Unit =
this.provide[T](i)
def >>[T: ClassTag] (consumer: T => Unit): ConsumeResult =
this.use[T](consumer)
def use [T: ClassTag] (consumer: T => Unit): ConsumeResult =
private type CxtOption[T] = Either[ContextNotGivenException, T]
def use [T: ClassTag]: CxtOption[T] =
variables get classTag[T].runtimeClass match
case Some(i) => consumer(i.asInstanceOf[T]); ConsumeResult(true)
case None => ConsumeResult(false)
def consume [T: ClassTag] (consume: T => Unit): ConsumeResult =
this.use[T](consume)
case Some(i) => Right(i.asInstanceOf[T])
case None => Left(ContextNotGivenException())
def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
this.use[T] match
case Left(_) => ConsumeFailed[U]()
case Right(i) => ConsumeSucceed[U](consumer(i))
def >> [T: ClassTag] (t: Class[T]): CxtOption[T] =
this.use[T]
def >!> [T: ClassTag] (t: Class[T]): T =
this.use[T].toTry.get
def >>[T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
this.use[T,U](consumer)
def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] =
this.use[T,Any](consume)
@targetName("ownedBy")
def / [O: ClassTag] (owner: O): OwnedContext[O] =
@ -33,26 +91,46 @@ class GivenContext {
class OwnedContext [O: ClassTag] {
def provide (i: Any): Unit =
def provide [T: ClassTag] (i: T): Unit =
(variablesWithOwner getOrElseUpdate (classTag[O].runtimeClass, mutable.HashMap.empty))
.addOne(i.getClass -> i)
def << (i: Any): Unit =
this.provide(i)
.addOne(classTag[T].runtimeClass -> i)
def << [T: ClassTag] (is: (Class[T], T)): Unit =
val (_, i) = is
this.provide[T](i)
def << [T: ClassTag] (i: T): Unit =
this.provide[T](i)
def >> [T: ClassTag] (consumer: T => Unit): ConsumeResult =
this.use[T](consumer)
def use [T: ClassTag] (consumer: T => Unit): ConsumeResult =
def use [T: ClassTag]: CxtOption[T] =
variablesWithOwner(classTag[O].runtimeClass) get classTag[T].runtimeClass match
case Some(i) => consumer(i.asInstanceOf[T]); ConsumeResult(true)
case None => ConsumeResult(false)
case Some(i) => Right(i.asInstanceOf[T])
case None => Left(ContextNotGivenException())
def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
use[T] match
case Left(_) => ConsumeFailed[U]()
case Right(i) => ConsumeSucceed[U](consumer(i))
def >> [T: ClassTag] (t: Class[T]): CxtOption[T] =
this.use[T]
def >!> [T: ClassTag] (t: Class[T]): T =
this.use[T].toTry.get
def >> [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
this.use[T,U](consumer)
def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] =
this.use[T,Any](consume)
}
class ConsumeResult (success: Boolean) {
trait ConsumeResult[U]:
def toOption: Option[U]
def |? : Option[U] = toOption
@targetName("orElse")
def || (processor: => Unit): Unit = {
if !success then processor
}
}
def || (processor: =>U): U
private class ConsumeSucceed[U] (succeedValue: U) extends ConsumeResult[U]:
override def toOption: Option[U] = Some(succeedValue)
@targetName("orElse")
override def || (processor: => U): U = succeedValue
private class ConsumeFailed[U] extends ConsumeResult[U]:
override def toOption: Option[U] = None
@targetName("orElse")
override def || (processor: => U): U = processor
}