Browse Source

Add a "--format-file" flag to "bashbrew cat" which overrides "--format" with the contents of a file (and add more useful functions to our template processing)

Tianon Gravi 9 năm trước cách đây
mục cha
commit
b244f42327

+ 34 - 18
bashbrew/go/src/bashbrew/cmd-cat.go

@@ -1,53 +1,69 @@
 package main
 package main
 
 
 import (
 import (
-	"encoding/json"
+	"bytes"
 	"fmt"
 	"fmt"
-	"os"
+	"io/ioutil"
 	"strings"
 	"strings"
 	"text/template"
 	"text/template"
 
 
 	"github.com/codegangsta/cli"
 	"github.com/codegangsta/cli"
+	"github.com/docker-library/go-dockerlibrary/pkg/templatelib"
 )
 )
 
 
-var DefaultCatFormat = `{{if (ne i 0)}}{{"\n\n"}}{{end}}{{if (eq .TagName "")}}{{.Manifest}}{{else}}{{.Manifest.GetTag .TagName}}{{end}}`
+var DefaultCatFormat = `
+{{- if i }}{{ "\n\n" }}{{ end -}}
+{{- .TagName | ternary (.Manifest.GetTag .TagName) .Manifest -}}
+`
 
 
 func cmdCat(c *cli.Context) error {
 func cmdCat(c *cli.Context) error {
 	repos, err := repos(c.Bool("all"), c.Args()...)
 	repos, err := repos(c.Bool("all"), c.Args()...)
 	if err != nil {
 	if err != nil {
-		return err
+		return cli.NewMultiError(fmt.Errorf(`failed gathering repo list`), err)
 	}
 	}
 
 
 	format := c.String("format")
 	format := c.String("format")
+	formatFile := c.String("format-file")
+
+	templateName := "--format"
+	tmplMultiErr := fmt.Errorf(`failed parsing --format %q`, format)
+	if formatFile != "" {
+		b, err := ioutil.ReadFile(formatFile)
+		if err != nil {
+			return cli.NewMultiError(fmt.Errorf(`failed reading --format-file %q`, formatFile), err)
+		}
+		templateName = formatFile
+		tmplMultiErr = fmt.Errorf(`failed parsing --format-file %q`, formatFile)
+		format = string(b)
+	}
 
 
 	var i int
 	var i int
-	tmpl, err := template.New("--format").Funcs(template.FuncMap{
+	tmpl, err := template.New(templateName).Funcs(templatelib.FuncMap).Funcs(template.FuncMap{
 		"i": func() int {
 		"i": func() int {
 			return i
 			return i
 		},
 		},
-		"join": strings.Join,
-		"json": func(v interface{}) (string, error) {
-			j, err := json.Marshal(v)
-			return string(j), err
-		},
 	}).Parse(format)
 	}).Parse(format)
 	if err != nil {
 	if err != nil {
-		return err
+		return cli.NewMultiError(tmplMultiErr, err)
 	}
 	}
 
 
 	var repo string
 	var repo string
 	for i, repo = range repos {
 	for i, repo = range repos {
-		//if i > 0 {
-		//	fmt.Println()
-		//}
-
 		r, err := fetch(repo)
 		r, err := fetch(repo)
 		if err != nil {
 		if err != nil {
-			return err
+			return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err)
 		}
 		}
 
 
-		err = tmpl.Execute(os.Stdout, r)
-		fmt.Println()
+		buf := &bytes.Buffer{}
+		err = tmpl.ExecuteTemplate(buf, templateName, r)
+		if err != nil {
+			return cli.NewMultiError(fmt.Errorf(`failed executing template`), err)
+		}
+		out := buf.String()
+		fmt.Print(out)
+		if !strings.HasSuffix(out, "\n") {
+			fmt.Println()
+		}
 	}
 	}
 
 
 	return nil
 	return nil

+ 7 - 1
bashbrew/go/src/bashbrew/main.go

@@ -322,13 +322,19 @@ func main() {
 				commonFlags["all"],
 				commonFlags["all"],
 				cli.StringFlag{
 				cli.StringFlag{
 					Name:  "format, f",
 					Name:  "format, f",
-					Usage: `change the format of the output (see Go's "text/template" package; https://golang.org/pkg/text/template/)`,
+					Usage: "change the `FORMAT` of the output",
 					Value: DefaultCatFormat,
 					Value: DefaultCatFormat,
 				},
 				},
+				cli.StringFlag{
+					Name:  "format-file, F",
+					Usage: "use the contents of `FILE` for \"--format\"",
+				},
 			},
 			},
 			Before: subcommandBeforeFactory("cat"),
 			Before: subcommandBeforeFactory("cat"),
 			Action: cmdCat,
 			Action: cmdCat,
 
 
