Skip to content

Instantly share code, notes, and snippets.

@TomTriple
Created September 30, 2023 19:19
Show Gist options
  • Save TomTriple/3fb5b67781383eea681cd713a1d71fd9 to your computer and use it in GitHub Desktop.
Save TomTriple/3fb5b67781383eea681cd713a1d71fd9 to your computer and use it in GitHub Desktop.
Flash.scala
package zio.http
import zio.schema.Schema
import zio.schema.codec.JsonCodec
import java.net.URLDecoder
sealed trait Flash[+A] { self =>
def flatMap[B](f: A => Flash[B]):Flash[B] = Flash.FlatMap(self, f)
def map[B](f: A => B):Flash[B] = self.flatMap(a => Flash.succeed(f(a)))
def run(request: Request):Either[Throwable, A] = Flash.run(self, request)
def orElse[B >: A](that: => Flash[B]):Flash[B] = Flash.OrElse(self, that)
def |[B >: A](that: => Flash[B]):Flash[B] = self.orElse(that)
def zip[B](that: => Flash[B]):Flash[(A, B)] = self.zipWith(that)((a, b) => a -> b)
def zipWith[B, C](that: => Flash[B])(f: (A, B) => C):Flash[C] =
self.flatMap(a => that.map(b => f(a, b)))
def optional:Flash[Option[A]] = self.map(Option(_)) | Flash.succeed(None)
}
object Flash {
private[zio] val COOKIE_NAME = "zio-http-flash"
private case class Get[A](schema: Schema[A], key: String) extends Flash[A]
private case class FlatMap[A, B](self: Flash[A], f: A => Flash[B]) extends Flash[B]
private case class OrElse[A, B >: A](self: Flash[A], that:Flash[B]) extends Flash[B]
private case class WithInput[A](f:Map[String, String] => Flash[A]) extends Flash[A]
private case class Succeed[A](a: A) extends Flash[A]
private def succeed[A](a:A):Flash[A] = Succeed(a)
private def withInput[A](f: Map[String, String] => Flash[A]):Flash[A] = WithInput(f)
def get[A](key: String)(implicit ev: Schema[A]): Flash[A] = Flash.Get(ev, key)
def getString(key:String):Flash[String] = get[String](key)
def getFloat(key:String):Flash[Float] = get[Float](key)
def getDouble(key:String):Flash[Double] = get[Double](key)
def getInt(key:String):Flash[Int] = get[Int](key)
def getLong(key:String):Flash[Long] = get[Long](key)
def getBoolean(key:String):Flash[Boolean] = get[Boolean](key)
def getType[A : Schema]:Flash[A] = withInput { map =>
map.keys.map(a => Flash.get(a)(Schema[A])).reduce(_ | _)
}
private def loop[A](flash: Flash[A], map: Map[String, String]): Either[Throwable, A] =
flash match {
case Get(schema, key) =>
map.get(key).toRight(new Throwable(s"no key: $key")).flatMap { value =>
JsonCodec.jsonDecoder(schema).decodeJson(value).left.map(e => new Throwable(e))
}
case WithInput(f) =>
loop(f(map), map)
case OrElse(self, that) =>
loop(self, map).orElse(loop(that, map)).asInstanceOf[Either[Throwable, A]]
case FlatMap(self, f) =>
loop(self, map) match {
case Right(value) => loop(f(value), map)
case Left(e) => Left(e)
}
case Succeed(a) => Right(a)
}
def run[A](flash: Flash[A], request: Request):Either[Throwable, A] = {
request
.cookie(COOKIE_NAME)
.toRight(new Throwable("flash cookie doesn't exist"))
.flatMap { cookie =>
try Right(URLDecoder.decode(cookie.content, java.nio.charset.Charset.defaultCharset))
catch {
case e:Exception => Left(e)
}
}
.flatMap { cookieContent =>
JsonCodec.jsonDecoder(Schema.map[String,String]).decodeJson(cookieContent).left.map(e => new Throwable(e))
}.flatMap(loop(flash, _))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment