Skip to content

Commit

Permalink
0.8.0: resolve #23 "support size-in-bytes type"
Browse files Browse the repository at this point in the history
  • Loading branch information
carueda committed Feb 3, 2017
1 parent 5534f78 commit 6a3d89b
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 14 deletions.
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
2017-01-31 - 0.8.0
2017-02-03 - 0.8.0
- resolve #23 "support size-in-bytes type"
- renaming to better reflect "duration" elements, and in preparation for "size in bytes" support.

2016-12-17 - 0.7.2
Expand Down
23 changes: 14 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,12 @@ The generated code only depends on the Typesafe Config library.

### status

The tool is already pretty usable.
It supports a good part of the common types as supported by Typesafe Config
(string, int, long, double, boolean, duration, list)
The tool supports all types handled by Typesafe Config
(string, int, long, double, boolean, duration, size-in-bytes, list)
and has good test coverage.
However, it can be enhanced in a number of ways, including:
command line interface can be improved;
syntax for types is not stable yet;
and a missing "type" is [size in bytes](https://github.com/typesafehub/config/blob/master/HOCON.md#size-in-bytes-format).
Feel free to fork, enter issues, submit PRs, etc.
Possible improvements include a more standard command line interface and perhaps
a revision of the syntax for types.
Feel free to fork, enter issues/reactions, submit PRs, etc.


## configuration spec
Expand Down Expand Up @@ -269,9 +266,17 @@ The following basic types are supported:
| `long` | `long` / `Long` | `Long` / `Option[Long]`
| `double` | `double` / `Double` | `Double` / `Option[Double]`
| `boolean` | `boolean` / `Boolean` | `Boolean` / `Option[Boolean]`
| `size` | `long` / `Long` | `Long` / `Option[Long]`
| `duration` | `long` / `Long` | `Long` / `Option[Long]`
#### size-in-bytes
The `size` type corresponds to the
[size-in-bytes formats](https://github.com/typesafehub/config/blob/master/HOCON.md#size-in-bytes-format)
supported by the Typesafe library.
See [#23](https://github.com/carueda/tscfg/issues/23) for various examples.
#### durations
A duration type can be further qualified with a suffix consisting of a colon
Expand Down Expand Up @@ -312,7 +317,7 @@ is denoted `[` _t_ `]`. The corresponding types in Java and Scala are:
|---------------|---------------------|--------------------------
| `[` _t_ `]` | `List<T>` / `List<T>` | `List[T]` / `Option[List[T]]`
where T is the corresponding translation of _t_ in the target language, with
where `T` is the corresponding translation of _t_ in the target language, with
`List<T>` corresponding to an unmodifiable list in Java, and
`List[T]` corresponding to an immutable list in Scala.
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -1 +1 @@
tscfg.version = 0.7.2
tscfg.version = 0.8.0
5 changes: 4 additions & 1 deletion src/main/scala/tscfg/ModelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package tscfg

import com.typesafe.config._
import tscfg.generators.tsConfigUtil
import tscfg.model.DURATION
import tscfg.model.{DURATION, SIZE}
import tscfg.model.durations.ms

import scala.collection.JavaConversions._
Expand Down Expand Up @@ -153,6 +153,9 @@ class ModelBuilder {
if (tsConfigUtil.isDurationValue(valueString))
return Some((DURATION(ms), true, Some(valueString)))

if (tsConfigUtil.isSizeValue(valueString))
return Some((SIZE, true, Some(valueString)))

val tokens = valueString.split("""\s*\|\s*""")
val typePart = tokens(0).toLowerCase
val hasDefault = tokens.size == 2
Expand Down
22 changes: 20 additions & 2 deletions src/main/scala/tscfg/generators/java/JavaGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ class JavaGen(genOpts: GenOpts) extends Generator(genOpts) {
case LONG "long"
case DOUBLE "double"
case BOOLEAN "boolean"
case DURATION(q) "long"
case SIZE "long"
case DURATION(q) "long"
}))
}

Expand Down Expand Up @@ -218,7 +219,7 @@ class JavaGen(genOpts: GenOpts) extends Generator(genOpts) {
): String = {

val (_, methodName) = rec(javaType, lt, "")
methodName + s"""(c.getList("$path"))"""
methodName + s"""(c.getList("$path"))"""
}

private def rec(ljt: ListJavaType, lt: ListType, prefix: String
Expand Down Expand Up @@ -257,6 +258,7 @@ class JavaGen(genOpts: GenOpts) extends Generator(genOpts) {
case LONG methodNames.lngA
case DOUBLE methodNames.dblA
case BOOLEAN methodNames.blnA
case SIZE methodNames.sizA
case DURATION(q) methodNames.durA

case _: ObjectType name.replace('.', '_')
Expand Down Expand Up @@ -368,6 +370,7 @@ private[java] case class MethodNames(prefix: String = "$_") {
val dblA = prefix + "dbl"
val blnA = prefix + "bln"
val durA = prefix + "dur"
val sizA = prefix + "siz"
val expE = prefix + "expE"
val listPrefix = prefix + "L"

Expand Down Expand Up @@ -407,6 +410,21 @@ private[java] case class MethodNames(prefix: String = "$_") {
|}
|""".stripMargin.trim,

// since there's no something like cv.getBytes() nor is SimpleConfig.parseBytes visible,
// use ConfigFactory.parseString:
sizA s"""
|private static java.lang.Long $sizA(com.typesafe.config.ConfigValue cv) {
| java.lang.Object u = cv.unwrapped();
| if (cv.valueType() == com.typesafe.config.ConfigValueType.NUMBER ||
| (u instanceof java.lang.Long) || (u instanceof java.lang.Integer))
| return ((java.lang.Number) u).longValue();
| if (cv.valueType() == com.typesafe.config.ConfigValueType.STRING) {
| return com.typesafe.config.ConfigFactory.parseString("s = " + '"' + u + '"').getBytes("s");
| }
| throw $expE(cv, "size");
|}
|""".stripMargin.trim,

dblA s"""
|private static java.lang.Double $dblA(com.typesafe.config.ConfigValue cv) {
| java.lang.Object u = cv.unwrapped();
Expand Down
18 changes: 18 additions & 0 deletions src/main/scala/tscfg/generators/scala/ScalaGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class ScalaGen(genOpts: GenOpts) extends Generator(genOpts) {
case LONG "scala.Long"
case DOUBLE "scala.Double"
case BOOLEAN "scala.Boolean"
case SIZE "scala.Long"
case DURATION(_) "scala.Long"
}))
}
Expand Down Expand Up @@ -228,6 +229,7 @@ private[scala] case class MethodNames(prefix: String = "$_") {
val dblA = prefix + "dbl"
val blnA = prefix + "bln"
val durA = prefix + "dur"
val sizA = prefix + "siz"
val expE = prefix + "expE"
val listPrefix = prefix + "L"

Expand Down Expand Up @@ -265,6 +267,21 @@ private[scala] case class MethodNames(prefix: String = "$_") {
| u.asInstanceOf[java.lang.Number].longValue()
|}""".stripMargin.trim,

// since there's no something like cv.getBytes() nor is SimpleConfig.parseBytes visible,
// use ConfigFactory.parseString:
sizA s"""
|private def $sizA(cv:com.typesafe.config.ConfigValue): scala.Long = {
| val u: Any = cv.unwrapped
| if (cv.valueType() == com.typesafe.config.ConfigValueType.NUMBER ||
| u.isInstanceOf[scala.Long] || u.isInstanceOf[scala.Int])
| u.asInstanceOf[java.lang.Number].longValue()
| else if (cv.valueType() == com.typesafe.config.ConfigValueType.STRING) {
| com.typesafe.config.ConfigFactory.parseString("s = " + '"' + u + '"').getBytes("s")
| }
| else throw $expE(cv, "size")
|}
|""".stripMargin.trim,

dblA s"""
|private def $dblA(cv:com.typesafe.config.ConfigValue): scala.Double = {
| val u: Any = cv.unwrapped
Expand Down Expand Up @@ -399,6 +416,7 @@ private[scala] class Accessors {
case LONG methodNames.lngA
case DOUBLE methodNames.dblA
case BOOLEAN methodNames.blnA
case SIZE methodNames.sizA
case DURATION(_) methodNames.durA

case _: ObjectType name.replace('.', '_')
Expand Down
18 changes: 18 additions & 0 deletions src/main/scala/tscfg/generators/tsConfigUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ object tsConfigUtil {
case LONG s"""getLong("$path")"""
case DOUBLE s"""getDouble("$path")"""
case BOOLEAN s"""getBoolean("$path")"""
case SIZE s"""getBytes("$path")"""
case DURATION(q) durationGetter(path, q)
}

def basicValue(t: Type, value: String): String = t match {
case SIZE sizeValue(value)
case DURATION(q) durationValue(value, q)
case STRING '"' + escapeString(value) + '"'
case _ value
Expand Down Expand Up @@ -81,4 +83,20 @@ object tsConfigUtil {
case `hour` => TimeUnit.HOURS
case `day` => TimeUnit.DAYS
}

def isSizeValue(value: String): Boolean = {
val config: Config = ConfigFactory.parseString(s"""s = "$value"""")
try {
config.getBytes("s")
true
}
catch {
case NonFatal(_) false
}
}

private def sizeValue(value: String): String = {
val config: Config = ConfigFactory.parseString(s"""s = "$value"""")
config.getBytes("s").toString
}
}
2 changes: 2 additions & 0 deletions src/main/scala/tscfg/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ object model {
case object LONG extends BasicType
case object DOUBLE extends BasicType
case object BOOLEAN extends BasicType
case object SIZE extends BasicType
case class DURATION(q: DurationQualification) extends BasicType

val recognizedAtomic: Map[String, BasicType] = Map(
Expand All @@ -48,6 +49,7 @@ object model {
"long" LONG,
"double" DOUBLE,
"boolean" BOOLEAN,
"size" SIZE,
"duration" DURATION(ms)
)

Expand Down
14 changes: 14 additions & 0 deletions src/main/tscfg/example/issue23.spec.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# required size
sizeReq: "size"

# optional size, no default
sizeOpt: "size?"

# optional size with default value 1024 bytes
sizeOptDef: "size | 1K"

# list of sizes
sizes: [ size ]

# list of lists of sizes
sizes2: [ [ size ] ]
32 changes: 32 additions & 0 deletions src/test/scala/tscfg/generators/java/JavaMainSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,36 @@ class JavaMainSpec extends Specification {
c.idleTimeout === 3600*1000
}
}

"issue23" should {
"generate SIZE type" in {
val r = JavaGen.generate("example/issue23.spec.conf")
r.classNames === Set("JavaIssue23Cfg")
r.fields === Map(
"sizeReq" "long",
"sizeOpt" "java.lang.Long",
"sizeOptDef" "long",
"sizes" "java.util.List<java.lang.Long>",
"sizes2" "java.util.List<java.util.List<java.lang.Long>>"
)
}

"example" in {
val c = new JavaIssue23Cfg(ConfigFactory.parseString(
"""
|sizeReq = "2048K"
|sizeOpt = "1024000"
|sizes = [ 1000, "64G", "16kB" ]
|sizes2 = [[ 1000, "64G" ], [ "16kB" ] ]
""".stripMargin
))
c.sizeReq === 2048*1024
c.sizeOpt === 1024000
c.sizeOptDef === 1024
c.sizes.toList === List(1000, 64*1024*1024*1024L, 16*1000)
c.sizes2.toList.map(_.toList) === List(
List(1000, 64*1024*1024*1024L),
List(16*1000))
}
}
}
32 changes: 32 additions & 0 deletions src/test/scala/tscfg/generators/scala/ScalaMainSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -373,4 +373,36 @@ class ScalaMainSpec extends Specification {
c.idleTimeout === 3600*1000
}
}

"issue23" should {
"generate SIZE type" in {
val r = ScalaGen.generate("example/issue23.spec.conf")
r.classNames === Set("ScalaIssue23Cfg")
r.fields === Map(
"sizeReq" "scala.Long",
"sizeOpt" "scala.Option[scala.Long]",
"sizeOptDef" "scala.Long",
"sizes" "scala.List[scala.Long]",
"sizes2" "scala.List[scala.List[scala.Long]]"
)
}

"example" in {
val c = ScalaIssue23Cfg(ConfigFactory.parseString(
"""
|sizeReq = "2048K"
|sizeOpt = "1024000"
|sizes = [ 1000, "64G", "16kB" ]
|sizes2 = [[ 1000, "64G" ], [ "16kB" ] ]
""".stripMargin
))
c.sizeReq === 2048*1024
c.sizeOpt === Some(1024000)
c.sizeOptDef === 1024
c.sizes === List(1000, 64*1024*1024*1024L, 16*1000)
c.sizes2 === List(
List(1000, 64*1024*1024*1024L),
List(16*1000))
}
}
}

0 comments on commit 6a3d89b

Please sign in to comment.