diff --git a/README.md b/README.md index a9e7517..d32b64c 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,8 @@ or normalizing data configurable. * **Optional Type Safety**: Choose between pedantic, strict or humane typing for your programs. Strict allows nearly no type conversions, humane allows for things like `1` (int) turning into `"1"` (string) when needed. -* **Flexible**: The Rudi CLI interpreter (`rudi`) supports reading/writing JSON, YAML and TOML. +* **Flexible**: The Rudi CLI interpreter (`rudi`) supports reading/writing JSON, + [JSON5](https://json5.org/), [YAML](https://yaml.org/) and [TOML](https://toml.io/en/). ## Installation @@ -125,16 +126,22 @@ Usage of rudi: #### File Handling +Rudi can load JSON, JSON5, YAML and TOML files and will determine the file format based on the +file extension (`.json` for JSON, `.json5` for JSON5, `.yml` and `.yaml` for YAML and `.tml` / +`.toml` for TOML). For data provided via stdin, `rudi` by default assumes YAML (or JSON) encoding. +If you want to use TOML/JSON5 instead, you must use the `--stdin-format` flag. + The first loaded file is known as the "document". Its content is available via path expressions like `.foo[0]`. All loaded files are also available via the `$files` variable (i.e. `.` is the same as `$files[0]` for reading, but when writing data, there is a difference between both notations; refer to the docs for `set` for more information). Additionally the filenames are available in the `$filenames` variable. -For data provided via stdin, `rudi` by default assumes YAML (or JSON) encoding. If you want to use -TOML instead, you must pass `--stdin-format=toml`. When files are used, the format is deduced from -the file extension: `.toml` and `.tml` are parsed as TOML, everything else uses YAML decoding by -default. +Additional raw files can be loaded using the `--var` flag: To load files, the format for this flag +is `ENCODING:file:FILENAME`, for example `--var "myvar=yaml:file:config.kubeconfig"`. This allows +you to load files regardless of their extension and also allows to load raw files (that will be +kept as strings) using `"myvar=raw:file:logo.png"`. Raw file encoding is not supported for files +given as arguments, those files must have a recognized file extension. ### Embedding diff --git a/cmd/rudi/encoding/decode.go b/cmd/rudi/encoding/decode.go index 72ce090..8d62955 100644 --- a/cmd/rudi/encoding/decode.go +++ b/cmd/rudi/encoding/decode.go @@ -11,6 +11,7 @@ import ( "go.xrstf.de/rudi/cmd/rudi/types" "github.com/BurntSushi/toml" + "github.com/titanous/json5" "gopkg.in/yaml.v3" ) @@ -32,6 +33,12 @@ func Decode(input io.Reader, enc types.Encoding) (any, error) { return nil, fmt.Errorf("failed to parse file as JSON: %w", err) } + case types.Json5Encoding: + decoder := json5.NewDecoder(input) + if err := decoder.Decode(&data); err != nil { + return nil, fmt.Errorf("failed to parse file as JSON5: %w", err) + } + case types.YamlEncoding: decoder := yaml.NewDecoder(input) if err := decoder.Decode(&data); err != nil { diff --git a/cmd/rudi/go.mod b/cmd/rudi/go.mod index 72831f3..453eb64 100644 --- a/cmd/rudi/go.mod +++ b/cmd/rudi/go.mod @@ -10,6 +10,7 @@ require ( github.com/chzyer/readline v1.5.1 github.com/muesli/termenv v0.15.2 github.com/spf13/pflag v1.0.5 + github.com/titanous/json5 v1.0.0 go.xrstf.de/rudi v0.5.1 go.xrstf.de/rudi-contrib/semver v0.1.5 go.xrstf.de/rudi-contrib/set v0.1.1 diff --git a/cmd/rudi/go.sum b/cmd/rudi/go.sum index 00a4b0f..2632b67 100644 --- a/cmd/rudi/go.sum +++ b/cmd/rudi/go.sum @@ -18,6 +18,8 @@ github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -31,8 +33,12 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0= +github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/titanous/json5 v1.0.0 h1:hJf8Su1d9NuI/ffpxgxQfxh/UiBFZX7bMPid0rIL/7s= +github.com/titanous/json5 v1.0.0/go.mod h1:7JH1M8/LHKc6cyP5o5g3CSaRj+mBrIimTxzpvmckH8c= github.com/xrstf/colorjson v0.0.0-20231123184920-5ea6fecf578f h1:gVBqsyWwyxxzSGjfeOZVHGWMQNN7pgMwJzKdJc8sHzs= github.com/xrstf/colorjson v0.0.0-20231123184920-5ea6fecf578f/go.mod h1:AY6XdslHQYqT5ivYt21gXNpCjsck8iEoytnNfz3COxY= go.xrstf.de/rudi-contrib/semver v0.1.5 h1:qnp5dfoHk7X7EBuBHGplUfbtfpHcOD9f9zXo+GegErI= @@ -48,8 +54,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= diff --git a/cmd/rudi/options/options.go b/cmd/rudi/options/options.go index 1d2bb8b..fcaa535 100644 --- a/cmd/rudi/options/options.go +++ b/cmd/rudi/options/options.go @@ -43,8 +43,8 @@ func NewDefaultOptions() Options { func (o *Options) AddFlags(fs *pflag.FlagSet) { fs.SortFlags = false - stdinFormatFlag := newEnumFlag(&o.StdinFormat, types.AllEncodings...) - outputFormatFlag := newEnumFlag(&o.OutputFormat, types.AllEncodings...) + stdinFormatFlag := newEnumFlag(&o.StdinFormat, types.InputEncodings...) + outputFormatFlag := newEnumFlag(&o.OutputFormat, types.OutputEncodings...) coalescingFlag := newEnumFlag(&o.Coalescing, types.AllCoalescings...) fs.BoolVarP(&o.Interactive, "interactive", "i", o.Interactive, "Start an interactive REPL to run expressions.") diff --git a/cmd/rudi/types/const.go b/cmd/rudi/types/const.go index 9b74fa3..c4c2df2 100644 --- a/cmd/rudi/types/const.go +++ b/cmd/rudi/types/const.go @@ -23,18 +23,30 @@ func (e Encoding) IsValid() bool { } const ( - RawEncoding Encoding = "raw" - JsonEncoding Encoding = "json" - YamlEncoding Encoding = "yaml" - TomlEncoding Encoding = "toml" + RawEncoding Encoding = "raw" + JsonEncoding Encoding = "json" + Json5Encoding Encoding = "json5" + YamlEncoding Encoding = "yaml" + TomlEncoding Encoding = "toml" ) -var AllEncodings = []Encoding{ - RawEncoding, - JsonEncoding, - YamlEncoding, - TomlEncoding, -} +var ( + AllEncodings = []Encoding{ + RawEncoding, + JsonEncoding, + Json5Encoding, + YamlEncoding, + TomlEncoding, + } + + InputEncodings = AllEncodings + OutputEncodings = []Encoding{ + RawEncoding, + JsonEncoding, + YamlEncoding, + TomlEncoding, + } +) type Coalescing string diff --git a/cmd/rudi/util/files.go b/cmd/rudi/util/files.go index 703f08d..2a858b9 100644 --- a/cmd/rudi/util/files.go +++ b/cmd/rudi/util/files.go @@ -52,6 +52,8 @@ func getFileFormat(filename string) types.Encoding { switch strings.ToLower(filepath.Ext(filename)) { case ".json": return types.JsonEncoding + case ".json5": + return types.Json5Encoding case ".tml", ".toml": return types.TomlEncoding default: