SkillAgentSearch skills...

Tscfg

Schema-first, boilerplate-free, type-safe access to configuration properties in Java and Scala

Install / Use

/learn @carueda/Tscfg
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Build Status Coverage Status Known Vulnerabilities PRs Welcome Scala Steward badge

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

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:optionals flag to generate Optional<T> instead of null.
  • 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 (or sbt ++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:

  1. 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.

  2. 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

View on GitHub
GitHub Stars110
CategoryDevelopment
Updated3d ago
Forks18

Languages

Scala

Security Score

100/100

Audited on Apr 2, 2026

No findings