Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encoding: missing support for toml #68

Closed
cueckoo opened this issue Jul 3, 2021 · 5 comments
Closed

encoding: missing support for toml #68

cueckoo opened this issue Jul 3, 2021 · 5 comments
Assignees
Labels
FeatureRequest New feature or request

Comments

@cueckoo
Copy link
Collaborator

cueckoo commented Jul 3, 2021

Originally opened by @xinau in cuelang/cue#68

(Go) Tools like InfluxDB, GitLab CI (Runner), Traefik use TOML as their configuration language. Atm. CUE has no encoding support for TOML configuration files to be imported or exported. Since Go doesn't have native support for TOML, a third-party implementation needs to be imported as it's the case for YAML. Another thing to be aware of is that the native support for Date-Time, Date, Time values in TOML. Maybe this could be something CUE might want to support in the future as well. Also a mapping of array of tables to a appropriate CUE representation might be needed.

@cueckoo cueckoo added the FeatureRequest New feature or request label Jul 3, 2021
@cueckoo
Copy link
Collaborator Author

cueckoo commented Jul 3, 2021

Original reply by @mpvl in cuelang/cue#68 (comment)

I'm fine with TOML, being as common as it is, to be one of the few more configuration formats that's natively supported. At the moment, YAML, JSON, Go, Proto, and OpenAPI are higher on the list to complete support for, though.

Note that generating TOML output should already not be too hard using the text/template builtin package.

CUE has some limited support for time and date parsing through the time builtin package. It is high on the list to complete this support as part of the OpenAPI support effort. With this, a time field is indicated as:

import "time"

meetingTime: time.Time

where meetingTime is a string with the respective time format.

I don't understand why arrays of tables are not just lists of values. It seems to be a special TOML notation that needs no special treatment in CUE. I do plan to add support for associative lists at some point, which would allow one to define different items of a list at different locations, though, sticking to CUE's associativity and commutativity properties, without any guarantee of order other than adhering to some topological sort.

@jmgilman
Copy link

jmgilman commented Jun 6, 2022

Is this feature request on the radar for getting some attention soon? Using text/template works in simple cases but can get out of hand pretty quickly.

@myitcv
Copy link
Member

myitcv commented Jun 7, 2022

This is not currently a priority because there are aspects like package management that are higher priority. But we would very much welcome community contributions in this space.

@yujunz
Copy link

yujunz commented Sep 30, 2022

MVP in cue.

import (
    "strings"
    "encoding/json"
)

cue: {
	tables: {
		global_tags: {
			cluster:      "foo"
			environment?: string
		}
		agent: {
			interval: "interval"
		}
	}

	table_arrays: {
		"inputs.prometheus": [
			{
				metric_version: 2
			}, {
				metric_version: 2
			},
		]
	}
}

toml: strings.Join([ for table, kv in cue.tables {
	strings.Join(["[\(table)]"]+[ for k, v in kv {"\(k) = \(json.Marshal(v))"}], "\n")
}]+[ for table, array in cue.table_arrays {
	strings.Join([ for kv in array {
		strings.Join(["[[\(table)]]"]+[ for k, v in kv {"\(k) = \(json.Marshal(v))"}], "\n")
	}], "\n\n")
}], "\n\n")

renders

[global_tags]
cluster = "foo"

[agent]
interval = "interval"

[[inputs.prometheus]]
metric_version = 2

[[inputs.prometheus]]
metric_version = 2

https://cuelang.org/play/?id=YEIedqgEzNT#cue@export@cue

Nesting tables are hard to implement and likely requires extension in cue.

@myitcv myitcv added the zGarden label Jun 13, 2023
@mvdan mvdan mentioned this issue Nov 10, 2023
@mvdan mvdan removed the zGarden label Feb 8, 2024
@mvdan mvdan self-assigned this Apr 24, 2024
cueckoo pushed a commit that referenced this issue May 9, 2024
Multiple pieces are left to be done, such as tables with headers,
comments, node positions, and error positions.
However, this is already a significant enough amount of code and tests
which can be reviewed and merged on its own.

The Go API will remain experimental for the foreseeable future.
We will hook up this encoding to cmd/cue and the standard library
once it is more complete, particularly once we have an encoder.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I4daca2d0e34e60ef891233872ae978a5df0ebedd
cueckoo pushed a commit that referenced this issue May 9, 2024
Multiple pieces are left to be done, such as tables with headers,
comments, node positions, and error positions.
However, this is already a significant enough amount of code and tests
which can be reviewed and merged on its own.

The Go API will remain experimental for the foreseeable future.
We will hook up this encoding to cmd/cue and the standard library
once it is more complete, particularly once we have an encoder.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I4daca2d0e34e60ef891233872ae978a5df0ebedd
cueckoo pushed a commit that referenced this issue May 9, 2024
Multiple pieces are left to be done, such as tables with headers,
comments, node positions, and error positions.
However, this is already a significant enough amount of code and tests
which can be reviewed and merged on its own.

