-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Jakub Janeček <[email protected]> Co-authored-by: Pavel Kefurt <[email protected]>
- Loading branch information
1 parent
1a63572
commit 36f3760
Showing
8 changed files
with
459 additions
and
5 deletions.
There are no files selected for viewing
This file contains 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
16 changes: 16 additions & 0 deletions
16
pureconfig/src/main/scala/com/avast/sst/pureconfig/WithConfig.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.avast.sst.pureconfig | ||
|
||
import com.typesafe.config.Config | ||
import pureconfig.ConfigReader | ||
|
||
/** Used to retrieve both parsed configuration object and underlying [[Config]] instance. */ | ||
final case class WithConfig[T](value: T, config: Config) | ||
|
||
object WithConfig { | ||
implicit def configReader[T: ConfigReader]: ConfigReader[WithConfig[T]] = | ||
for { | ||
config <- ConfigReader[Config] | ||
value <- ConfigReader[T] | ||
} yield WithConfig(value, config) | ||
|
||
} |
114 changes: 114 additions & 0 deletions
114
pureconfig/src/main/scala/com/avast/sst/pureconfig/util/Toggle.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package com.avast.sst.pureconfig.util | ||
|
||
import cats.{FlatMap, Functor, Monad, Monoid, Order, Semigroup} | ||
|
||
import java.util.Collections | ||
import com.typesafe.config.ConfigValueFactory | ||
import pureconfig.{ConfigReader, ConfigWriter} | ||
|
||
import scala.annotation.tailrec | ||
|
||
sealed trait Toggle[+T] { | ||
def toOption: Option[T] | ||
def fold[A](empty: => A, fromValue: T => A): A | ||
def isEmpty: Boolean | ||
} | ||
|
||
object Toggle { | ||
final case class Enabled[+T](value: T) extends Toggle[T] { | ||
override def toOption: Option[T] = Some(value) | ||
override def fold[A](empty: => A, fromValue: T => A): A = fromValue(value) | ||
override def isEmpty: Boolean = false | ||
def get: T = value | ||
} | ||
case object Disabled extends Toggle[Nothing] { | ||
override def toOption: Option[Nothing] = None | ||
override def fold[A](empty: => A, fromValue: Nothing => A): A = empty | ||
override def isEmpty: Boolean = true | ||
} | ||
|
||
object TogglePureConfigInstances { | ||
implicit def toggleConfigReader[T: ConfigReader]: ConfigReader[Toggle[T]] = { | ||
ConfigReader | ||
.forProduct1[ConfigReader[Toggle[T]], Boolean]("enabled") { enabled => | ||
if (enabled) implicitly[ConfigReader[T]].map(Enabled[T]) | ||
else ConfigReader.fromCursor(_ => Right(Disabled)) | ||
} | ||
.flatMap(identity) | ||
} | ||
|
||
implicit def toggleConfigWriter[T: ConfigWriter]: ConfigWriter[Toggle[T]] = { | ||
ConfigWriter.fromFunction[Toggle[T]] { | ||
case Enabled(value) => ConfigWriter[T].to(value).withFallback(ConfigValueFactory.fromMap(Collections.singletonMap("enabled", true))) | ||
case Disabled => ConfigValueFactory.fromMap(Collections.singletonMap("enabled", false)) | ||
} | ||
} | ||
} | ||
|
||
object ToggleStdInstances { | ||
implicit val functorForToggle: Functor[Toggle] = new ToggleFunctor | ||
implicit val flatMapForToggle: FlatMap[Toggle] = new ToggleFlatMap | ||
implicit val monadForToggle: Monad[Toggle] = new ToggleMonad | ||
implicit def monoidForToggle[A: Semigroup]: Monoid[Toggle[A]] = new ToggleMonoid[A] | ||
implicit def orderForToggle[A: Order]: Order[Toggle[A]] = new ToggleOrder[A] | ||
} | ||
|
||
class ToggleFunctor extends Functor[Toggle] { | ||
override def map[A, B](fa: Toggle[A])(f: A => B): Toggle[B] = { | ||
fa match { | ||
case Enabled(value) => Enabled(f(value)) | ||
case Disabled => Disabled | ||
} | ||
} | ||
} | ||
|
||
class ToggleFlatMap extends ToggleFunctor with FlatMap[Toggle] { | ||
override def flatMap[A, B](fa: Toggle[A])(f: A => Toggle[B]): Toggle[B] = { | ||
fa match { | ||
case Enabled(value) => f(value) | ||
case Disabled => Disabled | ||
} | ||
} | ||
|
||
override def tailRecM[A, B](a: A)(f: A => Toggle[Either[A, B]]): Toggle[B] = tailRecMPrivate(a)(f) | ||
|
||
@tailrec | ||
private def tailRecMPrivate[A, B](a: A)(f: A => Toggle[Either[A, B]]): Toggle[B] = { | ||
f(a) match { | ||
case Enabled(Left(value)) => tailRecMPrivate(value)(f) | ||
case Enabled(Right(value)) => Enabled(value) | ||
case Disabled => Disabled | ||
} | ||
} | ||
} | ||
|
||
class ToggleMonad extends ToggleFlatMap with Monad[Toggle] { | ||
override def pure[A](x: A): Toggle[A] = Enabled(x) | ||
} | ||
|
||
class ToggleMonoid[A](implicit A: Semigroup[A]) extends Monoid[Toggle[A]] { | ||
override def empty: Toggle[A] = Disabled | ||
override def combine(x: Toggle[A], y: Toggle[A]): Toggle[A] = | ||
x match { | ||
case Disabled => y | ||
case Enabled(a) => | ||
y match { | ||
case Disabled => x | ||
case Enabled(b) => Enabled(A.combine(a, b)) | ||
} | ||
} | ||
} | ||
|
||
class ToggleOrder[A](implicit A: Order[A]) extends Order[Toggle[A]] { | ||
override def compare(x: Toggle[A], y: Toggle[A]): Int = | ||
x match { | ||
case Disabled => if (y.isEmpty) 0 else -1 | ||
case Enabled(a) => | ||
y match { | ||
case Disabled => 1 | ||
case Enabled(b) => A.compare(a, b) | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains 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
111 changes: 111 additions & 0 deletions
111
pureconfig/src/test/scala-2/com/avast/sst/pureconfig/ToggleTest.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package com.avast.sst.pureconfig | ||
|
||
import cats.{Applicative, Eq, FlatMap, Functor, Monad, Monoid} | ||
import com.avast.sst.pureconfig.util.Toggle | ||
import com.avast.sst.pureconfig.util.Toggle.{Disabled, Enabled} | ||
import org.scalatest.diagrams.Diagrams | ||
import org.scalatest.funsuite.AnyFunSuite | ||
|
||
class ToggleTest extends AnyFunSuite with Diagrams { | ||
|
||
test("has Functor instance and map method works correctly") { | ||
import com.avast.sst.pureconfig.util.Toggle.ToggleStdInstances._ | ||
|
||
val oldValue = "some value" | ||
val newValue = "new value" | ||
val toggle: Toggle[String] = Enabled(oldValue) | ||
val toggle2: Toggle[String] = Disabled | ||
val result = Functor[Toggle].map(toggle)(_ => newValue) | ||
|
||
import cats.syntax.functor._ | ||
val result2 = toggle.map(_ => newValue) | ||
|
||
val result3 = toggle2.map(_ => newValue) | ||
|
||
assert(result.fold(false, value => value === newValue)) | ||
assert(result2.fold(false, value => value === newValue)) | ||
assert(result3.fold(true, _ => false)) | ||
} | ||
|
||
test("has FlatMap instance and flatMap method works correctly") { | ||
import com.avast.sst.pureconfig.util.Toggle.ToggleStdInstances._ | ||
|
||
val oldValue = "some value" | ||
val newValue = "new value" | ||
val toggle: Toggle[String] = Enabled(oldValue) | ||
val toggle2: Toggle[String] = Disabled | ||
val result = FlatMap[Toggle].flatMap(toggle)(_ => Enabled(newValue)) | ||
|
||
import cats.syntax.flatMap._ | ||
val result2 = toggle.flatMap(_ => Enabled(newValue)) | ||
|
||
val result3 = toggle2.flatMap(_ => Enabled(newValue)) | ||
val result4 = toggle.flatMap(_ => Disabled) | ||
|
||
assert(result.fold(false, value => value === newValue)) | ||
assert(result2.fold(false, value => value === newValue)) | ||
assert(result3 === Disabled) | ||
assert(result4 === Disabled) | ||
} | ||
|
||
test("has Applicative and Monad instance and pure method works correctly") { | ||
import com.avast.sst.pureconfig.util.Toggle.ToggleStdInstances._ | ||
val value = "some value" | ||
val result = Applicative[Toggle].pure(value) | ||
val result2 = Monad[Toggle].pure(value) | ||
|
||
import cats.syntax.applicative._ | ||
val result3 = value.pure[Toggle] | ||
|
||
assert(result.fold(false, value => value === value)) | ||
assert(result2.fold(false, value => value === value)) | ||
assert(result3.fold(false, value => value === value)) | ||
} | ||
|
||
test("has Monoid instance and combine method works correctly") { | ||
import com.avast.sst.pureconfig.util.Toggle.ToggleStdInstances._ | ||
|
||
val result = Monoid[Toggle[String]].empty | ||
|
||
val value1 = 1 | ||
val value2 = 2 | ||
val toggle1: Toggle[Int] = Enabled(value1) | ||
val toggle2: Toggle[Int] = Enabled(value2) | ||
val toggle3: Toggle[Int] = Disabled | ||
|
||
import cats.syntax.monoid._ | ||
val result2 = toggle1.combine(toggle2) | ||
val result3 = toggle1.combine(Disabled) | ||
val result4 = toggle3.combine(toggle2) | ||
val result5 = toggle3.combine(Disabled) | ||
|
||
assert(result === Disabled) | ||
assert(result2.fold(false, value => value === 3)) | ||
assert(result3.fold(false, value => value === 1)) | ||
assert(result4.fold(false, value => value === 2)) | ||
assert(result5 === Disabled) | ||
} | ||
|
||
test("has Order instance and compare method works correctly") { | ||
import com.avast.sst.pureconfig.util.Toggle.ToggleStdInstances._ | ||
|
||
val value1 = 1 | ||
val value2 = 2 | ||
val toggle1: Toggle[Int] = Enabled(value1) | ||
val toggle2: Toggle[Int] = Enabled(value2) | ||
val toggle3: Toggle[Int] = Disabled | ||
|
||
import cats.syntax.order._ | ||
val result1 = toggle1.compare(toggle2) | ||
val result2 = toggle1.compare(toggle1) | ||
val result3 = toggle2.compare(toggle1) | ||
val result4 = toggle3.compare(toggle2) | ||
val result5 = toggle3.compare(Disabled) | ||
|
||
assert(result1 < 0) | ||
assert(Eq[Int].eqv(result2, 0)) | ||
assert(result3 > 0) | ||
assert(result4 < 0) | ||
assert(Eq[Int].eqv(result5, 0)) | ||
} | ||
} |
This file contains 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
Oops, something went wrong.