Skip to content

Commit ecf678f

Browse files
authored
Add TOML file parsing functionality (#164)
* Add TOML file parsing functionality to ConfigurationFile * Fix TOML parsing by unmarshalling JSON data before remarshal * Enhance TOML parsing by normalizing types and improving JSON decoding * Refactor TOML parsing to use JSON decoder for improved type handling
1 parent 4586d7b commit ecf678f

2 files changed

Lines changed: 91 additions & 2 deletions

File tree

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ require (
134134
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
135135
github.com/opencontainers/go-digest v1.0.0 // indirect
136136
github.com/opencontainers/image-spec v1.1.0 // indirect
137-
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
137+
github.com/pelletier/go-toml/v2 v2.2.2
138138
github.com/pierrec/lz4/v4 v4.1.21 // indirect
139139
github.com/pkg/errors v0.9.1 // indirect
140140
github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -164,7 +164,7 @@ require (
164164
golang.org/x/mod v0.26.0 // indirect
165165
golang.org/x/net v0.42.0 // indirect
166166
golang.org/x/text v0.28.0 // indirect
167-
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
167+
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af
168168
golang.org/x/tools v0.35.0 // indirect
169169
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
170170
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect

parser/parser.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"strconv"
88
"strings"
9+
"time"
910

1011
"emperror.dev/errors"
1112
"github.com/apex/log"
@@ -14,6 +15,7 @@ import (
1415
"github.com/goccy/go-json"
1516
"github.com/icza/dyno"
1617
"github.com/magiconair/properties"
18+
"github.com/pelletier/go-toml/v2"
1719
"github.com/tidwall/pretty"
1820
"gopkg.in/ini.v1"
1921
"gopkg.in/yaml.v3"
@@ -30,6 +32,7 @@ const (
3032
Ini = "ini"
3133
Json = "json"
3234
Xml = "xml"
35+
Toml = "toml"
3336
)
3437

3538
type ReplaceValue struct {
@@ -228,6 +231,8 @@ func (f *ConfigurationFile) Parse(file ufs.File) error {
228231
err = f.parseIniFile(file)
229232
case Xml:
230233
err = f.parseXmlFile(file)
234+
case Toml:
235+
err = f.parseTomlFile(file)
231236
}
232237
return err
233238
}
@@ -480,6 +485,90 @@ func (f *ConfigurationFile) parseYamlFile(file ufs.File) error {
480485
return nil
481486
}
482487

488+
// Parses a toml file and updates any matching key/value pairs before persisting
489+
// it back to the disk.
490+
func (f *ConfigurationFile) parseTomlFile(file ufs.File) error {
491+
b, err := io.ReadAll(file)
492+
if err != nil {
493+
return err
494+
}
495+
496+
i := make(map[string]interface{})
497+
if err := toml.Unmarshal(b, &i); err != nil {
498+
return err
499+
}
500+
501+
// Convert to JSON to reuse IterateOverJson for value replacement.
502+
jsonBytes, err := json.Marshal(dyno.ConvertMapI2MapS(i))
503+
if err != nil {
504+
return err
505+
}
506+
507+
data, err := f.IterateOverJson(jsonBytes)
508+
if err != nil {
509+
return err
510+
}
511+
512+
var jsonData interface{}
513+
decoder := json.NewDecoder(bytes.NewReader(data))
514+
decoder.UseNumber()
515+
if err := decoder.Decode(&jsonData); err != nil {
516+
return err
517+
}
518+
jsonData = normalizeTomlTypes(jsonData)
519+
520+
// Remarshal back to TOML format.
521+
marshaled, err := toml.Marshal(jsonData)
522+
if err != nil {
523+
return err
524+
}
525+
526+
if _, err := file.Seek(0, io.SeekStart); err != nil {
527+
return err
528+
}
529+
if err := file.Truncate(0); err != nil {
530+
return err
531+
}
532+
533+
if _, err := io.Copy(file, bytes.NewReader(marshaled)); err != nil {
534+
return errors.Wrap(err, "parser: failed to write toml file to disk")
535+
}
536+
return nil
537+
}
538+
539+
func normalizeTomlTypes(value interface{}) interface{} {
540+
switch typed := value.(type) {
541+
case map[string]interface{}:
542+
for key, item := range typed {
543+
typed[key] = normalizeTomlTypes(item)
544+
}
545+
return typed
546+
case []interface{}:
547+
for i := range typed {
548+
typed[i] = normalizeTomlTypes(typed[i])
549+
}
550+
return typed
551+
case json.Number:
552+
if intVal, err := typed.Int64(); err == nil {
553+
return intVal
554+
}
555+
if floatVal, err := typed.Float64(); err == nil {
556+
return floatVal
557+
}
558+
return typed.String()
559+
case string:
560+
if timeVal, err := time.Parse(time.RFC3339Nano, typed); err == nil {
561+
return timeVal
562+
}
563+
if timeVal, err := time.Parse(time.RFC3339, typed); err == nil {
564+
return timeVal
565+
}
566+
return typed
567+
default:
568+
return value
569+
}
570+
}
571+
483572
// Parses a text file using basic find and replace. This is a highly inefficient method of
484573
// scanning a file and performing a replacement. You should attempt to use anything other
485574
// than this function where possible.

0 commit comments

Comments
 (0)