Created
October 9, 2022 12:16
-
-
Save jdegoes/5e3f12337d0e375203b77be6f41e1b4b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import zio._ | |
sealed trait Config[+A] { self => | |
def ++ [B](that: Config[B])(implicit zippable: Zippable[A, B]): Config[zippable.Out] = | |
Config.Zipped[A, B, zippable.Out](self, that, zippable) | |
def || [A1 >: A](that: Config[A1]): Config[A1] = Config.Fallback(self, that) | |
def ?? (label: String): Config[A] = Config.Labelled(self, label) | |
def map[B](f: A => B): Config[B] = self.mapOrFail(a => Right(f(a))) | |
def mapOrFail[B](f: A => Either[Config.Error, B]): Config[B] = Config.MapOrFail(self, f) | |
def optional: Config[Option[A]] = Config.Optional(self) | |
def sequence: Config[Chunk[A]] = Config.Sequence(self) | |
def validate[A1 >: A](f: A1 => Boolean): Config[A1] = | |
self.mapOrFail(a => if (!f(a)) Left(Config.Error.Generic(Chunk.empty, "Validation of configuration data failed")) else Right(a)) | |
} | |
object Config { | |
final case class CBoolean(name: String) extends Config[Boolean] | |
final case class CInt(name: String) extends Config[Int] | |
final case class CString(name: String) extends Config[String] | |
final case class CSecret(name: String) extends Config[Chunk[Byte]] | |
final case class MapOrFail[A, B](original: Config[A], mapOrFail: A => Either[Config.Error, B]) extends Config[B] | |
final case class Optional[A](config: Config[A]) extends Config[Option[A]] | |
final case class Sequence[A](config: Config[A]) extends Config[Chunk[A]] | |
final case class Fallback[A](first: Config[A], second: Config[A]) extends Config[A] | |
final case class Labelled[A](config: Config[A], label: String) extends Config[A] | |
final case class Zipped[A, B, C](left: Config[A], right: Config[B], zippable: Zippable.Out[A, B, C]) extends Config[C] | |
sealed trait Error { | |
def prefixed(prefix: Chunk[String]): Error = ??? | |
} | |
object Error { | |
final case class Generic(path: Chunk[String], message: String) extends Error | |
final case class InvalidData(path: Chunk[String], message: String) extends Error | |
final case class MissingData(path: Chunk[String], message: String) extends Error | |
final case class Or(left: Error, right: Error) extends Error | |
final case class And(left: Error, right: Error) extends Error | |
} | |
def boolean(name: String): Config[Boolean] = CBoolean(name) | |
def int(name: String): Config[Int] = CInt(name) | |
def secret(name: String): Config[Chunk[Byte]] = CSecret(name) | |
def string(name: String): Config[String] = CString(name) | |
// ... | |
} | |
trait ConfigProvider { | |
def load[A: Tag](config: Config[A], forService: Option[Tag[_]], serviceTrace: Option[Trace])(implicit trace: Trace): IO[Config.Error, A] | |
} | |
object Example { | |
trait Client | |
def makeClient(host: String, port: Int): UIO[Client] = ??? | |
implicit class ZIOObjectExtensions(zio: ZIO.type) { | |
def config[A: Tag](config: Config[A]): IO[Config.Error, A] = ??? | |
} | |
implicit class ZLayerObjectExtensions(zlayer: ZLayer.type) { | |
def loadConfig[C: Tag](config: Config[C]): ZLayer[Any, Config.Error, C] = ZLayer.fromZIO(ZIO.config(config)) | |
} | |
final case class ClientConfig(host: String, port: Int) | |
def clientConfig = (Config.string("host") ++ Config.int("port")).map { case (host, port) => ClientConfig(host, port) } | |
/** | |
* Can be configured with: | |
* | |
* {"my.package.Client": {"host": "localhost", "port": 8080}} | |
*/ | |
def layer = | |
ZLayer { | |
for { | |
config <- ZIO.config(clientConfig) | |
client <- makeClient(config.host, config.port) | |
} yield client | |
} | |
def main(args: Array[String]): Unit = println("Hello World!") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment