mikee47 / ConfigDB

Configuration database for Sming
GNU General Public License v3.0
3 stars 1 forks source link

Extensions to schema for custom data types #13

Closed mikee47 closed 2 months ago

mikee47 commented 3 months ago

Several items in the pre-release issues #1 appear related, so I'm opening an issue to discuss in a bit more detail and scope the problem before doing any coding.

Support mapping enumerated values to strings

Certainly enumerated values should be supported since JsonSchema offers the enum keyword for this purpose.

"bit-test": {
    "type": "string",
    "enum": ["red", "green", "blue"]
}

This is enough for dbgen to generate the following:

enum class BitTest: uint8_t {
    red,
    green,
    blue,
};

Similarly BitSet can be implemented like this: Not so sure about this... logically this would be an array of enums.

"set-test": {
    "type": "array",
    "items":{
        "type": "string",
        "enum": ["red", "green", "blue"]
    }
}

Generating:

enum class SetTest: uint8_t {
    red,
    green,
    blue,
};
using SetTestBits = BitSet<uint8_t, SetTest, 3>;

Note that accessing these new types could get a bit windy, depending on how deep in the structure they're defined. I guess applications can always add aliases as required...

Also this needn't be restricted to string types. It could define a mapping of floating-point co-efficients, or some other kind of constant data.

Enable custom value types

For example, can map string object with ip4 format to IpAddress.

JsonSchema defines a number of formats, but continuing from enums and sets we could use the ctype annotation instead to indicate a wrapper class for the property. For example, for the ContainedMqtt class in the basic-config sample application, we can add "ctype": "Url" to the server property definition. This would produce:

Url getServer() const
{
    return getString(Struct::Ptr(getData())->server);
}

void setServer(const Url& value)
{
    Struct::Ptr(getData())->server = getStringId(value);
}

The only caveat would be that whatever class is given has appropriate conversion operators for the given storage type. The advantage of this approach is that it's a very easy change :-)

Generate struct for each Object

... which applications can use to persist contents in code. Provide methods to read/write these structures.

I added this since I though it might be useful. However, since the configuration database persists settings for us there should be no need to store most values elsewhere, especially in structured form. I'm inclined to leave this unless a specific need arises.

What we can do though is pass our ConfigDB object instances to other functions:

bool initialiseSecurity(BasicConfig::Root::SecurityConst& settings)
{
    ...
}

void init()
{
    ...
    BasicConfig db("test");
    BasicConfig::Root::SecurityConst sec(database);
    initialiseSecurity(sec);
}

There are a few possible shortcomings with this:

(a) If initialiseSecurity is in another translation unit or library then it must #include <basic-config.h> (b) All settings, even unrelated ones, are defined in one header file (c) need to know where in the configuration structure the necessary definition is

Perhaps the simplest way to deal with this is to add header files as needed, such as SecurityConfig.h:

#include <basic-config.h>

using SecurityConfig = BasicConfig::Root::SecurityConst;

The definition SecurityConfig is then suitably decoupled from the structure of the configuration database.

So if the path changes we only need to update one definition.

mikee47 commented 3 months ago

@pljakobs Are you interested in custom data types or should we leave that for now?

pljakobs commented 2 months ago

sorry, only noticed this today.

I found that a schema inferrer created something linke

"supported_color_modes": {
    "type": "array",
    "items":{
        "type": "string",
        "enum": ["RGB", "RGBWW", "RGBCW","RGBWWCW"]
    }
}

from my array

"supported_color_modes":["RGB", "RGBWW", "RGBCW","RGBWWCW"]

so yes, that would be an array of enums and it does make sense, although since this is a hard-coded value for a given controller (basically it's capabilities depending on hardware and software), I don't see a need to really have a tight schema definition for this.