creachadair / tomledit

Edit the structure of TOML documents
MIT License
6 stars 0 forks source link

Support for TOML arrays #1

Open GFSimone opened 1 month ago

GFSimone commented 1 month ago

As suggested by Cosmos Devs, I'm bringing here a feature request opened for Cosmos Confix. https://github.com/cosmos/cosmos-sdk/issues/21034

It would be nice if tomledit could parse arrays blocks.

Thanks in advance for any attention to this.

creachadair commented 1 month ago

Can you say more specifically what feature you're requesting? The parser handles array-valued blocks already, and it's not clear to me whether this is about the CLI tool or the library, and what kind of API you'd like to see.

It would be helpful if you can give a couple specific examples of what you want to do (ideally with a code snippet, even if that snippet doesn't work with the current API).

julienrbrt commented 1 month ago

Currently, doc.Find needs a key to be present to find a value.

Say you have the following toml:

[[fruit]]
  name = "apple"

  [[fruit.variety]]
    name = "red delicious"

  [[fruit.variety]]
    name = "granny smith"

tomledit.Document{}.Find("fruit","variety") will return a slice of Items. It would be great if it could range over the array if we specify an index as key: eg.g tomledit.Document{}.Find("fruit","variety","1"). Then it allows us to access the value of that item with tomledit.Document{}.Find("fruit","variety","1","name").

Right now, you don't have an easy way (afaict) to access the toml array values à la jq by simply passing up keys.

creachadair commented 1 month ago

There are a few tricky things to sort out to make a JSONpath-style API work for TOML. Looking just at this example, the TOML is equivalent to the JSON:

{
  "fruit": [
    {
      "name": "apple",
      "variety": [
        {
          "name": "red delicious"
        },
        {
          "name": "granny smith"
        }
      ]
    }
  ]
}

So a JSONpath (or similar) expression like fruit.variety[1] doesn't match anything in the document. (Maybe you meant [fruit] instead of [[fruit]]).

Anyway, taking it as written, you'd want fruit[0].variety[1].name or similar. To do this uniquely, you'll either need to preprocess the syntactic structure of the TOML to combine array elements, or track the current stack on the way through the file.

For example, this TOML:

[a]
  [[a.b]]
    id = 1
  [[a.b]]
    id = 2

[c]

[[a.b]]
  id = 3

is valid, and represents the JSON

{
  "a": {
    "b": [
      {"id": 1},
      {"id": 2},
      {"id": 3}
    ]
  },
  "c": {}
}

To resolve a.b[1].id requires keeping at least a count of the number of elements of (each) array in the traversal. This applies recursively, since arrays and array indices can be nested (cf. fruit[0].variety[1].name above). Plus, in a path API, both the inputs and the outputs are polymorphic—since a.b yields an array of items, vs. a.b[1].id which yields a single value.

All that is of course quite achievable, but it's not a straightforward extension of the existing API—this would need a new API and probably some new types. It's not something I have a lot of time to invest in here—it might even want to be a separate package maybe.