Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a module to support julienrf's enum library #108

Merged
merged 1 commit into from
Feb 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ lazy val enumeratum = (project in file("modules/enumeratum")).
settings(settings).
dependsOn(core)

lazy val enum = (project in file("modules/enum")).
settings(settings).
dependsOn(core)

lazy val joda = (project in file("modules/joda")).
settings(settings).
dependsOn(core).
Expand Down
49 changes: 49 additions & 0 deletions modules/enum/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Enum module for PureConfig
Add support for [julienrf's enum](https://github.com/julienrf/enum) library to pureconfig.

## Why

Automatically create a converter to read [enum](https://github.com/julienrf/enum) elements from a configuration.

## Add pureconfig-enum to your project

In addition to [core pureconfig](https://github.com/melrief/pureconfig), you'll need:

```scala
libraryDependencies += "com.github.melrief" %% "pureconfig-enum" % "0.5.1"
```

## Example

```scala
// Given a Greeting ADT composed of `case object`s with an `implicit` `Enum` instance:
sealed trait Greeting

object Greeting {
case object Hello extends Greeting
case object WhisperHello extends Greeting
case object GoodBye extends Greeting
case object ShoutGoodBye extends Greeting

final implicit val EnumInstance: Enum[Greeting] = Enum.derived[Greeting]
}

// And a class to hold the configuration:
case class GreetingConf(start: Greeting, end: Greeting)

// We can read a GreetingConf like:
import pureconfig.loadConfig
import pureconfig.module.enum._
import com.typesafe.config.ConfigFactory.parseString

val conf = parseString("""{
start: WhisperHello
end: ShoutGoodBye
}""")
loadConfig(conf)
// GreetingConf(WhisperHello, ShoutGoodBye)
```

## Can I configure how the elements are read?

Nope. If you need more flexibility, look at [enumeratum](https://github.com/lloydmeta/enumeratum) and its companion pureconfig library, [pureconfig-enumeratum](https://github.com/leifwickland/pureconfig/tree/master/modules/enumeratum).
57 changes: 57 additions & 0 deletions modules/enum/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name := "pureconfig-enum"

organization := "com.github.melrief"

homepage := Some(url("https://github.com/melrief/pureconfig"))

licenses := Seq("Mozilla Public License, version 2.0" -> url("https://www.mozilla.org/MPL/2.0/"))

resolvers ++= Seq(
Resolver.sonatypeRepo("releases"),
Resolver.sonatypeRepo("snapshots")
)

libraryDependencies ++= Seq(
"org.julienrf" %% "enum" % "3.1",
Dependencies.scalaMacrosParadise,
Dependencies.scalaTest
)

publishMavenStyle := true

publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
}

publishArtifact in Test := false

pomExtra := (
<scm>
<url>git@github.com:melrief/pureconfig.git</url>
<connection>scm:git:git@github.com:melrief/pureconfig.git</connection>
</scm>
<developers>
<developer>
<id>melrief</id>
<name>Mario Pastorelli</name>
<url>https://github.com/melrief</url>
</developer>
<developer>
<id>leifwickland</id>
<name>Leif Wickland</name>
<url>https://github.com/leifwickland</url>
</developer>
</developers>)

osgiSettings

OsgiKeys.exportPackage := Seq("pureconfig.module.enum.*")

OsgiKeys.privatePackage := Seq()

OsgiKeys.importPackage := Seq(s"""scala.*;version="[${scalaBinaryVersion.value}.0,${scalaBinaryVersion.value}.50)"""", "*")

22 changes: 22 additions & 0 deletions modules/enum/src/main/scala/pureconfig/module/enum.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package pureconfig.module

import _root_.enum.Enum
import pureconfig.ConfigConvert
import pureconfig.ConfigConvert.{ nonEmptyStringConvert }

import scala.reflect.ClassTag
import scala.util.{ Success, Failure }

package object enum {
implicit def enumConfigConvert[T](implicit e: Enum[T], ct: ClassTag[T]): ConfigConvert[T] = {
nonEmptyStringConvert(
s =>
e.decodeOpt(s)
.map(new Success(_))
.getOrElse {
val err = s"Could not interpret '$s' as a member of ${ct.runtimeClass.getSimpleName}."
new Failure(new IllegalArgumentException(err))
},
e.encode)
}
}
33 changes: 33 additions & 0 deletions modules/enum/src/test/scala/pureconfig/module/enum/EnumTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package pureconfig.module.enum

import com.typesafe.config.ConfigFactory
import org.scalatest.{ FlatSpec, Matchers, TryValues }
import pureconfig.syntax._
import org.scalatest.Inspectors._
import _root_.enum.Enum

sealed trait Greeting

object Greeting {
case object Hello extends Greeting
case object GoodBye extends Greeting
case object ShoutGoodBye extends Greeting

final implicit val EnumInstance: Enum[Greeting] = Enum.derived[Greeting]
}

class EnumTest extends FlatSpec with Matchers with TryValues {
"enum config convert" should "parse an enum" in forAll(Greeting.EnumInstance.values) { greeting =>
val conf = ConfigFactory.parseString(s"""{greeting:"$greeting"}""")
case class Conf(greeting: Greeting)
conf.to[Conf].success.value shouldEqual Conf(greeting)
}

it should "politely refuse an invalid member" in {
val conf = ConfigFactory.parseString(s"""{greeting:"Psych"}""")
case class Conf(greeting: Greeting)
conf.to[Conf].failure.exception.getMessage should include regex "'Psych' as a member of Greeting"
}

}