Skip to content

Commit

Permalink
ValueReader instances for Either[L,R] (#45)
Browse files Browse the repository at this point in the history
* eitherReader: valueReader implementation for Either[A,B] given A and B have their own value readers.

* eitherReader: minor test utility.

* eitherReader: test spec for either.

* eitherReader: removed auto generated ide comments

* eitherReader: minor fixes according to review comments.
  • Loading branch information
eyalfa authored and kailuowang committed Nov 28, 2016
1 parent 5609b7f commit f5f7f57
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/main/scala/net/ceedubs/ficus/readers/EitherReader.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.ceedubs.ficus.readers
import com.typesafe.config.{Config, ConfigException}

trait EitherReader {
implicit def eitherReader[L,R]( implicit lReader : ValueReader[L], rReader : ValueReader[R]) : ValueReader[Either[L,R]] =
new ValueReader[Either[L,R]]{
/** Reads the value at the path `path` in the Config */
override def read(config: Config, path: String): Either[L, R] = {
TryReader.tryValueReader(rReader).read( config, path )
.map( Right(_) )
.recover{
case _ : ConfigException => Left( lReader.read(config, path))
}
.get
}
}
}

object EitherReader extends EitherReader
4 changes: 3 additions & 1 deletion src/test/scala/net/ceedubs/ficus/ConfigSerializer.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.ceedubs.ficus

import com.typesafe.config.ConfigUtil
import com.typesafe.config.{ConfigFactory, ConfigUtil, ConfigValue}

import scala.language.implicitConversions

trait ConfigSerializer[A] {
Expand Down Expand Up @@ -45,6 +46,7 @@ object ConfigSerializer {

final case class ConfigSerializerOps[A](a: A, serializer: ConfigSerializer[A]) {
def asConfigValue: String = serializer.serialize(a)
def toConfigValue : ConfigValue = ConfigFactory.parseString( s"dummy=$asConfigValue").root().get("dummy")
}

object ConfigSerializerOps {
Expand Down
63 changes: 63 additions & 0 deletions src/test/scala/net/ceedubs/ficus/readers/EitherReadersSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package net.ceedubs.ficus.readers

import com.typesafe.config.ConfigFactory
import net.ceedubs.ficus.{ConfigSerializer, Spec}
import net.ceedubs.ficus.ConfigSerializerOps._
import org.scalacheck.Arbitrary

import scala.util.{Failure, Try}
import scala.collection.JavaConverters._

class EitherReadersSpec extends Spec with EitherReader with OptionReader with AnyValReaders with StringReader with TryReader with CollectionReaders{
def is=s2"""
An Either value reader should
should read right side when possible $readRightSideString
fallback to left side when key is missing $fallbackToLeftSideOnMissingKey
fallback to left when failing to read right $fallbackToLeftSideOnBadRightValue
fail when both sides fail $rightAndLeftFailure
handle a Try on the right side $rightSideTry
handle a Try on the left side $leftSideTry
handle complex types $handleComplexTypes
"""


def readRightSideString = prop{ a : String =>
val cfg = a.toConfigValue.atKey("x")
eitherReader[String,String].read(cfg, "x") must beEqualTo(Right(a))
}

def fallbackToLeftSideOnMissingKey = prop{ a : String =>
eitherReader[Option[String], String].read( ConfigFactory.empty(), "x" ) must beEqualTo( Left(None) )
}

def fallbackToLeftSideOnBadRightValue = prop{ a : Int =>
val badVal = a.toString + "xx"
eitherReader[String, Int].read( badVal.toConfigValue.atKey("x"), "x" ) must beEqualTo( Left(badVal) )
}

def rightAndLeftFailure = prop{ a : Int =>
val badVal = a.toString + "xx"
tryValueReader(eitherReader[Int, Int]).read( badVal.toConfigValue.atKey("x"), "x" ) must beAnInstanceOf[Failure[Int]]
}

def rightSideTry = prop{ a : Int =>
val badVal = a.toString + "xx"
eitherReader[Int, Try[Int]].read( a.toConfigValue.atKey("x"), "x" ) must beRight( a )
eitherReader[Int, Try[Int]].read( badVal.toConfigValue.atKey("x"), "x" ) must beRight( beFailedTry[Int] )
}

def leftSideTry = prop{ a : Int =>
val badVal = a.toString + "xx"
eitherReader[Try[String], Int].read( badVal.toConfigValue.atKey("x"), "x" ) must beLeft( beSuccessfulTry( badVal) )
eitherReader[Try[Int], Int].read( badVal.toConfigValue.atKey("x"), "x" ) must beLeft( beFailedTry[Int] )
}

def handleComplexTypes = prop{ (a : Int, b : Int ) =>
val iMap = Map( "a" -> a, "b" -> b )
val sMap = Map( "a" -> s"${a}xx", "b" -> s"${b}xx")

eitherReader[Map[String,String], Map[String,String]].read( sMap.toConfigValue.atKey("a"), "a" ) must beRight(sMap)
eitherReader[Map[String,String], Map[String,Int]].read( iMap.toConfigValue.atKey("a"), "a" ) must beRight(iMap)
eitherReader[Map[String,String], Map[String,Int]].read( sMap.toConfigValue.atKey("a"), "a" ) must beLeft(sMap)
}
}

0 comments on commit f5f7f57

Please sign in to comment.