smallrye-config icon indicating copy to clipboard operation
smallrye-config copied to clipboard

Config instance builder

Open dmlloyd opened this issue 2 years ago • 4 comments

Update: there is now a basic (but incomplete) implementation.

This is a first draft of the builder API for constructing configuration instances. No implementation is included yet; this is just for API review.

Fixes #1001.

dmlloyd avatar Oct 03 '23 19:10 dmlloyd

@dmlloyd

I've now started to look into this and pushed some initial implementation that includes:

  • Builder class generation
  • Simple leaf types and primitives
  • Simple optionals
  • @WithDefault

Of course, there is still a lot missing, and even the pieces I did are only for the most basic cases, so I need to start looking into all the edge cases, but it is a start.

In terms of the API, I think we could start with something smaller and drop withString and withDefaultFor variants. We could add them later if required.

On another note, with(F getter, int value) and with(F getter, long value) (as well as their Optional counterparts), are ambiguous when we try to call them:

interface Server {
    String host();
    int port();
}

Server server = forInterface(Server.class)
        .with(Server::host, "localhost")
        .with(Server::port, 8080)
        .build();

So, .with(Server::port, 8080) is reported as ambiguous by the compiler. Casting the value type doesn't seem to help, and it seems to be related to the Function type and method reference inferred type. Would you happen to have any idea?

radcortez avatar Sep 03 '25 20:09 radcortez

@dmlloyd

I've now started to look into this and pushed some initial implementation that includes:

  • Builder class generation
  • Simple leaf types and primitives
  • Simple optionals
  • @WithDefault

Of course, there is still a lot missing, and even the pieces I did are only for the most basic cases, so I need to start looking into all the edge cases, but it is a start.

In terms of the API, I think we could start with something smaller and drop withString and withDefaultFor variants. We could add them later if required.

On another note, with(F getter, int value) and with(F getter, long value) (as well as their Optional counterparts), are ambiguous when we try to call them:

interface Server {
    String host();
    int port();
}

Server server = forInterface(Server.class)
        .with(Server::host, "localhost")
        .with(Server::port, 8080)
        .build();

So, .with(Server::port, 8080) is reported as ambiguous by the compiler. Casting the value type doesn't seem to help, and it seems to be related to the Function type and method reference inferred type. Would you happen to have any idea?

This is caused by using a type variable in the first argument, which I believe prevents inferencing due to some complexity aspect. I recommend creating a custom interface type for each type of property which also implements Serializable, like I did in Gizmo 2. That should remove the ambiguity from the type inference.

dmlloyd avatar Sep 05 '25 16:09 dmlloyd

@dmlloyd I think this is ready for a first version:

  • Supports all types and combinations as @ConfigMapping
  • Support @WithDefault to automatically set values for all types
  • Support @WithConverter in combination with @WithDefault (values set via the builder are not subject to conversions)
  • Throws an exception if all required values are not set when calling build

I've removed the withString and withDefaultFor method variants to simplify this initial version. We can add them later.

Let me know what you think.

radcortez avatar Sep 12 '25 14:09 radcortez

Seems OK. Don't forget to JavaDoc the rest of the API.

dmlloyd avatar Sep 18 '25 19:09 dmlloyd