Tscfg
Schema-first, boilerplate-free, type-safe access to configuration properties in Java and Scala
Install / Use
/learn @carueda/TscfgREADME
tscfg
tscfg is a command line tool that takes a configuration schema parseable by Typesafe Config and generates all the boilerplate to make the definitions available in type-safe, immutable objects (POJOs/records for Java, case classes for Scala). Field comments are captured as well, reflected as javadoc or scala doc comments.
The generated code only depends on the Typesafe Config library.
- status
- configuration schema
- running tscfg
- configuration access
- supported types
- configuration template
- FAQ
- tests
status
The tool supports all types handled by Typesafe Config (string, int, long, double, boolean, duration, size-in-bytes, list, object) and has great test coverage. Possible improvements include a more standard command line interface, a proper tscfg library, and perhaps a revision of the syntax for types. Feel free to fork, enter issues/reactions, submit PRs, etc.
configuration schema
In tscfg's approach, the configuration schema itself can be captured in any source parseable by Typesafe Config, so the familiar syntax/format and loading mechanisms are used.
For example, from this configuration:
service {
url = "https://example.net/rest"
poolSize = 32
debug = true
factor = 0.75
}
tscfg will generate the following (javadoc, constructors and other methods omitted):
-
Java:
-
With records (
--java:records):public record Cfg(Service service) { public record Service( boolean debug, double factor, int poolSize, String url ) {} }Nesting of configuration properties is captured via inner records.
-
POJOs:
public class Cfg { public final Service service; public static class Service { public final boolean debug; public final double factor; public final int poolSize; public final String url; } }Nesting of configuration properties is captured via inner static classes.
-
-
Scala:
case class Cfg( service : Cfg.Service ) object Cfg { case class Service( debug : Boolean, factor : Double, poolSize : Int, url : String ) }Nesting of configuration properties is captured via nested companion objects.
The tool determines the type of each field according to the given value in the input configuration. Used in this way, all fields are considered optional, with the given value as the default.
But this wouldn't be flexible enough! To allow the specification of required fields, explicit types, and default values, a string with a simple syntax as follows can be used (illustrated with the integer type):
| field schema | meaning | java type / default | scala type / default
|---|---|---|---|
| a: "int" | required integer | int / no default | Int / no default
| a: "int | 3" | optional integer with default value 3 | int / 3 | Int/ 3
| a: "int?" | optional integer | Integer / null(*) | Option[Int] / None
[!note]
- (*) You can use the
--java:optionalsflag to generateOptional<T>instead ofnull.- The type syntax is still subject to change.
The following is a complete example exercising this mechanism.
endpoint {
path: "string"
url: "String | http://example.net"
serial: "int?"
interface {
port: "int | 8080"
}
}
For Java, this basically becomes the immutable class:
public class JavaExampleCfg {
public final Endpoint endpoint;
public static class Endpoint {
public final int intReq;
public final Interface_ interface_;
public final String path;
public final Integer serial;
public final String url;
public static class Interface_ {
public final int port;
public final String type;
}
}
}
And for Scala:
case class ScalaExampleCfg(
endpoint : ScalaExampleCfg.Endpoint
)
object ScalaExampleCfg {
case class Endpoint(
intReq : Int,
interface : Endpoint.Interface,
path : String,
serial : Option[Int],
url : String
)
object Endpoint {
case class Interface(
port : Int,
`type` : Option[String]
)
}
}
running tscfg
You will need a JRE 8+ and the latest fat JAR (tscfg-x.y.z.jar) from the releases.
Or run
sbt assembly(orsbt ++2.13.7 assembly) under a clone of this repo to generate the fat jar.
$ java -jar tscfg-x.y.z.jar
tscfg x.y.z
Usage: tscfg.Main --spec inputFile [options]
Options (default):
--pn <packageName> (tscfg.example)
--cn <className> (ExampleCfg)
--dd <destDir> (/tmp if existing or OS dependent temp dir)
--java generate java code (the default)
--java:getters generate getters (see #31) (false)
--java:records generate records (false)
--java:optionals use optionals (false)
--scala generate scala code (java)
--scala:bt use backticks (see #30) (false)
--durations use java.time.Duration (false)
--no-doc do not capture doc comments (see #312)
--all-required assume all properties are required (see #47)
--tpl <filename> generate config template (no default)
--tpl.ind <string> template indentation string (" ")
--tpl.cp <string> prefix for template comments ("##")
--withoutTimestamp generate header w/out timestamp (false)
Output is written to $destDir/$className.ext
So, to generate the Java class tscfg.example.ExampleCfg with the example above
saved in a file example.spec.conf, you can run:
$ java -jar tscfg-x.y.z.jar --spec example.spec.conf
parsing: example.spec.conf
generating: /tmp/ExampleCfg.java
maven plugin
Please see tscfg-maven-plugin.
gradle plugin
Please see tscfg-plugin-gradle.
configuration access
Access to a configuration instance is via usual Typesafe Config mechanism as appropriate for your application, for example, to load the default configuration:
Config tsConfig = ConfigFactory.load().resolve()
or from a given file:
Config tsConfig = ConfigFactory.parseFile(new File("my.conf")).resolve();
Now, to access the configuration fields, instead of, for example:
Config endpoint = tsConfig.getConfig("endpoint");
String path = endpoint.getString("path");
Integer serial = endpoint.hasPathOrNull("serial") ? endpoint.getInt("serial") : null;
int port = endpoint.hasPathOrNull("port") ? endpoint.getInt("interface.port") : 8080;
you can:
-
Create the tscfg generated wrapper:
ExampleCfg cfg = new ExampleCfg(tsConfig);which will make all verifications about required settings and associated types. In particular, as is typical with Config use, an exception will be thrown if this verification fails.
-
Then, while enjoying full type safety and the code completion and navigation capabilities of your editor or IDE:
String path = cfg.endpoint.path; Integer serial = cfg.endpoint.serial; int port = cfg.endpoint.interface_.port;
An object reference will never be null (or Optional.empty()) (None in Scala) if the corresponding field is required according to
the schema. It will only be null (or Optional.empty()) (None in Scala) if it is marked optional with no default value and
has been omitted in the input configuration.
With this example schema, the generated Java code looks like this and an example of use [like this](https://github.com/carueda/tscfg/blob/main/src/main/java/tsc
Related Skills
node-connect
349.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.0kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
