Skip to content

Commit

Permalink
Make ConfigKeyNaming immutable
Browse files Browse the repository at this point in the history
  • Loading branch information
kxbmap committed Feb 25, 2021
1 parent 769d597 commit 96a1384
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 21 deletions.
56 changes: 40 additions & 16 deletions core/src/main/scala/configs/ConfigKeyNaming.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,55 @@ package configs
import configs.ConfigUtil.splitWords
import java.util.Locale

trait ConfigKeyNaming[A] { self =>
sealed trait ConfigKeyNaming[A] {

def apply(field: String): Seq[String]

def applyFirst(field: String): String = apply(field).head
def applyFirst(field: String): String

def andThen(f: String => Seq[String]): ConfigKeyNaming[A] =
field => self.apply(field).flatMap(f)
def andThen(f: String => Seq[String]): ConfigKeyNaming[A]

def or(f: String => Seq[String]): ConfigKeyNaming[A] =
field => (self.apply(field) ++ f(field)).distinct
def or(f: String => Seq[String]): ConfigKeyNaming[A]

/**
* Enable fail on parsing an object and there are superfluous keys in the config
*/
def withFailOnSuperfluousKeys(): ConfigKeyNaming[A] = {
failOnSuperfluousKeys = true
this
}
def withFailOnSuperfluousKeys: ConfigKeyNaming[A]

def failOnSuperfluousKeys: Boolean

var failOnSuperfluousKeys: Boolean = false
}

object ConfigKeyNaming {

def apply[A](f: String => String): ConfigKeyNaming[A] =
apply(f.andThen(Seq(_)))

def apply[A](f: String => Seq[String])(implicit dummy: DummyImplicit): ConfigKeyNaming[A] =
new Impl(f, false).asInstanceOf[ConfigKeyNaming[A]]


private final class Impl(f: String => Seq[String], val failOnSuperfluousKeys: Boolean) extends ConfigKeyNaming[Nothing] {
self =>

def apply(field: String): Seq[String] =
f(field)

def applyFirst(field: String): String =
apply(field).head

def andThen(f: String => Seq[String]): ConfigKeyNaming[Nothing] =
new Impl(self.f.andThen(_.flatMap(f)), failOnSuperfluousKeys)

def or(f: String => Seq[String]): ConfigKeyNaming[Nothing] =
new Impl(field => (apply(field) ++ f(field)).distinct, failOnSuperfluousKeys)

def withFailOnSuperfluousKeys: ConfigKeyNaming[Nothing] =
new Impl(f, true)

}


implicit def defaultNaming[A]: ConfigKeyNaming[A] =
hyphenSeparated[A]

Expand All @@ -52,28 +76,28 @@ object ConfigKeyNaming {
_identity.asInstanceOf[ConfigKeyNaming[A]]

private[this] val _identity: ConfigKeyNaming[Any] =
x => Seq(x)
ConfigKeyNaming(x => x)


def hyphenSeparated[A]: ConfigKeyNaming[A] =
_hyphenSeparated.asInstanceOf[ConfigKeyNaming[A]]

private[this] val _hyphenSeparated: ConfigKeyNaming[Any] =
x => Seq(splitWords(x).mkString("-").toLowerCase(Locale.ROOT))
ConfigKeyNaming(x => splitWords(x).mkString("-").toLowerCase(Locale.ROOT))


def snakeCase[A]: ConfigKeyNaming[A] =
_snakeCase.asInstanceOf[ConfigKeyNaming[A]]

private[this] val _snakeCase: ConfigKeyNaming[Any] =
x => Seq(splitWords(x).mkString("_").toLowerCase(Locale.ROOT))
ConfigKeyNaming(x => splitWords(x).mkString("_").toLowerCase(Locale.ROOT))


def lowerCamelCase[A]: ConfigKeyNaming[A] =
_lowerCamelCase.asInstanceOf[ConfigKeyNaming[A]]

private[this] val _lowerCamelCase: ConfigKeyNaming[Any] =
x => Seq(splitWords(x) match {
ConfigKeyNaming(x => splitWords(x) match {
case Nil => ""
case h :: t => (h.toLowerCase(Locale.ROOT) :: t.map(_.capitalize)).mkString
})
Expand All @@ -83,6 +107,6 @@ object ConfigKeyNaming {
_upperCamelCase.asInstanceOf[ConfigKeyNaming[A]]

private[this] val _upperCamelCase: ConfigKeyNaming[Any] =
x => Seq(splitWords(x).map(_.capitalize).mkString)
ConfigKeyNaming(x => splitWords(x).map(_.capitalize).mkString)

}
10 changes: 5 additions & 5 deletions core/src/test/scala/configs/instance/CaseClassTypesTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ object CaseClassTypesTest extends Scalaprops {
val failOnSuperfluousConfig = {
implicit def myDefaultNaming[A]: ConfigKeyNaming[A] =
ConfigKeyNaming.lowerCamelCase[A].or(ConfigKeyNaming.hyphenSeparated[A].apply)
.withFailOnSuperfluousKeys()
.withFailOnSuperfluousKeys
forAll {
val reader = ConfigReader.derive[ComplexClass]
val configStr = """
Expand All @@ -87,7 +87,7 @@ object CaseClassTypesTest extends Scalaprops {
// generic default naming
implicit def myDefaultNaming[A]: ConfigKeyNaming[A] =
ConfigKeyNaming.hyphenSeparated[A].or(ConfigKeyNaming.lowerCamelCase[A].apply)
.withFailOnSuperfluousKeys()
.withFailOnSuperfluousKeys
forAll {
val reader = ConfigReader.derive[TestSeal]
val configStr = """
Expand All @@ -112,7 +112,7 @@ object CaseClassTypesTest extends Scalaprops {
// generic default naming
implicit def myDefaultNaming[A]: ConfigKeyNaming[A] =
ConfigKeyNaming.hyphenSeparated[A].or(ConfigKeyNaming.lowerCamelCase[A].apply)
.withFailOnSuperfluousKeys()
.withFailOnSuperfluousKeys
forAll {
val reader = ConfigReader.derive[TestSeal]
val configStr = """
Expand All @@ -133,7 +133,7 @@ object CaseClassTypesTest extends Scalaprops {
// generic default naming
implicit def myDefaultNaming[A]: ConfigKeyNaming[A] =
ConfigKeyNaming.hyphenSeparated[A].or(ConfigKeyNaming.lowerCamelCase[A].apply)
.withFailOnSuperfluousKeys()
.withFailOnSuperfluousKeys
forAll {
val reader = ConfigReader.derive[TestClass]
val configStr = """
Expand All @@ -159,7 +159,7 @@ object CaseClassTypesTest extends Scalaprops {
// generic default naming
implicit def myDefaultNaming[A]: ConfigKeyNaming[A] =
ConfigKeyNaming.hyphenSeparated[A].or(ConfigKeyNaming.lowerCamelCase[A].apply)
.withFailOnSuperfluousKeys()
.withFailOnSuperfluousKeys
forAll {
val reader = ConfigReader.derive[TestClass]
val configStr = """
Expand Down

0 comments on commit 96a1384

Please sign in to comment.