diff --git a/build.sh b/build.sh index fb8204e..d8971b3 100755 --- a/build.sh +++ b/build.sh @@ -25,7 +25,7 @@ then exit fi -go build -o goyamp -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" goyamp.go +go build -o goyamp -ldflags "-X main.Version=${version}" ( cd test @@ -37,10 +37,11 @@ then go tool cover -html=coverage.out fi -GOOS=windows GOARCH=amd64 go build -o goyamp.exe -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" goyamp.go -GOOS=darwin GOARCH=amd64 go build -o goyamp_mac -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" goyamp.go -GOOS=linux GOARCH=arm GOARM=7 go build -o goyamp_arm7 -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" goyamp.go -GOOS=linux GOARCH=arm GOARM=6 go build -o goyamp_arm6 -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" goyamp.go +GOOS=windows GOARCH=amd64 go build -o goyamp.exe -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" +GOOS=darwin GOARCH=amd64 go build -o goyamp_mac -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" +GOOS=linux GOARCH=arm GOARM=7 go build -o goyamp_arm7 -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" +GOOS=linux GOARCH=arm GOARM=6 go build -o goyamp_arm6 -ldflags "-X github.com/birchb1024/goyamp.Version=${version}" + if [[ "${args}" == "package" ]] then diff --git a/examples/deepmerge.lua b/examples/deepmerge.lua new file mode 100644 index 0000000..22d253c --- /dev/null +++ b/examples/deepmerge.lua @@ -0,0 +1,179 @@ +#!/usr/bin/env glua +module("dm", package.seeall) + +_ = [[ +This file contains the deepmerge(a, b) function. + +Deepmerge takes two objects A and B, normally expected to be tables containing lists and maps. +It scans the tables and based on the kind of table it merges the two together and returns the merge. +If there is a conflict A always takes priority over B. Conflicts occur if two maps have the same key, +or there are differing types. Let us see the different cases: + +Maps with keys + + { a = 1, c = 4, d = {y = 10} }, { a = 2, b = 3, d = { y = 99, u = 100 } } + + { + a = 1, + b = 3, + c = 4, + d = { + u = 100, + y = 10 + } + } + + +Arrays (sequential integer keys with no gaps): + + Lists (where there are duplicate items) + + One list is added to the end of the other. + + { 1, 1, 2, 3 }, {3, 4, 5, 1} + + { 1, 1, 2, 3, 3, 4, 5, 1} + + Sets (where both A and B have unique elements) + + A set union is returned + + { 1, 2, 3 }, {3, 4, 5, 1} + + { 1, 2, 3, 4, 5} + +]] + +-- --Debug functions, uncomment when needed +-- local inspect = require('inspect') +-- ix = function (x) print(inspect.inspect(x)) end +-- ix(args) + +-- classify - Return 'set', 'list' or 'map' for table types else the Lua type name +function classify(t) + if type(t) ~= "table" then return type(t) end + -- Are all the keys integer? + for k,_ in pairs(t) do + if type(k) ~= "number" then + return "map" + end + end + -- Determine if a Lua table is a sequence of keys with nil holes. https://ericjmritz.wordpress.com/2014/02/26/lua-is_array/ + local i = 0 + for _,_ in pairs(t) do + i = i + 1 + if t[i] == nil then return "map" end + end + if i == 0 then -- empty list or map? + if getmetatable(t) == mapy then + return "map" + end + if getmetatable(t) == seqy then + return "list" + end + end + + -- See if the vlaues are unique, if not it's a list + local set = {} + for _, v in ipairs(t) do + if set[v] ~= nil then return "list" end + set[v] = true + end + + return "set" +end + +function set_merge(a, b) + -- Treat tables as sets. + -- result randomly ordered + local elements = {} + for i, v in ipairs(a) do + elements[v] = true + end + for i, v in ipairs(b) do + elements[v] = true + end + local results = {} + setmetatable(results, seqy) + for k, _ in pairs(elements) do + table.insert(results, k) + end + return results +end + +function list_merge(a, b) + -- Treat tables as a list, i.e. possibly with duplicates and ordered. + -- result ordered a first, then b + local result = {} + setmetatable(result, seqy) + for i, v in ipairs(a) do + result[i] = v + end + for i, v in ipairs(b) do + table.insert(result, v) + end + return result +end + +function map_merge(a, b) + -- map elements with same keys to be merged, otherwise just added + local result = {} + setmetatable(result, mapy) + for ka, va in pairs(a) do + if b[ka] ~= nil then -- both maps have this key + result[ka] = deep_merge(va, b[ka]) + else + result[ka] = va + end + end + + for kb, vb in pairs(b) do + if a[kb] == nil then -- A does not have this key + result[kb] = vb -- For a hard earned thirst. + else + -- A already has this key and it trumps B + end + end + return result +end + +function isarray(typ) + arraytypes = {set = true, list = true} + return arraytypes[typ] ~= nil +end +-- +-- Deep merge two trees +-- +function dm.deep_merge(a, b) + --ix({"deep_merge: ", a, b}) + local at = classify(a) + local bt = classify(b) + + if at == "number" or at == "string" then + return a + end + + if at == bt then + if at == "set" then + return set_merge(a, b) + elseif at == "list" then + return list_merge(a, b) + elseif at == "map" then + return map_merge(a, b) + else + return a + end + else + if isarray(at) and isarray(bt) then + return list_merge(a, b) + else + return a + end + end +end + +return dm + +--ix(deep_merge({ a = 1, c = 4, d = {y = 10} }, { a = 2, b = 3, d = { y = 99, u = 100 } })) +--ix(deep_merge({ 1, 1, 2, 3 }, {3, 4, 5, 1})) +--ix(deep_merge({ 1, 2, 3 }, {3, 4, 5, 1})) \ No newline at end of file diff --git a/goyamp.go b/goyamp.go index 6564b89..7756ecd 100755 --- a/goyamp.go +++ b/goyamp.go @@ -5,9 +5,10 @@ import ( "fmt" "github.com/birchb1024/goyamp/internal" "io" - "io/ioutil" "log" "os" + "runtime/debug" + "strings" ) func helpText(out io.Writer, doOrNotDo bool) { @@ -42,8 +43,13 @@ func makeBooleanFlag(flagVar *bool, switchName string, desc string) { flag.BoolVar(flagVar, string(switchName[0]), false, desc) } +// Version provides the Git version tag used in the build of the binary +var Version string + func main() { + version := getBuildInfoVersion(Version) + var help, debugFlag bool var outputFormatVar string @@ -75,7 +81,7 @@ func main() { } if !debugFlag { - log.SetOutput(ioutil.Discard) + log.SetOutput(io.Discard) } log.Printf("output = %#v", outputFormatVar) @@ -83,7 +89,7 @@ func main() { if len(flag.Args()) > 0 { commandArgs = flag.Args()[1:] } - engine := internal.NewExpander(commandArgs, os.Environ(), os.Stdout, outFormat) + engine := internal.NewExpander(commandArgs, os.Environ(), os.Stdout, outFormat, version) var err error if len(flag.Args()) == 0 { @@ -104,3 +110,24 @@ func main() { panic(2) } } + +func getBuildInfoVersion(ldflagVersion string) string { + parts := []string{} + if ldflagVersion != "" { + parts = append(parts, ldflagVersion) + } + info, ok := debug.ReadBuildInfo() + if !ok { + return strings.Join(parts, "-") + } + for _, kv := range info.Settings { + switch kv.Key { + case "vcs.revision": + parts = append(parts, kv.Value[:9]) + //case "vcs.time":LastCommit, _ = time.Parse(time.RFC3339, kv.Value) + case "vcs.modified": + parts = append(parts, "dirty") + } + } + return strings.Join(parts, "-") +} diff --git a/internal/api.go b/internal/api.go index f018b7e..2ee53d9 100644 --- a/internal/api.go +++ b/internal/api.go @@ -22,7 +22,7 @@ func (engine *Expander) init(environment []string, argv []string) { pwd, err := os.Getwd() if err != nil { pwd = "." - fmt.Fprintf(os.Stderr, "%v", err) + _, _ = fmt.Fprintf(os.Stderr, "%v", err) } engine.globals = &env{ engine: engine, @@ -30,7 +30,7 @@ func (engine *Expander) init(environment []string, argv []string) { bind: map[string]yamly{ "argv": classify(argv), "env": envMap, - "__VERSION__": stringy(Version), + "__VERSION__": stringy(engine.version), "__DIR__": stringy(pwd), }, } @@ -47,14 +47,14 @@ const ( LINES ) -// // NewExpander creates a Goyamp macro expansion engine. -func NewExpander(commandArgs []string, environment []string, ow io.Writer, format Syntax) Expander { +func NewExpander(commandArgs []string, environment []string, ow io.Writer, format Syntax, version string) Expander { ex := Expander{ globals: nil, output: ow, outFormat: format, + version: version, } ex.init(environment, commandArgs) diff --git a/internal/goyamp.go b/internal/goyamp.go index 93fb103..f1b63d1 100644 --- a/internal/goyamp.go +++ b/internal/goyamp.go @@ -11,42 +11,13 @@ import ( "path/filepath" "reflect" "regexp" - "runtime/debug" "sort" "strconv" "strings" ) -// Version provides the Git version tag used in the build of the binary -var Version string var documentCount int -func init() { - if Version != "" { - return - } - info, ok := debug.ReadBuildInfo() - if !ok { - return - } - parts := make([]string, 0, 3) - parts = append(parts, info.Main.Version) - for _, kv := range info.Settings { - if kv.Value == "" { - continue - } - switch kv.Key { - case "vcs.revision": - - parts = append(parts, kv.Value[:7]) - case "vcs.time": - //LastCommit, _ = time.Parse(time.RFC3339, kv.Value) - case "vcs.modified": - parts = append(parts, "dirty") - } - } - Version = strings.Join(parts, "-") -} type yamly interface { expand(binding *env) yamly @@ -107,6 +78,7 @@ type Expander struct { globals *env output io.Writer outFormat Syntax + version string } func (e env) String() string { return fmt.Sprintf("an environment...") } diff --git a/test/goyamp_test.go b/test/goyamp_test.go index 8c1b0b1..5f46266 100644 --- a/test/goyamp_test.go +++ b/test/goyamp_test.go @@ -18,7 +18,8 @@ func fileRunner(output io.Writer, filename string, format internal.Syntax) error []string{"A", "B", "C", "D"}, []string{"USERNAME=birchb", "USER=birchb", "TEST_EMBEDDED=x=y,y=5"}, output, - format) + format, + "9.9.9") return engine.ExpandFile(filename) }