Skip to content

Commit

Permalink
Explicitly type implicit definitions (#881)
Browse files Browse the repository at this point in the history
* Explicitly type implicit definitions

* Prefer using JavaDuration to distinguish from Scala's Duration
  • Loading branch information
jcazevedo committed Nov 30, 2020
1 parent a27746b commit 2f9a6ff
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 90 deletions.
33 changes: 17 additions & 16 deletions core/src/main/scala/pureconfig/BasicReaders.scala
Expand Up @@ -23,18 +23,18 @@ import scala.util.Try
*/
trait PrimitiveReaders {

implicit val stringConfigReader = ConfigReader.fromCursor(_.asString)
implicit val stringConfigReader: ConfigReader[String] = ConfigReader.fromCursor(_.asString)

implicit val charConfigReader = ConfigReader.fromNonEmptyString[Char](s =>
implicit val charConfigReader: ConfigReader[Char] = ConfigReader.fromNonEmptyString[Char](s =>
s.size match {
case 1 => Right(s.charAt(0))
case len => Left(WrongSizeString(1, len))
}
)

implicit val booleanConfigReader = ConfigReader.fromCursor(_.asBoolean)
implicit val booleanConfigReader: ConfigReader[Boolean] = ConfigReader.fromCursor(_.asBoolean)

implicit val doubleConfigReader = ConfigReader.fromCursor({ cur =>
implicit val doubleConfigReader: ConfigReader[Double] = ConfigReader.fromCursor({ cur =>
val asStringReader = catchReadError({
case v if v.last == '%' => v.dropRight(1).toDouble / 100f
case v => v.toDouble
Expand All @@ -43,7 +43,7 @@ trait PrimitiveReaders {
cur.asString.right.flatMap(s => cur.scopeFailure(asStringReader(s))).left.flatMap(_ => cur.asDouble)
})

implicit val floatConfigReader = ConfigReader.fromCursor({ cur =>
implicit val floatConfigReader: ConfigReader[Float] = ConfigReader.fromCursor({ cur =>
val asStringReader = catchReadError({
case v if v.last == '%' => v.dropRight(1).toFloat / 100f
case v => v.toFloat
Expand All @@ -52,13 +52,13 @@ trait PrimitiveReaders {
cur.asString.right.flatMap(s => cur.scopeFailure(asStringReader(s))).left.flatMap(_ => cur.asFloat)
})

implicit val intConfigReader = ConfigReader.fromCursor(_.asInt)
implicit val intConfigReader: ConfigReader[Int] = ConfigReader.fromCursor(_.asInt)

implicit val longConfigReader = ConfigReader.fromCursor(_.asLong)
implicit val longConfigReader: ConfigReader[Long] = ConfigReader.fromCursor(_.asLong)

implicit val shortConfigReader = ConfigReader.fromCursor(_.asShort)
implicit val shortConfigReader: ConfigReader[Short] = ConfigReader.fromCursor(_.asShort)

implicit val byteConfigReader = ConfigReader.fromCursor(_.asByte)
implicit val byteConfigReader: ConfigReader[Byte] = ConfigReader.fromCursor(_.asByte)
}

/** Trait containing `ConfigReader` instance for Java Enums.
Expand All @@ -76,19 +76,20 @@ trait JavaEnumReader {
*/
trait UriAndPathReaders {

implicit val urlConfigReader = ConfigReader.fromNonEmptyString[URL](catchReadError(new URL(_)))
implicit val uuidConfigReader = ConfigReader.fromNonEmptyString[UUID](catchReadError(UUID.fromString))
implicit val pathConfigReader = ConfigReader.fromString[Path](catchReadError(Paths.get(_)))
implicit val fileConfigReader = ConfigReader.fromString[File](catchReadError(new File(_)))
implicit val uriConfigReader = ConfigReader.fromString[URI](catchReadError(new URI(_)))
implicit val urlConfigReader: ConfigReader[URL] = ConfigReader.fromNonEmptyString[URL](catchReadError(new URL(_)))
implicit val uuidConfigReader: ConfigReader[UUID] =
ConfigReader.fromNonEmptyString[UUID](catchReadError(UUID.fromString))
implicit val pathConfigReader: ConfigReader[Path] = ConfigReader.fromString[Path](catchReadError(Paths.get(_)))
implicit val fileConfigReader: ConfigReader[File] = ConfigReader.fromString[File](catchReadError(new File(_)))
implicit val uriConfigReader: ConfigReader[URI] = ConfigReader.fromString[URI](catchReadError(new URI(_)))
}

/** Trait containing `ConfigReader` instances for classes related to regular expressions.
*/
trait RegexReaders {

implicit val patternReader = ConfigReader.fromString[Pattern](catchReadError(Pattern.compile))
implicit val regexReader = ConfigReader.fromString[Regex](catchReadError(new Regex(_)))
implicit val patternReader: ConfigReader[Pattern] = ConfigReader.fromString[Pattern](catchReadError(Pattern.compile))
implicit val regexReader: ConfigReader[Regex] = ConfigReader.fromString[Regex](catchReadError(new Regex(_)))
}

/** Trait containing `ConfigReader` instances for `java.time` classes.
Expand Down
51 changes: 27 additions & 24 deletions core/src/main/scala/pureconfig/BasicWriters.scala
Expand Up @@ -19,15 +19,15 @@ import com.typesafe.config._
*/
trait PrimitiveWriters {

implicit val stringConfigWriter = ConfigWriter.forPrimitive[String]
implicit val charConfigWriter = ConfigWriter.toDefaultString[Char]
implicit val booleanConfigWriter = ConfigWriter.forPrimitive[Boolean]
implicit val doubleConfigWriter = ConfigWriter.forPrimitive[Double]
implicit val floatConfigWriter = ConfigWriter.forPrimitive[Float]
implicit val intConfigWriter = ConfigWriter.forPrimitive[Int]
implicit val longConfigWriter = ConfigWriter.forPrimitive[Long]
implicit val shortConfigWriter = ConfigWriter.forPrimitive[Short]
implicit val byteConfigWriter = ConfigWriter.forPrimitive[Byte]
implicit val stringConfigWriter: ConfigWriter[String] = ConfigWriter.forPrimitive[String]
implicit val charConfigWriter: ConfigWriter[Char] = ConfigWriter.toDefaultString[Char]
implicit val booleanConfigWriter: ConfigWriter[Boolean] = ConfigWriter.forPrimitive[Boolean]
implicit val doubleConfigWriter: ConfigWriter[Double] = ConfigWriter.forPrimitive[Double]
implicit val floatConfigWriter: ConfigWriter[Float] = ConfigWriter.forPrimitive[Float]
implicit val intConfigWriter: ConfigWriter[Int] = ConfigWriter.forPrimitive[Int]
implicit val longConfigWriter: ConfigWriter[Long] = ConfigWriter.forPrimitive[Long]
implicit val shortConfigWriter: ConfigWriter[Short] = ConfigWriter.forPrimitive[Short]
implicit val byteConfigWriter: ConfigWriter[Byte] = ConfigWriter.forPrimitive[Byte]
}

/** Trait containing instance for `ConfigWriter` for Java Enum.
Expand All @@ -41,45 +41,48 @@ trait JavaEnumWriter {
*/
trait UriAndPathWriters {

implicit val urlConfigWriter = ConfigWriter.toDefaultString[URL]
implicit val uuidConfigWriter = ConfigWriter.toDefaultString[UUID]
implicit val pathConfigWriter = ConfigWriter.toDefaultString[Path]
implicit val fileConfigWriter = ConfigWriter.toDefaultString[File]
implicit val uriConfigWriter = ConfigWriter.toDefaultString[URI]
implicit val urlConfigWriter: ConfigWriter[URL] = ConfigWriter.toDefaultString[URL]
implicit val uuidConfigWriter: ConfigWriter[UUID] = ConfigWriter.toDefaultString[UUID]
implicit val pathConfigWriter: ConfigWriter[Path] = ConfigWriter.toDefaultString[Path]
implicit val fileConfigWriter: ConfigWriter[File] = ConfigWriter.toDefaultString[File]
implicit val uriConfigWriter: ConfigWriter[URI] = ConfigWriter.toDefaultString[URI]
}

/** Trait containing `ConfigWriter` instances for classes related to regular expressions.
*/
trait RegexWriters {

implicit val patternWriter = ConfigWriter.toString[Pattern](_.pattern)
implicit val regexWriter = ConfigWriter.toString[Regex](_.pattern.pattern) // Regex.regex isn't supported until 2.11
implicit val patternWriter: ConfigWriter[Pattern] = ConfigWriter.toString[Pattern](_.pattern)
implicit val regexWriter: ConfigWriter[Regex] =
ConfigWriter.toString[Regex](_.pattern.pattern) // Regex.regex isn't supported until 2.11
}

/** Trait containing `ConfigWriter` instances for `java.time` classes.
*/
trait JavaTimeWriters {

implicit val instantConfigWriter = ConfigWriter.toDefaultString[Instant]
implicit val zoneOffsetConfigWriter = ConfigWriter.toDefaultString[ZoneOffset]
implicit val zoneIdConfigWriter = ConfigWriter.toDefaultString[ZoneId]
implicit val periodConfigWriter = ConfigWriter.toDefaultString[Period]
implicit val instantConfigWriter: ConfigWriter[Instant] = ConfigWriter.toDefaultString[Instant]
implicit val zoneOffsetConfigWriter: ConfigWriter[ZoneOffset] = ConfigWriter.toDefaultString[ZoneOffset]
implicit val zoneIdConfigWriter: ConfigWriter[ZoneId] = ConfigWriter.toDefaultString[ZoneId]
implicit val periodConfigWriter: ConfigWriter[Period] = ConfigWriter.toDefaultString[Period]

// see documentation for [[java.time.Year.parse]]
private[this] def yearToString(year: Year): String =
if (year.getValue > 9999) "+" + year else year.toString

implicit val yearConfigWriter = ConfigWriter.toString[Year](yearToString)
implicit val javaDurationConfigWriter = ConfigWriter.toDefaultString[JavaDuration]
implicit val yearConfigWriter: ConfigWriter[Year] = ConfigWriter.toString[Year](yearToString)
implicit val javaDurationConfigWriter: ConfigWriter[JavaDuration] = ConfigWriter.toDefaultString[JavaDuration]
}

/** Trait containing `ConfigWriter` instances for [[scala.concurrent.duration.Duration]] and
* [[scala.concurrent.duration.FiniteDuration]].
*/
trait DurationWriters {

implicit val durationConfigWriter = ConfigWriter.toString[Duration](DurationUtils.fromDuration)
implicit val finiteDurationConfigWriter = ConfigWriter.toString[FiniteDuration](DurationUtils.fromDuration)
implicit val durationConfigWriter: ConfigWriter[Duration] =
ConfigWriter.toString[Duration](DurationUtils.fromDuration)
implicit val finiteDurationConfigWriter: ConfigWriter[FiniteDuration] =
ConfigWriter.toString[FiniteDuration](DurationUtils.fromDuration)
}

/** Trait containing `ConfigWriter` instances for Java and Scala arbitrary-precision numeric types.
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/pureconfig/CollectionReaders.scala
Expand Up @@ -26,7 +26,7 @@ trait CollectionReaders {
implicit def traversableReader[A, F[A] <: TraversableOnce[A]](implicit
configConvert: Derivation[ConfigReader[A]],
cbf: FactoryCompat[A, F[A]]
) =
): ConfigReader[F[A]] =
new ConfigReader[F[A]] {

override def from(cur: ConfigCursor): ConfigReader.Result[F[A]] = {
Expand All @@ -37,14 +37,14 @@ trait CollectionReaders {
}
}

implicit def mapReader[A](implicit reader: Derivation[ConfigReader[A]]) =
implicit def mapReader[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader[Map[String, A]] =
new ConfigReader[Map[String, A]] {
override def from(cur: ConfigCursor): ConfigReader.Result[Map[String, A]] = {
cur.fluent.mapObject { valueCur => reader.value.from(valueCur) }
}
}

implicit def arrayReader[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]) =
implicit def arrayReader[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]): ConfigReader[Array[A]] =
new ConfigReader[Array[A]] {
override def from(cur: ConfigCursor): ConfigReader.Result[Array[A]] =
cur.fluent.mapList(reader.value.from).right.map(_.toArray)
Expand Down
8 changes: 5 additions & 3 deletions core/src/main/scala/pureconfig/CollectionWriters.scala
Expand Up @@ -29,22 +29,24 @@ trait CollectionWriters {
def toOpt(t: Option[A]): Option[ConfigValue] = t.map(conv.value.to)
}

implicit def traversableWriter[A, F[A] <: TraversableOnce[A]](implicit configConvert: Derivation[ConfigWriter[A]]) =
implicit def traversableWriter[A, F[A] <: TraversableOnce[A]](implicit
configConvert: Derivation[ConfigWriter[A]]
): ConfigWriter[F[A]] =
new ConfigWriter[F[A]] {

override def to(ts: F[A]): ConfigValue = {
ConfigValueFactory.fromIterable(ts.toList.map(configConvert.value.to).asJava)
}
}

implicit def mapWriter[A](implicit configConvert: Derivation[ConfigWriter[A]]) =
implicit def mapWriter[A](implicit configConvert: Derivation[ConfigWriter[A]]): ConfigWriter[Map[String, A]] =
new ConfigWriter[Map[String, A]] {
override def to(keyVals: Map[String, A]): ConfigValue = {
ConfigValueFactory.fromMap(keyVals.mapValues(configConvert.value.to).toMap.asJava)
}
}

implicit def arrayWriter[A](implicit writer: Derivation[ConfigWriter[A]]) =
implicit def arrayWriter[A](implicit writer: Derivation[ConfigWriter[A]]): ConfigWriter[Array[A]] =
new ConfigWriter[Array[A]] {
override def to(a: Array[A]): ConfigValue =
ConfigValueFactory.fromIterable(a.toList.map(writer.value.to).asJava)
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/pureconfig/ConfigConvert.scala
Expand Up @@ -53,7 +53,7 @@ object ConfigConvert extends ConvertHelpers {
implicit def fromReaderAndWriter[A](implicit
reader: Derivation[ConfigReader[A]],
writer: Derivation[ConfigWriter[A]]
) =
): ConfigConvert[A] =
ConfigConvert(reader.value, writer.value)

def viaString[A](fromF: String => Either[FailureReason, A], toF: A => String): ConfigConvert[A] =
Expand Down
Expand Up @@ -2,36 +2,36 @@ package pureconfig.module.joda.configurable

import org.joda.time._
import org.joda.time.format.{DateTimeFormat, ISOPeriodFormat, PeriodFormatter}
import pureconfig.BaseSuite
import pureconfig.{BaseSuite, ConfigConvert}
import pureconfig.module.joda.arbitrary._

class ConfigurableSuite extends BaseSuite {

val isoFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ")
implicit val dateTimeInstance = dateTimeConfigConvert(isoFormatter)
implicit val dateTimeInstance: ConfigConvert[DateTime] = dateTimeConfigConvert(isoFormatter)
checkArbitrary[DateTime]

val timeFormatter = DateTimeFormat.forPattern("HH:mm:ss.SSS")
implicit val localTimeInstance = localTimeConfigConvert(timeFormatter)
implicit val localTimeInstance: ConfigConvert[LocalTime] = localTimeConfigConvert(timeFormatter)
checkArbitrary[LocalTime]

val dateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd")
implicit val localDateInstance = localDateConfigConvert(dateFormatter)
implicit val localDateInstance: ConfigConvert[LocalDate] = localDateConfigConvert(dateFormatter)
checkArbitrary[LocalDate]

val dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
implicit val localDateTimeInstance = localDateTimeConfigConvert(dateTimeFormatter)
implicit val localDateTimeInstance: ConfigConvert[LocalDateTime] = localDateTimeConfigConvert(dateTimeFormatter)
checkArbitrary[LocalDateTime]

val monthDayFormat = DateTimeFormat.forPattern("MM-dd")
implicit val monthDayInstance = monthDayConfigConvert(monthDayFormat)
implicit val monthDayInstance: ConfigConvert[MonthDay] = monthDayConfigConvert(monthDayFormat)
checkArbitrary[MonthDay]

val yearMonthFormat = DateTimeFormat.forPattern("yyyy-MM")
implicit val yearMonthInstance = yearMonthConfigConvert(yearMonthFormat)
implicit val yearMonthInstance: ConfigConvert[YearMonth] = yearMonthConfigConvert(yearMonthFormat)
checkArbitrary[YearMonth]

val periodFormatter: PeriodFormatter = ISOPeriodFormat.standard()
implicit val periodInstance = periodConfigConvert(periodFormatter)
implicit val periodInstance: ConfigConvert[Period] = periodConfigConvert(periodFormatter)
checkArbitrary[Period]
}
Expand Up @@ -62,19 +62,19 @@ class DerivationModesSuite extends BaseSuite {
it should "provide methods to derive readers on demand" in {
import pureconfig.module.magnolia.semiauto.reader._

implicit val personReader = deriveReader[Person]
implicit val placeReader = deriveReader[Place]
implicit val entityReader = deriveReader[Entity]
implicit val personReader: ConfigReader[Person] = deriveReader[Person]
implicit val placeReader: ConfigReader[Place] = deriveReader[Place]
implicit val entityReader: ConfigReader[Entity] = deriveReader[Entity]

ConfigReader[Entity].from(conf.root) shouldBe Right(person)
}

it should "provide methods to derive writers on demand" in {
import pureconfig.module.magnolia.semiauto.writer._

implicit val personWriter = deriveWriter[Person]
implicit val placeWriter = deriveWriter[Place]
implicit val entityWriter = deriveWriter[Entity]
implicit val personWriter: ConfigWriter[Person] = deriveWriter[Person]
implicit val placeWriter: ConfigWriter[Place] = deriveWriter[Place]
implicit val entityWriter: ConfigWriter[Entity] = deriveWriter[Entity]

ConfigWriter[Entity].to(person) shouldBe conf.root()
}
Expand Down
53 changes: 31 additions & 22 deletions testkit/src/main/scala/pureconfig/arbitrary/package.scala
@@ -1,31 +1,40 @@
package pureconfig

import java.io.File
import java.math.BigInteger
import java.nio.file.Path
import java.time.{Duration => JavaDuration, _}
import java.util.UUID

import scala.concurrent.duration.{Duration, FiniteDuration}

import org.scalacheck.{Arbitrary, Gen}
import pureconfig.data.Percentage
import pureconfig.gen._

package object arbitrary {

implicit val arbDuration = Arbitrary(genDuration)
implicit val arbJavaDuration = Arbitrary(genJavaDuration)
implicit val arbFiniteDuration = Arbitrary(genFiniteDuration)
implicit val arbInstant = Arbitrary(genInstant)
implicit val arbPeriod = Arbitrary(genPeriod)
implicit val arbYear = Arbitrary(genYear)
implicit val arbUUID = Arbitrary(Gen.uuid)
implicit val arbPath = Arbitrary(genPath)
implicit val arbFile = Arbitrary(genFile)
implicit val arbPercentage = Arbitrary(genPercentage)
implicit val arbJavaBigDecimal = Arbitrary(genJavaBigDecimal)
implicit val arbJavaBigInteger = Arbitrary(genBigInt)
implicit val arbDuration: Arbitrary[Duration] = Arbitrary(genDuration)
implicit val arbJavaDuration: Arbitrary[JavaDuration] = Arbitrary(genJavaDuration)
implicit val arbFiniteDuration: Arbitrary[FiniteDuration] = Arbitrary(genFiniteDuration)
implicit val arbInstant: Arbitrary[Instant] = Arbitrary(genInstant)
implicit val arbPeriod: Arbitrary[Period] = Arbitrary(genPeriod)
implicit val arbYear: Arbitrary[Year] = Arbitrary(genYear)
implicit val arbUUID: Arbitrary[UUID] = Arbitrary(Gen.uuid)
implicit val arbPath: Arbitrary[Path] = Arbitrary(genPath)
implicit val arbFile: Arbitrary[File] = Arbitrary(genFile)
implicit val arbPercentage: Arbitrary[Percentage] = Arbitrary(genPercentage)
implicit val arbJavaBigDecimal: Arbitrary[java.math.BigDecimal] = Arbitrary(genJavaBigDecimal)
implicit val arbJavaBigInteger: Arbitrary[BigInteger] = Arbitrary(genBigInt)

implicit val arbLocalTime = Arbitrary(genLocalTime)
implicit val arbLocalDate = Arbitrary(genLocalDate)
implicit val arbLocalDateTime = Arbitrary(genLocalDateTime)
implicit val arbMonthDay = Arbitrary(genMonthDay)
implicit val arbZoneOffset = Arbitrary(genZoneOffset)
implicit val arbOffsetDateTime = Arbitrary(genOffsetDateTime)
implicit val arbOffsetTime = Arbitrary(genOffsetTime)
implicit val arbYearMonth = Arbitrary(genYearMonth)
implicit val arbZoneId = Arbitrary(genZoneId)
implicit val arbZonedDateTime = Arbitrary(genZonedDateTime)
implicit val arbLocalTime: Arbitrary[LocalTime] = Arbitrary(genLocalTime)
implicit val arbLocalDate: Arbitrary[LocalDate] = Arbitrary(genLocalDate)
implicit val arbLocalDateTime: Arbitrary[LocalDateTime] = Arbitrary(genLocalDateTime)
implicit val arbMonthDay: Arbitrary[MonthDay] = Arbitrary(genMonthDay)
implicit val arbZoneOffset: Arbitrary[ZoneOffset] = Arbitrary(genZoneOffset)
implicit val arbOffsetDateTime: Arbitrary[OffsetDateTime] = Arbitrary(genOffsetDateTime)
implicit val arbOffsetTime: Arbitrary[OffsetTime] = Arbitrary(genOffsetTime)
implicit val arbYearMonth: Arbitrary[YearMonth] = Arbitrary(genYearMonth)
implicit val arbZoneId: Arbitrary[ZoneId] = Arbitrary(genZoneId)
implicit val arbZonedDateTime: Arbitrary[ZonedDateTime] = Arbitrary(genZonedDateTime)
}
2 changes: 1 addition & 1 deletion testkit/src/main/scala/pureconfig/data/Percentage.scala
Expand Up @@ -13,6 +13,6 @@ object Percentage {
Left(CannotConvert(s, "Percentage", "Percentage is a dummy type, you should not read it"))
}

implicit val percentageConfigWriter =
implicit val percentageConfigWriter: ConfigConvert[Percentage] =
ConfigConvert.viaNonEmptyString[Percentage](failConfigReadPercentage, percentage => s"${percentage.value} %")
}
4 changes: 2 additions & 2 deletions testkit/src/main/scala/pureconfig/equality/package.scala
Expand Up @@ -9,15 +9,15 @@ import org.scalactic.TypeCheckedTripleEquals._

package object equality {

implicit final val PatternEquality = new Equality[Pattern] {
implicit final val PatternEquality: Equality[Pattern] = new Equality[Pattern] {
def areEqual(a: Pattern, b: Any): Boolean =
b match {
case bp: Pattern => a.pattern === bp.pattern
case _ => false
}
}

implicit final val RegexEquality = new Equality[Regex] {
implicit final val RegexEquality: Equality[Regex] = new Equality[Regex] {
override def areEqual(a: Regex, b: Any): Boolean =
b match {
case r: Regex => PatternEquality.areEqual(a.pattern, r.pattern)
Expand Down

0 comments on commit 2f9a6ff

Please sign in to comment.