-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #65 from ekrich/topic/sconfig
Change to Scala based HOCON config library for JVM
- Loading branch information
Showing
7 changed files
with
283 additions
and
8 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
82 changes: 82 additions & 0 deletions
82
metaconfig-sconfig/src/main/scala/metaconfig/sconfig/SConfig2Class.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,82 @@ | ||
package metaconfig | ||
package sconfig | ||
|
||
import org.ekrich.config._ | ||
import scala.collection.JavaConverters._ | ||
import scala.collection.mutable | ||
import metaconfig.internal.ConfGet | ||
|
||
object SConfig2Class { | ||
def gimmeConfFromString(string: String): Configured[Conf] = | ||
gimmeSafeConf(() => ConfigFactory.parseString(string)) | ||
def gimmeConfFromFile(file: java.io.File): Configured[Conf] = { | ||
if (!file.exists()) | ||
Configured.NotOk(ConfError.fileDoesNotExist(file.getAbsolutePath)) | ||
else if (file.isDirectory) | ||
Configured.NotOk( | ||
ConfError.message(s"File ${file.getAbsolutePath} is a directory") | ||
) | ||
else gimmeSafeConf(() => ConfigFactory.parseFile(file)) | ||
} | ||
def gimmeConf(config: Config): Configured[Conf] = | ||
gimmeSafeConf(() => config) | ||
|
||
private def gimmeSafeConf(config: () => Config): Configured[Conf] = { | ||
val cache = mutable.Map.empty[Input, Array[Int]] | ||
def loop(value: ConfigValue): Conf = { | ||
val conf = value match { | ||
case obj: ConfigObject => | ||
Conf.Obj(obj.asScala.mapValues(loop).toList) | ||
case lst: ConfigList => | ||
Conf.Lst(lst.listIterator().asScala.map(loop).toList) | ||
case _ => | ||
value.unwrapped match { | ||
case x: String => Conf.Str(x) | ||
case x: java.lang.Integer => Conf.Num(BigDecimal(x)) | ||
case x: java.lang.Long => Conf.Num(BigDecimal(x)) | ||
case x: java.lang.Double => Conf.Num(BigDecimal(x)) | ||
case x: java.lang.Boolean => Conf.Bool(x) | ||
case null => Conf.Null() | ||
case x => | ||
throw new IllegalArgumentException( | ||
s"Unexpected config value $value with unwrapped value $x" | ||
) | ||
} | ||
} | ||
getPositionOpt(value.origin, cache).fold(conf)(conf.withPos) | ||
} | ||
try { | ||
Configured.Ok(loop(config().resolve().root)) | ||
} catch { | ||
case e: ConfigException.Parse => | ||
Configured.NotOk( | ||
ConfError.parseError(getPosition(e.origin, cache), e.getMessage) | ||
) | ||
} | ||
} | ||
|
||
private def getPosition( | ||
originOrNull: ConfigOrigin, | ||
cache: mutable.Map[Input, Array[Int]] | ||
): Position = | ||
getPositionOpt(originOrNull, cache).getOrElse(Position.None) | ||
|
||
private def getPositionOpt( | ||
originOrNull: ConfigOrigin, | ||
cache: mutable.Map[Input, Array[Int]] | ||
): Option[Position] = | ||
for { | ||
origin <- Option(originOrNull) | ||
url <- Option(origin.url) | ||
linePlus1 <- Option(origin.lineNumber) | ||
line = linePlus1 - 1 | ||
input = Input.File(new java.io.File(url.toURI)) | ||
offsetByLine = cache.getOrElseUpdate( | ||
input, | ||
ConfGet.getOffsetByLine(input.chars) | ||
) | ||
if line < offsetByLine.length | ||
start = offsetByLine(line) | ||
} yield Position.Range(input, start, start) | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
metaconfig-sconfig/src/main/scala/metaconfig/sconfig/package.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,12 @@ | ||
package metaconfig | ||
|
||
package object sconfig { | ||
implicit val sConfigMetaconfigParser = new MetaconfigParser { | ||
override def fromInput(input: Input): Configured[Conf] = input match { | ||
case Input.File(path, _) => | ||
SConfig2Class.gimmeConfFromFile(path.toFile) | ||
case els => | ||
SConfig2Class.gimmeConfFromString(new String(els.chars)) | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
metaconfig-sconfig/src/test/scala/metaconfig/sconfig/HoconPrinterProps.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,56 @@ | ||
package metaconfig.sconfig | ||
|
||
import metaconfig.Conf | ||
import metaconfig.ConfOps | ||
import metaconfig.ConfShow | ||
import org.scalacheck.Properties | ||
import org.scalameta.logger | ||
import org.scalatest.FunSuite | ||
import scala.meta.testkit.DiffAssertions | ||
import metaconfig.Generators.argConfShow | ||
import org.scalacheck.Prop.forAll | ||
|
||
object HoconPrinterProps { | ||
def checkRoundtrip(conf: String): Boolean = { | ||
val a = Conf.parseString(conf).get | ||
val hocon = Conf.printHocon(a) | ||
val b = Conf.parseString(hocon).get | ||
val isEqual = a == b | ||
if (!isEqual) { | ||
pprint.log(a) | ||
pprint.log(b) | ||
logger.elem(conf, hocon, Conf.patch(a, b)) | ||
} | ||
a == b | ||
} | ||
|
||
} | ||
|
||
class HoconPrinterProps extends Properties("HoconPrinter") { | ||
property("roundtrip") = forAll { conf: ConfShow => | ||
HoconPrinterProps.checkRoundtrip(conf.str) | ||
} | ||
} | ||
|
||
class HoconPrinterRoundtripSuite extends FunSuite with DiffAssertions { | ||
def ignore(conf: String): Unit = super.ignore(conf) {} | ||
def checkRoundtrip(conf: String): Unit = | ||
test(conf.take(100)) { | ||
assert(HoconPrinterProps.checkRoundtrip(conf)) | ||
} | ||
|
||
ignore( | ||
""" | ||
|a.a = "d" | ||
|a.bc = 9 | ||
""".stripMargin | ||
) | ||
|
||
checkRoundtrip( | ||
""" | ||
|aa.bb = true | ||
|aa.d = 3 | ||
|aa.aa = "cb" | ||
""".stripMargin | ||
) | ||
} |
57 changes: 57 additions & 0 deletions
57
metaconfig-sconfig/src/test/scala/metaconfig/sconfig/PatchProps.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,57 @@ | ||
package metaconfig.sconfig | ||
|
||
import metaconfig.Conf | ||
import metaconfig.ConfOps | ||
import metaconfig.ConfShow | ||
import org.scalacheck.Prop.forAll | ||
import org.scalacheck.Properties | ||
import org.scalameta.logger | ||
import org.scalatest.FunSuite | ||
import scala.meta.testkit.DiffAssertions | ||
import metaconfig.Generators.argConfShow | ||
|
||
object PatchProps { | ||
// asserts that applying | ||
def checkPatch(a: String, b: String): Boolean = { | ||
val original = Conf.parseString(a).get | ||
val revised = Conf.parseString(b).get | ||
val patch = Conf.patch(original, revised) | ||
val expected = Conf.applyPatch(original, revised) | ||
val obtained = Conf.applyPatch(original, patch) | ||
if (obtained != expected) { | ||
logger.elem( | ||
obtained, | ||
expected, | ||
patch.toString, | ||
Conf.patch(obtained, expected) | ||
) | ||
} | ||
obtained == expected | ||
} | ||
} | ||
|
||
class PatchProps extends Properties("Patch") { | ||
|
||
property("roundtrip") = forAll { (a: ConfShow, b: ConfShow) => | ||
PatchProps.checkPatch(a.str, b.str) | ||
} | ||
|
||
} | ||
class PatchPropsSuite extends FunSuite with DiffAssertions { | ||
def check(a: String, b: String): Unit = { | ||
test(a) { assert(PatchProps.checkPatch(a, b)) } | ||
} | ||
|
||
check( | ||
""" | ||
|ad.da = true | ||
|cc.bd = "dd" | ||
""".stripMargin, | ||
""" | ||
| | ||
|ad.a.dc = false | ||
|ad = "ad" | ||
""".stripMargin | ||
) | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
metaconfig-sconfig/src/test/scala/metaconfig/sconfig/SConfig2ClassTest.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,54 @@ | ||
package metaconfig.sconfig | ||
|
||
import java.io.File | ||
import java.nio.file.Files | ||
import java.nio.file.Paths | ||
import metaconfig.Conf | ||
import org.scalatest.FunSuite | ||
|
||
class SConfig2ClassTest extends FunSuite { | ||
test("basic") { | ||
val file = File.createTempFile("prefix", ".conf") | ||
Files.write( | ||
Paths.get(file.toURI), | ||
"""|a.b = 2 | ||
|a = [ | ||
| 1, | ||
| "2" | ||
|] | ||
|a += true""".stripMargin.getBytes() | ||
) | ||
val obtained = SConfig2Class.gimmeConfFromFile(file).get | ||
val expected = Conf.Obj( | ||
"a" -> Conf.Lst( | ||
Conf.Num(1), | ||
Conf.Str("2"), | ||
Conf.Bool(true) | ||
) | ||
) | ||
assert(obtained == expected) | ||
} | ||
|
||
test("file not found") { | ||
val f = File.createTempFile("doesnotexist", "conf") | ||
f.delete() | ||
assert(SConfig2Class.gimmeConfFromFile(f).isNotOk) | ||
} | ||
|
||
test("null") { | ||
val obtained = | ||
SConfig2Class | ||
.gimmeConfFromString( | ||
"""|keywords = [ | ||
| null | ||
|]""".stripMargin | ||
) | ||
.get | ||
val expected = Conf.Obj( | ||
"keywords" -> Conf.Lst( | ||
Conf.Null() | ||
) | ||
) | ||
assert(obtained == expected) | ||
} | ||
} |
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