diff --git a/modules/core/shared/src/main/scala/ciris/ConfigValue.scala b/modules/core/shared/src/main/scala/ciris/ConfigValue.scala index 313e797e..b713cbb1 100644 --- a/modules/core/shared/src/main/scala/ciris/ConfigValue.scala +++ b/modules/core/shared/src/main/scala/ciris/ConfigValue.scala @@ -46,6 +46,28 @@ abstract class ConfigValue[F[_]: Apply, V] { } } + /** + * If the value of this [[ConfigValue]] is available, wraps the + * value in a `Some`; otherwise, uses `None` as the value, and + * discards the [[ConfigError]]. This function is particularly + * useful when combined with [[orElse]] as in the example. + * + * @return a new [[ConfigValue]] + * @example {{{ + * scala> env[String]("API_KEY"). + * | orElse(prop[String]("api.key")). + * | orNone + * res0: ConfigValue[api.Id,Option[String]] = ConfigValue(Right(None)) + * }}} + */ + final def orNone: ConfigValue[F, Option[V]] = + ConfigValue.applyF[F, Option[V]] { + this.value.map { + case Right(v) => Right(Some(v)) + case Left(_) => Right(None) + } + } + private[ciris] final def append[A](next: ConfigValue[F, A]): ConfigValue2[F, V, A] = { new ConfigValue2((this.value product next.value).map { case (Right(v), Right(a)) => Right((v, a)) diff --git a/tests/shared/src/test/scala/ciris/ConfigValueSpec.scala b/tests/shared/src/test/scala/ciris/ConfigValueSpec.scala index c4c85e64..21378bfa 100644 --- a/tests/shared/src/test/scala/ciris/ConfigValueSpec.scala +++ b/tests/shared/src/test/scala/ciris/ConfigValueSpec.scala @@ -25,5 +25,19 @@ final class ConfigValueSpec extends PropertySpec { ConfigValue.applyF[Id, Int](right(123)).toString shouldBe "ConfigValue(Right(123))" } } + + "using orNone" should { + "wrap existing values in Some" in { + nonExistingEntry + .orElse(existingEntry("value")) + .orNone.value shouldBe Right(Some("value")) + } + + "discard errors and return None" in { + nonExistingEntry + .orElse(nonExistingEntry) + .orNone.value shouldBe Right(None) + } + } } }