diff --git a/build.sbt b/build.sbt index ade39885..d43ebd99 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ import scala.scalanative.build.Mode -val catsEffectVersion = "3.4.10" +val catsEffectVersion = "3.5.0" val circeVersion = "0.14.5" @@ -8,7 +8,7 @@ val circeYamlVersion = "0.14.2" val enumeratumVersion = "1.7.2" -val http4sVersion = "0.23.18" +val http4sVersion = "0.23.19" val refinedVersion = "0.10.3" diff --git a/modules/core/shared/src/main/scala/ciris/ConfigValue.scala b/modules/core/shared/src/main/scala/ciris/ConfigValue.scala index 1b1116e5..24fd2b1a 100644 --- a/modules/core/shared/src/main/scala/ciris/ConfigValue.scala +++ b/modules/core/shared/src/main/scala/ciris/ConfigValue.scala @@ -346,6 +346,25 @@ object ConfigValue { * @group Create */ final def async[F[_], A]( + k: (Either[Throwable, ConfigValue[F, A]] => Unit) => F[Option[F[Unit]]] + ): ConfigValue[F, A] = + new ConfigValue[F, A] { + override final def to[G[x] >: F[x]](implicit G: Async[G]): Resource[G, ConfigEntry[A]] = + Resource + .eval(G.async[ConfigValue[F, A]](k(_).asInstanceOf[G[Option[G[Unit]]]])) + .flatMap(_.to[G]) + + override final def toString: String = + "ConfigValue$" + System.identityHashCode(this) + } + + /** + * Returns a new [[ConfigValue]] which loads a configuration + * value using a callback. + * + * @group Create + */ + final def async_[F[_], A]( k: (Either[Throwable, ConfigValue[F, A]] => Unit) => Unit ): ConfigValue[F, A] = new ConfigValue[F, A] { diff --git a/modules/core/shared/src/test/scala/ciris/ConfigValueSpec.scala b/modules/core/shared/src/test/scala/ciris/ConfigValueSpec.scala index 7c47c9e5..15b75d09 100644 --- a/modules/core/shared/src/test/scala/ciris/ConfigValueSpec.scala +++ b/modules/core/shared/src/test/scala/ciris/ConfigValueSpec.scala @@ -137,34 +137,68 @@ final class ConfigValueSpec extends CatsEffectSuite with ScalaCheckEffectSuite w test("ConfigValue.async.default") { check( - ConfigValue.async[IO, String] { cb => cb(Right(default)) }, + ConfigValue.async[IO, String] { cb => IO(cb(Right(default))).as(None) }, default ) } test("ConfigValue.async.error") { checkLoadFail { - ConfigValue.async[IO, String] { cb => cb(Left(new RuntimeException)) } + ConfigValue.async[IO, String] { cb => IO(cb(Left(new RuntimeException))).as(None) } } } test("ConfigValue.async.failed") { check( - ConfigValue.async[IO, String] { cb => cb(Right(failed)) }, + ConfigValue.async[IO, String] { cb => IO(cb(Right(failed))).as(None) }, failed ) } test("ConfigValue.async.loaded") { check( - ConfigValue.async[IO, String] { cb => cb(Right(loaded)) }, + ConfigValue.async[IO, String] { cb => IO(cb(Right(loaded))).as(None) }, loaded ) } test("ConfigValue.async.missing") { check( - ConfigValue.async[IO, String] { cb => cb(Right(missing)) }, + ConfigValue.async[IO, String] { cb => IO(cb(Right(missing))).as(None) }, + missing + ) + } + + test("ConfigValue.async_.default") { + check( + ConfigValue.async_[IO, String] { cb => cb(Right(default)) }, + default + ) + } + + test("ConfigValue.async_.error") { + checkLoadFail { + ConfigValue.async_[IO, String] { cb => cb(Left(new RuntimeException)) } + } + } + + test("ConfigValue.async_.failed") { + check( + ConfigValue.async_[IO, String] { cb => cb(Right(failed)) }, + failed + ) + } + + test("ConfigValue.async_.loaded") { + check( + ConfigValue.async_[IO, String] { cb => cb(Right(loaded)) }, + loaded + ) + } + + test("ConfigValue.async_.missing") { + check( + ConfigValue.async_[IO, String] { cb => cb(Right(missing)) }, missing ) }