dati

A Go library/binary to parse & execute data against template langauges.
git clone git://src.gearsix.net/dati
Log | Files | Refs | Atom | README | LICENSE

commit 40334f2ac36e907b6b8c1a54462046d94cbf25f1
parent 0ae83bdad1a16e81b663da77005935200132dd74
Author: gearsix <gearsix@tuta.io>
Date:   Sun, 14 Mar 2021 13:21:39 +0000

added yaml support; ran gofmt

Diffstat:
Asrc/_test.go | 20++++++++++++++++++++
Msrc/data.go | 74+++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/data_test.go | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/suti.go | 6+++---
Msrc/template.go | 9++++-----
Msrc/template_test.go | 7++++---
6 files changed, 134 insertions(+), 77 deletions(-)

diff --git a/src/_test.go b/src/_test.go @@ -0,0 +1,20 @@ +package main + +import ( + "os" + "testing" +) + +func writeFile(path string, data string) (e error) { + var f *os.File + + if f, e = os.Create(path); e != nil { + return + } + if _, e = f.WriteString(data); e != nil { + return + } + f.Close() + + return +} diff --git a/src/data.go b/src/data.go @@ -3,13 +3,14 @@ package main import ( "encoding/json" "fmt" + "gopkg.in/yaml.v3" "io" "io/ioutil" "os" - "time" "path/filepath" - "strings" "sort" + "strings" + "time" ) // Data is the data type used to represent parsed Data (in any format). @@ -23,7 +24,9 @@ func getDataType(path string) string { func LoadData(lang string, in io.Reader) (d Data, e error) { var fbuf []byte if fbuf, e = ioutil.ReadAll(in); e != nil { - return + return make(Data), e + } else if len(fbuf) == 0 { + return make(Data), nil } if lang == "json" { @@ -32,6 +35,8 @@ func LoadData(lang string, in io.Reader) (d Data, e error) { } else { e = fmt.Errorf("invalid json %s", fbuf) } + } else if lang == "yaml" { + e = yaml.Unmarshal(fbuf, &d) } else { e = fmt.Errorf("'%s' is not a supported data language", lang) } @@ -95,13 +100,13 @@ func LoadDataFiles(order string, paths ...string) []Data { } } } - + return sortFileData(loaded, order) } func sortFileData(data map[string]Data, order string) []Data { sorted := make([]Data, 0, len(data)) - + if strings.HasPrefix(order, "filename") { if order == "filename-desc" { sorted = sortFileDataFilename("desc", data) @@ -123,7 +128,7 @@ func sortFileData(data map[string]Data, order string) []Data { sorted = append(sorted, d) } } - + return sorted } @@ -134,9 +139,9 @@ func sortFileDataFilename(direction string, data map[string]Data) []Data { fnames = append(fnames, filepath.Base(fpath)) } sort.Strings(fnames) - + if direction == "desc" { - for i := len(fnames)-1; i >= 0; i-- { + for i := len(fnames) - 1; i >= 0; i-- { for fpath, d := range data { if fnames[i] == filepath.Base(fpath) { sorted = append(sorted, d) @@ -145,11 +150,11 @@ func sortFileDataFilename(direction string, data map[string]Data) []Data { } } else { for _, fname := range fnames { - for fpath, d := range data { - if fname == filepath.Base(fpath) { - sorted = append(sorted, d) - } - } + for fpath, d := range data { + if fname == filepath.Base(fpath) { + sorted = append(sorted, d) + } + } } } return sorted @@ -165,7 +170,7 @@ func sortFileDataModified(direction string, data map[string]Data) []Data { stats[fpath] = stat } } - + modtimes := make([]time.Time, 0, len(data)) for _, stat := range stats { modtimes = append(modtimes, stat.ModTime()) @@ -179,7 +184,7 @@ func sortFileDataModified(direction string, data map[string]Data) []Data { return modtimes[i].Before(modtimes[j]) }) } - + for _, t := range modtimes { for fpath, stat := range stats { if t == stat.ModTime() { @@ -197,28 +202,28 @@ func GenerateSuperData(datakey string, d []Data, global ...Data) (superd Data) { datakey = "data" } superd = MergeData(global...) - + if superd[datakey] != nil { warn("global data has a key matching the datakey ('%s')\n", - "this value of this key will be overwritten") + "this value of this key will be overwritten") } superd[datakey] = d return } - - // MergeData combines all keys in `data` into a single Data object. If there's - // a conflict (duplicate key), the first found value is kept and the conflicting - // values are ignored. - func MergeData(data ...Data) Data { - merged := make(Data) - for _, d := range data { - for k, v := range d { - if merged[k] == nil { - merged[k] = v - } else { - warn("merge conflict for data key '%s'\n", k) - } - } - } - return merged - } -\ No newline at end of file + +// MergeData combines all keys in `data` into a single Data object. If there's +// a conflict (duplicate key), the first found value is kept and the conflicting +// values are ignored. +func MergeData(data ...Data) Data { + merged := make(Data) + for _, d := range data { + for k, v := range d { + if merged[k] == nil { + merged[k] = v + } else { + warn("merge conflict for data key '%s'\n", k) + } + } + } + return merged +} diff --git a/src/data_test.go b/src/data_test.go @@ -2,14 +2,21 @@ package main import ( "encoding/json" + "gopkg.in/yaml.v3" "os" "strings" "testing" ) -const goodJson1 = `{"example1":0}` -const goodJson2 = `{"example2":1}` -const badJson = `{"example":2:]}}` +const goodJson1 = `{"json":0}` +const goodJson2 = `{"json":1}` +const badJson = `{"json":2:]}}` + +const goodYaml1 = `yaml: 0 +` +const goodYaml2 = `yaml: "1" +` +const badYaml = `"yaml--: '2` func writeTestFile(path string, Data string) (e error) { var f *os.File @@ -41,12 +48,29 @@ func TestLoadData(t *testing.T) { t.Errorf("incorrect json: %s does not match %s", b, goodJson1) } } - if d, e = LoadData("json", strings.NewReader(badJson)); e == nil { t.Error("bad.json passed") } - if d, e = LoadData("json", strings.NewReader("")); e == nil { - t.Error("empty file passed") + if d, e = LoadData("json", strings.NewReader("")); e != nil || len(d) > 0 { + t.Errorf("empty file failed: %s, %s", d, e) + } + + if d, e = LoadData("yaml", strings.NewReader(goodYaml1)); e != nil { + t.Error(e) + } else if len(d) == 0 { + t.Error("no data loaded") + } else { + if b, e = yaml.Marshal(d); e != nil { + t.Error(e) + } else if string(b) != goodYaml1 { + t.Errorf("incorrect yaml: %s does not match %s", b, goodYaml1) + } + } + if d, e = LoadData("yaml", strings.NewReader(badYaml)); e == nil { + t.Error("bad.yaml passed") + } + if d, e = LoadData("yaml", strings.NewReader("")); e != nil || len(d) > 0 { + t.Errorf("empty file failed: %s, %s", d, e) } return @@ -63,8 +87,8 @@ func TestLoadDataFiles(t *testing.T) { if e = writeTestFile(p[0], goodJson2); e != nil { t.Skip("setup failure:", e) } - p = append(p, tdir+"/good1.json") - if e = writeTestFile(p[1], goodJson1); e != nil { + p = append(p, tdir+"/good1.yaml") + if e = writeTestFile(p[1], goodYaml1); e != nil { t.Skip("setup failure:", e) } p = append(p, tdir+"/bad.json") @@ -83,52 +107,52 @@ func TestLoadDataFiles(t *testing.T) { } else if string(b) == goodJson2 { t.Error("data returned out of order") } - - if b, e = json.Marshal(d[1]); e != nil { + + if b, e = yaml.Marshal(d[1]); e != nil { t.Error(e) - } else if string(b) == goodJson1 { + } else if string(b) == goodYaml1 { t.Error("data returned out of order") } } - - d = LoadDataFiles("filename-desc", tdir + "/*") + + d = LoadDataFiles("filename-desc", tdir+"/*") if len(d) == len(p) { t.Error("bad.json passed") } else if len(d) == 0 { t.Error("no data loaded") } else { - if b, e = json.Marshal(d[0]); e != nil { + if b, e = yaml.Marshal(d[0]); e != nil { t.Error(e) - } else if string(b) == goodJson1 { + } else if string(b) == goodYaml1 { t.Error("data returned out of order") } - + if b, e = json.Marshal(d[1]); e != nil { t.Error(e) } else if string(b) == goodJson2 { t.Error("data returned out of order") } } - + d = LoadDataFiles("modified", p...) if len(d) == len(p) { t.Error("bad.json passed") } else if len(d) == 0 { t.Error("no data loaded") } else { - if b, e = json.Marshal(d[0]); e != nil { + if b, e = yaml.Marshal(d[0]); e != nil { t.Error(e) - } else if string(b) == goodJson1 { + } else if string(b) == goodYaml1 { t.Error("data returned out of order") } - + if b, e = json.Marshal(d[1]); e != nil { t.Error(e) } else if string(b) == goodJson2 { t.Error("data returned out of order") } } - + d = LoadDataFiles("modified-desc", p...) if len(d) == len(p) { t.Error("bad.json passed") @@ -140,10 +164,10 @@ func TestLoadDataFiles(t *testing.T) { } else if string(b) == goodJson2 { t.Error("data returned out of order") } - - if b, e = json.Marshal(d[1]); e != nil { + + if b, e = yaml.Marshal(d[1]); e != nil { t.Error(e) - } else if string(b) == goodJson1 { + } else if string(b) == goodYaml1 { t.Error("data returned out of order") } } @@ -153,7 +177,7 @@ func TestMergeData(t *testing.T) { var e error var d []Data var m Data - + if m, e = LoadData("json", strings.NewReader(goodJson1)); e == nil { d = append(d, m) } else { @@ -164,10 +188,20 @@ func TestMergeData(t *testing.T) { } else { t.Skip("setup failure:", e) } - + if m, e = LoadData("json", strings.NewReader(goodYaml1)); e == nil { + d = append(d, m) + } else { + t.Skip("setup failure:", e) + } + if m, e = LoadData("json", strings.NewReader(goodYaml2)); e == nil { + d = append(d, m) + } else { + t.Skip("setup failure:", e) + } + m = nil m = MergeData(d...) - if m["example1"] == nil || m["example2"] == nil { + if m["json"] == nil || m["yaml"] == nil { t.Error("missing global keys") } } @@ -178,7 +212,7 @@ func TestGenerateSuperData(t *testing.T) { var gd []Data var d []Data var sd Data - + if data, e = LoadData("json", strings.NewReader(goodJson1)); e == nil { gd = append(gd, data) } else { @@ -189,12 +223,12 @@ func TestGenerateSuperData(t *testing.T) { } else { t.Skip("setup failure:", e) } - if data, e = LoadData("json", strings.NewReader(goodJson2)); e == nil { + if data, e = LoadData("yaml", strings.NewReader(goodYaml2)); e == nil { gd = append(gd, data) } else { t.Skip("setup failure:", e) } - if data, e = LoadData("json", strings.NewReader(goodJson1)); e == nil { + if data, e = LoadData("yaml", strings.NewReader(goodYaml1)); e == nil { d = append(d, data) } else { t.Skip("setup failure:", e) @@ -205,7 +239,6 @@ func TestGenerateSuperData(t *testing.T) { t.Skip("setup failure:", e) } - sd = GenerateSuperData("testdata", d, gd...) if sd["testdata"] == nil { t.Log(sd) diff --git a/src/suti.go b/src/suti.go @@ -36,7 +36,7 @@ func init() { _, err = fmt.Fscanln(cfgf, &cfgln) for i, a := range strings.Split(cfgln, "=") { if i == 0 { - a = "-"+a + a = "-" + a } cfgargs = append(cfgargs, a) } @@ -57,7 +57,7 @@ func main() { gd := LoadDataFiles("", options.GlobalDataPaths...) d := LoadDataFiles(options.SortData, options.DataPaths...) sd := GenerateSuperData(options.DataKey, d, gd...) - + if t, e := LoadTemplateFile(options.RootPath, options.PartialPaths...); e != nil { warn("unable to load templates (%s)", e) } else if out, err := ExecuteTemplate(t, sd); err != nil { @@ -65,7 +65,7 @@ func main() { } else { fmt.Println(out.String()) } - + return } diff --git a/src/template.go b/src/template.go @@ -4,9 +4,9 @@ import ( "bytes" "fmt" hmpl "html/template" - "reflect" "os" "path/filepath" + "reflect" "strings" tmpl "text/template" ) @@ -68,7 +68,7 @@ func ExecuteTemplate(t Template, d Data) (result bytes.Buffer, err error) { err = fmt.Errorf("missing parameters") return } - + tv := reflect.ValueOf(t) tt := reflect.TypeOf(t) if tt.String() == "*template.Template" { // tmpl or hmpl @@ -81,6 +81,6 @@ func ExecuteTemplate(t Template, d Data) (result bytes.Buffer, err error) { } else { err = fmt.Errorf("unable to infer template type '%s'", tt.String()) } - + return -} -\ No newline at end of file +} diff --git a/src/template_test.go b/src/template_test.go @@ -2,8 +2,8 @@ package main import ( "bytes" - "testing" "strings" + "testing" ) const tmplRootGood = "{{ .example1 }} {{ template \"tmplPartialGood\" . }}" @@ -83,7 +83,7 @@ func TestExecuteTemplate(t *testing.T) { var sd, gd, d Data var tmpl, hmpl Template var results bytes.Buffer - + if gd, e = LoadData("json", strings.NewReader(goodJson1)); e != nil { t.Skip("setup failure:", e) } @@ -99,11 +99,12 @@ func TestExecuteTemplate(t *testing.T) { if hmpl, e = LoadTemplateFile(hmplRootGood, hmplPartialGood); e != nil { t.Skip("setup failure:", e) } - + if results, e = ExecuteTemplate(tmpl, sd); e != nil { t.Error(e) } if results, e = ExecuteTemplate(hmpl, sd); e != nil { t.Error(e) } + t.Log(results) }