The Go API will remain experimental for the foreseeable future.
We will hook up this encoding to cmd/cue and the standard library
once it is more complete, particularly once we have an encoder.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I4daca2d0e34e60ef891233872ae978a5df0ebedd
cueckoo pushed a commit that referenced this issue May 9, 2024
Multiple pieces are left to be done, such as tables with headers,
comments, node positions, and error positions.
However, this is already a significant enough amount of code and tests
which can be reviewed and merged on its own.

The Go API will remain experimental for the foreseeable future.
We will hook up this encoding to cmd/cue and the standard library
once it is more complete, particularly once we have an encoder.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I4daca2d0e34e60ef891233872ae978a5df0ebedd
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1194508
Unity-Result: CUE porcuepine <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
Reviewed-by: Roger Peppe <[email protected]>
cueckoo pushed a commit that referenced this issue May 14, 2024
Given some input TOML, we can use our new encoding/toml.Decoder
to obtain an ast.Node and then compile that to a cue.Value.
Similarly, we can use go-toml's Unmarshal API on the same input TOML
to obtain a Go value directly.

Those two values should be equivalent, which we check via qt.JSONEquals.

Two test cases fail this new check: the one involving duplicate keys,
which is a known bug in our decoder, and the pair of tests involving
empty TOML input, which we decoded as a CUE null.

It seems like all TOML decoders decode empty input as an empty struct,
and the TOML spec also hints that way as it allows empty tables
which "simply have no key/value pairs within them".
Moreover, since we create an ast.File, it seems best to leave it empty,
which equals an empty struct, rather than add a "null" embedding.

While here, add a TODO to remind myself to revisit literal decoding.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I83e34b939f1c2dd3b7928e14076f69c39e5054e0
cueckoo pushed a commit that referenced this issue May 14, 2024
And teach the tests about inputs which we expect to fail.
`want` is now `wantCUE` for clarity; `wantErr` uses `qt.ErrorMatches`.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I2796a76faed01ed62c78145e257c8022efeb5ec3
cueckoo pushed a commit that referenced this issue May 15, 2024
Given some input TOML, we can use our new encoding/toml.Decoder
to obtain an ast.Node and then compile that to a cue.Value.
Similarly, we can use go-toml's Unmarshal API on the same input TOML
to obtain a Go value directly.

Those two values should be equivalent, which we check via qt.JSONEquals.

Two test cases fail this new check: the one involving duplicate keys,
which is a known bug in our decoder, and the pair of tests involving
empty TOML input, which we decoded as a CUE null.

It seems like all TOML decoders decode empty input as an empty struct,
and the TOML spec also hints that way as it allows empty tables
which "simply have no key/value pairs within them".
Moreover, since we create an ast.File, it seems best to leave it empty,
which equals an empty struct, rather than add a "null" embedding.

While here, add a TODO to remind myself to revisit literal decoding.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I83e34b939f1c2dd3b7928e14076f69c39e5054e0
cueckoo pushed a commit that referenced this issue May 15, 2024
And teach the tests about inputs which we expect to fail.
`want` is now `wantCUE` for clarity; `wantErr` uses `qt.ErrorMatches`.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I2796a76faed01ed62c78145e257c8022efeb5ec3
cueckoo pushed a commit that referenced this issue May 15, 2024
Given some input TOML, we can use our new encoding/toml.Decoder
to obtain an ast.Node and then compile that to a cue.Value.
Similarly, we can use go-toml's Unmarshal API on the same input TOML
to obtain a Go value directly.

Those two values should be equivalent, which we check via qt.JSONEquals.

Two test cases fail this new check: the one involving duplicate keys,
which is a known bug in our decoder, and the pair of tests involving
empty TOML input, which we decoded as a CUE null.

It seems like all TOML decoders decode empty input as an empty struct,
and the TOML spec also hints that way as it allows empty tables
which "simply have no key/value pairs within them".
Moreover, since we create an ast.File, it seems best to leave it empty,
which equals an empty struct, rather than add a "null" embedding.

While here, add a TODO to remind myself to revisit literal decoding.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I83e34b939f1c2dd3b7928e14076f69c39e5054e0
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1194706
Unity-Result: CUE porcuepine <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
Reviewed-by: Roger Peppe <[email protected]>
cueckoo pushed a commit that referenced this issue May 15, 2024
And teach the tests about inputs which we expect to fail.
`want` is now `wantCUE` for clarity; `wantErr` uses `qt.ErrorMatches`.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I2796a76faed01ed62c78145e257c8022efeb5ec3
cueckoo pushed a commit that referenced this issue May 15, 2024
And teach the tests about inputs which we expect to fail.
`want` is now `wantCUE` for clarity; `wantErr` uses `qt.ErrorMatches`.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I2796a76faed01ed62c78145e257c8022efeb5ec3
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1194707
Reviewed-by: Roger Peppe <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue May 22, 2024
This is a significant amount of code given that there are effectively
three ways in which table arrays can be used.