+			Description: `see Go's "text/template" package (https://golang.org/pkg/text/template/) for details on the syntax expected in "--format"`,
+
 			Category: "plumbing",
 			Category: "plumbing",
 		},
 		},
 		{
 		{

+ 8 - 1
bashbrew/go/vendor/manifest

@@ -10,7 +10,7 @@
 		{
 		{
 			"importpath": "github.com/docker-library/go-dockerlibrary",
 			"importpath": "github.com/docker-library/go-dockerlibrary",
 			"repository": "https://github.com/docker-library/go-dockerlibrary",
 			"repository": "https://github.com/docker-library/go-dockerlibrary",
-			"revision": "ea2a7991ce3e7e1ba50a635040fa0ee6bb967aeb",
+			"revision": "e9ae2802d93d05d3e8d58b83ede12701035a4c3f",
 			"branch": "master"
 			"branch": "master"
 		},
 		},
 		{
 		{
@@ -27,6 +27,13 @@
 			"branch": "master",
 			"branch": "master",
 			"path": "/pkg/stripper"
 			"path": "/pkg/stripper"
 		},
 		},
+		{
+			"importpath": "github.com/docker-library/go-dockerlibrary/pkg/templatelib",
+			"repository": "https://github.com/docker-library/go-dockerlibrary",
+			"revision": "939e8fc63d2d9960bbe2450c7fbf007fbabfe3b6",
+			"branch": "master",
+			"path": "/pkg/templatelib"
+		},
 		{
 		{
 			"importpath": "golang.org/x/crypto/cast5",
 			"importpath": "golang.org/x/crypto/cast5",
 			"repository": "https://go.googlesource.com/crypto",
 			"repository": "https://go.googlesource.com/crypto",

+ 99 - 0
bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib.go

@@ -0,0 +1,99 @@
+package templatelib
+
+import (
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"strings"
+	"text/template"
+)
+
+func swapStringsFuncBoolArgsOrder(a func(string, string) bool) func(string, string) bool {
+	return func(str1 string, str2 string) bool {
+		return a(str2, str1)
+	}
+}
+
+func thingsActionFactory(name string, actOnFirst bool, action func([]interface{}, interface{}) interface{}) func(args ...interface{}) interface{} {
+	return func(args ...interface{}) interface{} {
+		if len(args) < 1 {
+			panic(fmt.Sprintf(`%q requires at least one argument`, name))
+		}
+
+		actArgs := []interface{}{}
+		for _, val := range args {
+			v := reflect.ValueOf(val)
+
+			switch v.Kind() {
+			case reflect.Slice, reflect.Array:
+				for i := 0; i < v.Len(); i++ {
+					actArgs = append(actArgs, v.Index(i).Interface())
+				}
+			default:
+				actArgs = append(actArgs, v.Interface())
+			}
+		}
+
+		var arg interface{}
+		if actOnFirst {
+			arg = actArgs[0]
+			actArgs = actArgs[1:]
+		} else {
+			arg = actArgs[len(actArgs)-1]
+			actArgs = actArgs[:len(actArgs)-1]
+		}
+
+		return action(actArgs, arg)
+	}
+}
+
+func stringsActionFactory(name string, actOnFirst bool, action func([]string, string) string) func(args ...interface{}) interface{} {
+	return thingsActionFactory(name, actOnFirst, func(args []interface{}, arg interface{}) interface{} {
+		str := arg.(string)
+		strs := []string{}
+		for _, val := range args {
+			strs = append(strs, val.(string))
+		}
+		return action(strs, str)
+	})
+}
+
+func stringsModifierActionFactory(a func(string, string) string) func([]string, string) string {
+	return func(strs []string, str string) string {
+		for _, mod := range strs {
+			str = a(str, mod)
+		}
+		return str
+	}
+}
+
+// TODO write some tests for these
+
+var FuncMap = template.FuncMap{
+	"hasPrefix": swapStringsFuncBoolArgsOrder(strings.HasPrefix),
+	"hasSuffix": swapStringsFuncBoolArgsOrder(strings.HasSuffix),
+
+	"ternary": func(truthy interface{}, falsey interface{}, val interface{}) interface{} {
+		if t, ok := template.IsTrue(val); !ok {
+			panic(fmt.Sprintf(`template.IsTrue(%+v) says things are NOT OK`, val))
+		} else if t {
+			return truthy
+		} else {
+			return falsey
+		}
+	},
+
+	"first": thingsActionFactory("first", true, func(args []interface{}, arg interface{}) interface{} { return arg }),
+	"last":  thingsActionFactory("last", false, func(args []interface{}, arg interface{}) interface{} { return arg }),
+
+	"json": func(v interface{}) (string, error) {
+		j, err := json.Marshal(v)
+		return string(j), err
+	},
+	"join":         stringsActionFactory("join", true, strings.Join),
+	"trimPrefixes": stringsActionFactory("trimPrefixes", false, stringsModifierActionFactory(strings.TrimPrefix)),
+	"trimSuffixes": stringsActionFactory("trimSuffixes", false, stringsModifierActionFactory(strings.TrimSuffix)),
+	"replace": stringsActionFactory("replace", false, func(strs []string, str string) string {
+		return strings.NewReplacer(strs...).Replace(str)
+	}),
+}