First, declaring a new array and repeating its key like:

    [[foo]]
    leaf = "one"
    [[foo]]
    leaf = "two"

resulting in appending to the same array:

    foo: [
        {leaf: "one"},
        {leaf: "two"},
    ]

Second, a table array can be created under an existing table:

    [foo]
    leaf = "one"
    [[foo.bar]]
    leaf = "two"

resulting in:

    foo: {
        leaf: "one"
        bar: [
            {leaf: "two"},
        ]
    }

Third, and perhaps most confusing, a table or table array can be created
under an existing table array, adding to the table's last element:

    [[foo]]
    leaf = "one"
    [foo.bar]
    leaf = "two"
    [[foo.baz]]
    leaf = "three"

resulting in:

    foo: [
        {
            leaf: "one"
            bar: {
                leaf: "two"
            }
            baz: [
                {leaf: "three"}
            ]
        },
    }

We add test cases for all of these edge cases,
as well as test cases to ensure that unique keys are handled correctly,
and that we reject declaring a key as both a table and an array table.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I2b483a67810b7c42ddf319a04a0f22724877b8a9
cueckoo pushed a commit that referenced this issue Jun 10, 2024
This is a significant amount of code given that there are effectively
three ways in which table arrays can be used.

First, declaring a new array and repeating its key like:

    [[foo]]
    leaf = "one"
    [[foo]]
    leaf = "two"

resulting in appending to the same array:

    foo: [
        {leaf: "one"},
        {leaf: "two"},
    ]

Second, a table array can be created under an existing table:

    [foo]
    leaf = "one"
    [[foo.bar]]
    leaf = "two"

resulting in:

    foo: {
        leaf: "one"
        bar: [
            {leaf: "two"},
        ]
    }

Third, and perhaps most confusing, a table or table array can be created
under an existing table array, adding to the table's last element:

    [[foo]]
    leaf = "one"
    [foo.bar]
    leaf = "two"
    [[foo.baz]]
    leaf = "three"

resulting in:

    foo: [
        {
            leaf: "one"
            bar: {
                leaf: "two"
            }
            baz: [
                {leaf: "three"}
            ]
        },
    }

We add test cases for all of these edge cases,
as well as test cases to ensure that unique keys are handled correctly,
and that we reject declaring a key as both a table and an array table.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I2b483a67810b7c42ddf319a04a0f22724877b8a9
cueckoo pushed a commit that referenced this issue Jun 12, 2024
This is a significant amount of code given that there are effectively
three ways in which table arrays can be used.

First, declaring a new array and repeating its key like:

    [[foo]]
    leaf = "one"
    [[foo]]
    leaf = "two"

resulting in appending to the same array:

    foo: [
        {leaf: "one"},
        {leaf: "two"},
    ]

Second, a table array can be created under an existing table:

    [foo]
    leaf = "one"
    [[foo.bar]]
    leaf = "two"

resulting in:

    foo: {
        leaf: "one"
        bar: [
            {leaf: "two"},
        ]
    }

Third, and perhaps most confusing, a table or table array can be created
under an existing table array, adding to the table's last element:

    [[foo]]
    leaf = "one"
    [foo.bar]
    leaf = "two"
    [[foo.baz]]
    leaf = "three"

resulting in:

    foo: [
        {
            leaf: "one"
            bar: {
                leaf: "two"
            }
            baz: [
                {leaf: "three"}
            ]
        },
    }

We add test cases for all of these edge cases,
as well as test cases to ensure that unique keys are handled correctly,
and that we reject declaring a key as both a table and an array table.

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I2b483a67810b7c42ddf319a04a0f22724877b8a9
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1195025
Unity-Result: CUE porcuepine <[email protected]>
Reviewed-by: Aram Hăvărneanu <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 30, 2024
For now, all we test is that it can re-encode all the decoder test cases
because it does not yet aim to keep any comments, style, order,
or structure from the original CUE value.
The go-toml library we use is not able to encode its AST nodes to TOML,
so for now we encode as Go "any" values which drop that information.

This will be resolved in the near future via an encoder that produces
TOML syntax directly without going through an "any" phase first.
For the time being, this basic encoder works,
even if it produces unnecessarily verbose output such as

    [foo]
    [foo.bar]
    [foo.bar.'baz.zzz zzz']
    one = '1'

rather than the shorter

    [foo.bar.'baz.zzz zzz']
    one = "1"

Updates #68.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I5012bba77fb77b8bf1dd1d73615d34c3438cd147
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1198539
Reviewed-by: Roger Peppe <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
@mvdan
Copy link
Member

mvdan commented Jul 30, 2024

A TOML decoder and encoder are now in master and wired up to cmd/cue, so they should work for most basic use cases. Please let us know if you find any bugs either in master or in the upcoming alpha, or if anything doesn't behave like you would expect it to :)

Given that initial support is now merged, we have closed this issue, but we have opened a number of others for follow-up tasks:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FeatureRequest New feature or request
Projects
Archived in project
Development

No branches or pull requests

5 participants