From 0c109ea60d888e4febf50ef97e783b87d693258c Mon Sep 17 00:00:00 2001 From: Kyoichiro Yamada Date: Sun, 8 Dec 2019 22:53:52 +0900 Subject: [PATCH] support custom formatter fix: #48 --- command/empty_test.go | 2 +- command/formatter.go | 66 +++++++++++++++++++++++ command/list.go | 7 +-- command/list_test.go | 39 ++++++++++---- gogh/custom_formatter.go | 99 +++++++++++++++++++++++++++++++++++ gogh/custom_formatter_test.go | 63 ++++++++++++++++++++++ gogh/formatter.go | 88 +++++++++++++++---------------- gogh/formatter_test.go | 29 +++++----- gogh/repo_test.go | 2 + main.go | 8 +-- 10 files changed, 319 insertions(+), 84 deletions(-) create mode 100644 command/formatter.go create mode 100644 gogh/custom_formatter.go create mode 100644 gogh/custom_formatter_test.go diff --git a/command/empty_test.go b/command/empty_test.go index 8dd76bb6..62119fe8 100644 --- a/command/empty_test.go +++ b/command/empty_test.go @@ -39,7 +39,7 @@ func TestEmpty(t *testing.T) { })) assert.NoError(t, Get(ctx, false, false, false, mustRepo("kyoh86/gogh"))) assert.NoError(t, Fork(ctx, false, false, false, false, "", "", mustRepo("kyoh86/gogh"))) - assert.NoError(t, List(ctx, gogh.ProjectListFormatShort, false, false, "")) + assert.NoError(t, List(ctx, gogh.ShortFormatter(), false, false, "")) proj1 := filepath.Join(tmp, "github.com", "kyoh86", "gogh", ".git") require.NoError(t, os.MkdirAll(proj1, 0755)) assert.NoError(t, Root(ctx, false)) diff --git a/command/formatter.go b/command/formatter.go new file mode 100644 index 00000000..dc033180 --- /dev/null +++ b/command/formatter.go @@ -0,0 +1,66 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/kyoh86/gogh/gogh" +) + +// ProjectListFormat specifies how gogh prints a project. +type ProjectListFormat struct { + label string + formatter gogh.ProjectListFormatter +} + +func (f *ProjectListFormat) Set(value string) error { + switch value { + case ProjectListFormatLabelRelPath: + f.formatter = gogh.RelPathFormatter() + return nil + case ProjectListFormatLabelFullPath: + f.formatter = gogh.FullPathFormatter() + return nil + case ProjectListFormatLabelURL: + f.formatter = gogh.URLFormatter() + return nil + case ProjectListFormatLabelShort: + f.formatter = gogh.ShortFormatter() + return nil + } + if strings.HasPrefix(value, "custom:") { + er, err := gogh.CustomFormatter(strings.TrimPrefix(value, "custom:")) + if err != nil { + return fmt.Errorf("format custom: must have following valid template %w", err) + } + f.formatter = er + return nil + } + return fmt.Errorf("format must be one of %s or 'custom:', got '%s'", strings.Join(ProjectListFormats(), ","), value) +} + +func (f *ProjectListFormat) Formatter() gogh.ProjectListFormatter { + return f.formatter +} + +// ProjectListFormat choices. +const ( + ProjectListFormatLabelShort = "short" + ProjectListFormatLabelFullPath = "full" + ProjectListFormatLabelURL = "url" + ProjectListFormatLabelRelPath = "relative" +) + +func (f ProjectListFormat) String() string { + return f.label +} + +// ProjectListFormats shows all of ProjectListFormat constants. +func ProjectListFormats() []string { + return []string{ + ProjectListFormatLabelShort, + ProjectListFormatLabelFullPath, + ProjectListFormatLabelURL, + ProjectListFormatLabelRelPath, + } +} diff --git a/command/list.go b/command/list.go index 744599b3..93456802 100644 --- a/command/list.go +++ b/command/list.go @@ -5,17 +5,12 @@ import ( ) // List local projects -func List(ctx gogh.Context, format gogh.ProjectListFormat, primary bool, isPublic bool, query string) error { +func List(ctx gogh.Context, formatter gogh.ProjectListFormatter, primary bool, isPublic bool, query string) error { var walk gogh.Walker = gogh.Walk if primary { walk = gogh.WalkInPrimary } - formatter, err := format.Formatter() - if err != nil { - return err - } - if err := gogh.Query(ctx, query, walk, func(p *gogh.Project) error { if isPublic { repo, err := gogh.ParseProject(p) diff --git a/command/list_test.go b/command/list_test.go index b018aa18..d2925017 100644 --- a/command/list_test.go +++ b/command/list_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" ) -func ExampleList() { +func ExampleList_url() { tmp, _ := ioutil.TempDir(os.TempDir(), "gogh-test") defer os.RemoveAll(tmp) _ = os.MkdirAll(filepath.Join(tmp, "example.com", "kyoh86", "gogh", ".git"), 0755) @@ -24,7 +24,7 @@ func ExampleList() { VRoot: []string{tmp}, GitHub: config.GitHubConfig{Host: "example.com"}, }, - gogh.ProjectListFormatURL, + gogh.URLFormatter(), true, false, "", @@ -36,28 +36,45 @@ func ExampleList() { // https://example.com/owner/name } -func TestList(t *testing.T) { +func ExampleList_custom() { tmp, _ := ioutil.TempDir(os.TempDir(), "gogh-test") defer os.RemoveAll(tmp) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "example.com", "kyoh86", "gogh", ".git"), 0755)) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "example.com", "owner", "name", ".git"), 0755)) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "example.com", "owner", "empty"), 0755)) + _ = os.MkdirAll(filepath.Join(tmp, "example.com", "kyoh86", "gogh", ".git"), 0755) + _ = os.MkdirAll(filepath.Join(tmp, "example.com", "owner", "name", ".git"), 0755) + _ = os.MkdirAll(filepath.Join(tmp, "example.com", "owner", "empty"), 0755) - assert.Error(t, command.List(&config.Config{ + fmter, err := gogh.CustomFormatter("{{short .}};;{{relative .}}") + if err != nil { + panic(err) + } + if err := command.List(&config.Config{ VRoot: []string{tmp}, GitHub: config.GitHubConfig{Host: "example.com"}, }, - gogh.ProjectListFormat("invalid format"), - false, + fmter, + true, false, "", - ), "invalid format") + ); err != nil { + panic(err) + } + // Unordered output: + // gogh;;example.com/kyoh86/gogh + // name;;example.com/owner/name +} + +func TestList(t *testing.T) { + tmp, _ := ioutil.TempDir(os.TempDir(), "gogh-test") + defer os.RemoveAll(tmp) + require.NoError(t, os.MkdirAll(filepath.Join(tmp, "example.com", "kyoh86", "gogh", ".git"), 0755)) + require.NoError(t, os.MkdirAll(filepath.Join(tmp, "example.com", "owner", "name", ".git"), 0755)) + require.NoError(t, os.MkdirAll(filepath.Join(tmp, "example.com", "owner", "empty"), 0755)) assert.Error(t, command.List(&config.Config{ VRoot: []string{tmp, "/\x00"}, GitHub: config.GitHubConfig{Host: "example.com"}, }, - gogh.ProjectListFormatURL, + gogh.URLFormatter(), false, false, "", diff --git a/gogh/custom_formatter.go b/gogh/custom_formatter.go new file mode 100644 index 00000000..6efe1b8f --- /dev/null +++ b/gogh/custom_formatter.go @@ -0,0 +1,99 @@ +package gogh + +import ( + "bytes" + "fmt" + "io" + "text/template" +) + +type customListFormatter struct { + template *template.Template + *shortListFormatter + *fullPathFormatter + *urlFormatter + *relPathFormatter +} + +func CustomFormatter(format string) (ProjectListFormatter, error) { + // share list + simple := &simpleCollector{} + + // tmp.Funcs( + short := &shortListFormatter{ + dups: map[string]bool{}, + simpleCollector: simple, + } + full := &fullPathFormatter{ + simpleCollector: simple, + } + url := &urlFormatter{ + simpleCollector: simple, + } + rel := &relPathFormatter{ + simpleCollector: simple, + } + // NOTE: FuncMap is the type of the map defining the mapping from names to functions. + // Each function must have either a single return value, or two return values of + // which the second has type error. In that case, if the second (error) + // return value evaluates to non-nil during execution, execution terminates and + // Execute returns that error. + // + // When template execution invokes a function with an argument list, that list + // must be assignable to the function's parameter types. Functions meant to + // apply to arguments of arbitrary type can use parameters of type interface{} or + // of type reflect.Value. Similarly, functions meant to return a result of arbitrary + // type can return interface{} or reflect.Value. + tmp, err := template.New("").Funcs(template.FuncMap{ + "short": formatToTemplateFunc(short.format), + "full": formatToTemplateFunc(full.format), + "url": formatToTemplateFunc(url.format), + "relative": formatToTemplateFunc(rel.format), + "null": func() string { return "\x00" }, + }).Parse(format) + if err != nil { + return nil, fmt.Errorf("failed to parse custom format %w", err) + } + + return &customListFormatter{ + template: tmp, + shortListFormatter: short, + fullPathFormatter: full, + urlFormatter: url, + relPathFormatter: rel, + }, nil +} + +func formatToTemplateFunc(format func(w io.Writer, project *Project) error) func(project *Project) (string, error) { + return func(project *Project) (string, error) { + buf := new(bytes.Buffer) + if err := format(buf, project); err != nil { + return "", err + } + return buf.String(), nil + } +} +func (f *customListFormatter) format(w io.Writer, project *Project) error { + return f.template.Execute(w, project) +} + +func (f *customListFormatter) Add(r *Project) { + // add to short list formatter (it has special "Add" func) + f.shortListFormatter.Add(r) +} + +func (f *customListFormatter) Len() int { + return f.shortListFormatter.Len() +} + +func (f *customListFormatter) PrintAll(w io.Writer, sep string) error { + for _, project := range f.shortListFormatter.list { + if err := f.format(w, project); err != nil { + return err + } + if _, err := fmt.Fprint(w, sep); err != nil { + return err + } + } + return nil +} diff --git a/gogh/custom_formatter_test.go b/gogh/custom_formatter_test.go new file mode 100644 index 00000000..632ce529 --- /dev/null +++ b/gogh/custom_formatter_test.go @@ -0,0 +1,63 @@ +package gogh + +import ( + "bytes" + "strings" + "testing" + + "github.com/kyoh86/gogh/internal/context" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCustomListFormatter(t *testing.T) { + t.Run("null separator", func(t *testing.T) { + project1, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/foo") + require.NoError(t, err) + formatter, err := CustomFormatter("{{short .}}{{null}}{{full .}}{{null}}{{relative .}}") + require.NoError(t, err) + formatter.Add(project1) + assert.Equal(t, 1, formatter.Len()) + var buf bytes.Buffer + require.NoError(t, formatter.PrintAll(&buf, "\r\n")) + expected := "foo\x00/go/src/github.com/kyoh86/foo\x00github.com/kyoh86/foo\r\n" + assert.Equal(t, expected, buf.String()) + }) + t.Run("normal separator", func(t *testing.T) { + project1, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/foo") + require.NoError(t, err) + project2, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/bar") + require.NoError(t, err) + project3, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh87/bar") + require.NoError(t, err) + project4, err := parseProject(&context.MockContext{MGitHubHost: "example.com"}, "/go/src", "/go/src/example.com/kyoh86/bar") + require.NoError(t, err) + project5, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/baz") + require.NoError(t, err) + project6, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/foo", "/foo/github.com/kyoh86/baz") + require.NoError(t, err) + + formatter, err := CustomFormatter("{{short .}};;{{full .}};;{{relative .}}") + require.NoError(t, err) + formatter.Add(project1) + formatter.Add(project2) + formatter.Add(project3) + formatter.Add(project4) + formatter.Add(project5) + formatter.Add(project6) + assert.Equal(t, 6, formatter.Len()) + var buf bytes.Buffer + require.NoError(t, formatter.PrintAll(&buf, "\n")) + expected := ` +foo ;; /go/src/ github.com/kyoh86/foo ;;github.com/kyoh86/foo +github.com/kyoh86/bar ;; /go/src/ github.com/kyoh86/bar ;;github.com/kyoh86/bar +kyoh87/bar ;; /go/src/ github.com/kyoh87/bar ;;github.com/kyoh87/bar +example.com/kyoh86/bar ;; /go/src/ example.com/kyoh86/bar;;example.com/kyoh86/bar +/go/src/github.com/kyoh86/baz ;; /go/src/ github.com/kyoh86/baz ;;github.com/kyoh86/baz +/foo/github.com/kyoh86/baz ;; /foo/ github.com/kyoh86/baz ;;github.com/kyoh86/baz +` + expected = strings.Replace(expected, " ", "", -1) + expected = strings.TrimLeft(expected, "\n") + assert.Equal(t, expected, buf.String()) + }) +} diff --git a/gogh/formatter.go b/gogh/formatter.go index dcd08249..964a7b6d 100644 --- a/gogh/formatter.go +++ b/gogh/formatter.go @@ -10,52 +10,14 @@ type ProjectListFormatter interface { Add(*Project) Len() int PrintAll(io.Writer, string) error -} - -// ProjectListFormat specifies how gogh prints a project. -type ProjectListFormat string - -// ProjectListFormat choices. -const ( - ProjectListFormatShort = ProjectListFormat("short") - ProjectListFormatFullPath = ProjectListFormat("full") - ProjectListFormatURL = ProjectListFormat("url") - ProjectListFormatRelPath = ProjectListFormat("relative") -) - -func (f ProjectListFormat) String() string { - return string(f) -} - -// ProjectListFormats shows all of ProjectListFormat constants. -func ProjectListFormats() []string { - return []string{ - ProjectListFormatShort.String(), - ProjectListFormatFullPath.String(), - ProjectListFormatURL.String(), - ProjectListFormatRelPath.String(), - } -} - -// Formatter will get a formatter to print list. -func (f ProjectListFormat) Formatter() (ProjectListFormatter, error) { - switch f { - case ProjectListFormatRelPath: - return RelPathFormatter(), nil - case ProjectListFormatFullPath: - return FullPathFormatter(), nil - case ProjectListFormatURL: - return URLFormatter(), nil - case ProjectListFormatShort: - return ShortFormatter(), nil - } - return nil, fmt.Errorf("%q is invalid project format", f) + format(io.Writer, *Project) error } // ShortFormatter prints each project as short as possible. func ShortFormatter() ProjectListFormatter { return &shortListFormatter{ - dups: map[string]bool{}, + dups: map[string]bool{}, + simpleCollector: &simpleCollector{}, } } @@ -77,7 +39,7 @@ func RelPathFormatter() ProjectListFormatter { type shortListFormatter struct { // mark duplicated subpath dups map[string]bool - list []*Project + *simpleCollector } func (f *shortListFormatter) Add(r *Project) { @@ -85,7 +47,7 @@ func (f *shortListFormatter) Add(r *Project) { // (false, not ok) -> (false, ok) -> (true, ok) -> (true, ok) and so on _, f.dups[p] = f.dups[p] } - f.list = append(f.list, r) + f.simpleCollector.Add(r) } func (f *shortListFormatter) Len() int { @@ -94,13 +56,21 @@ func (f *shortListFormatter) Len() int { func (f *shortListFormatter) PrintAll(w io.Writer, sep string) error { for _, project := range f.list { - if _, err := fmt.Fprint(w, f.shortName(project)+sep); err != nil { + if err := f.format(w, project); err != nil { + return err + } + if _, err := fmt.Fprint(w, sep); err != nil { return err } } return nil } +func (f *shortListFormatter) format(w io.Writer, project *Project) error { + _, err := fmt.Fprint(w, f.shortName(project)) + return err +} + func (f *shortListFormatter) shortName(r *Project) string { for _, p := range r.Subpaths() { if f.dups[p] { @@ -129,35 +99,59 @@ type fullPathFormatter struct { func (f *fullPathFormatter) PrintAll(w io.Writer, sep string) error { for _, project := range f.list { - if _, err := fmt.Fprint(w, project.FullPath+sep); err != nil { + if err := f.format(w, project); err != nil { + return err + } + if _, err := fmt.Fprint(w, sep); err != nil { return err } } return nil } +func (f *fullPathFormatter) format(w io.Writer, project *Project) error { + _, err := fmt.Fprint(w, project.FullPath) + return err +} + type urlFormatter struct { *simpleCollector } func (f *urlFormatter) PrintAll(w io.Writer, sep string) error { for _, project := range f.list { - if _, err := fmt.Fprint(w, "https://"+project.RelPath+sep); err != nil { + if err := f.format(w, project); err != nil { + return err + } + if _, err := fmt.Fprint(w, sep); err != nil { return err } } return nil } +func (f *urlFormatter) format(w io.Writer, project *Project) error { + _, err := fmt.Fprint(w, "https://"+project.RelPath) + return err +} + type relPathFormatter struct { *simpleCollector } func (f *relPathFormatter) PrintAll(w io.Writer, sep string) error { for _, project := range f.list { - if _, err := fmt.Fprint(w, project.RelPath+sep); err != nil { + if err := f.format(w, project); err != nil { + return err + } + if _, err := fmt.Fprint(w, sep); err != nil { return err } } return nil } + +func (f *relPathFormatter) format(w io.Writer, project *Project) error { + _, err := fmt.Fprint(w, project.RelPath) + return err +} diff --git a/gogh/formatter_test.go b/gogh/formatter_test.go index f711b620..298e95c2 100644 --- a/gogh/formatter_test.go +++ b/gogh/formatter_test.go @@ -15,8 +15,12 @@ func TestFormatter(t *testing.T) { t.Run("dry run formatters", func(t *testing.T) { project, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/gogh") require.NoError(t, err) - for _, f := range ProjectListFormats() { - formatter, err := ProjectListFormat(f).Formatter() + for _, formatter := range []ProjectListFormatter{ + ShortFormatter(), + URLFormatter(), + FullPathFormatter(), + RelPathFormatter(), + } { require.NoError(t, err) formatter.Add(project) require.NoError(t, formatter.PrintAll(ioutil.Discard, "\n")) @@ -28,7 +32,7 @@ func TestFormatter(t *testing.T) { require.NoError(t, err) project2, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/bar") require.NoError(t, err) - formatter, err := ProjectListFormatRelPath.Formatter() + formatter := RelPathFormatter() require.NoError(t, err) formatter.Add(project1) formatter.Add(project2) @@ -40,7 +44,7 @@ func TestFormatter(t *testing.T) { t.Run("writer error by rel path formatter", func(t *testing.T) { project, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/foo") require.NoError(t, err) - formatter, err := ProjectListFormatRelPath.Formatter() + formatter := RelPathFormatter() require.NoError(t, err) formatter.Add(project) require.EqualError(t, formatter.PrintAll(testutil.DefaultErrorWriter, ""), "error writer") @@ -51,7 +55,7 @@ func TestFormatter(t *testing.T) { require.NoError(t, err) project2, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/bar") require.NoError(t, err) - formatter, err := ProjectListFormatFullPath.Formatter() + formatter := FullPathFormatter() require.NoError(t, err) formatter.Add(project1) formatter.Add(project2) @@ -60,7 +64,7 @@ func TestFormatter(t *testing.T) { t.Run("writer error by full path formatter", func(t *testing.T) { project, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/foo") require.NoError(t, err) - formatter, err := ProjectListFormatFullPath.Formatter() + formatter := FullPathFormatter() require.NoError(t, err) formatter.Add(project) require.EqualError(t, formatter.PrintAll(testutil.DefaultErrorWriter, ""), "error writer") @@ -71,7 +75,7 @@ func TestFormatter(t *testing.T) { require.NoError(t, err) project2, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/bar") require.NoError(t, err) - formatter, err := ProjectListFormatURL.Formatter() + formatter := URLFormatter() require.NoError(t, err) formatter.Add(project1) formatter.Add(project2) @@ -83,7 +87,7 @@ func TestFormatter(t *testing.T) { t.Run("writer error by url formatter", func(t *testing.T) { project, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/foo") require.NoError(t, err) - formatter, err := ProjectListFormatURL.Formatter() + formatter := URLFormatter() require.NoError(t, err) formatter.Add(project) require.EqualError(t, formatter.PrintAll(testutil.DefaultErrorWriter, ""), "error writer") @@ -102,7 +106,7 @@ func TestFormatter(t *testing.T) { require.NoError(t, err) project6, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/foo", "/foo/github.com/kyoh86/baz") require.NoError(t, err) - formatter, err := ProjectListFormatShort.Formatter() + formatter := ShortFormatter() require.NoError(t, err) formatter.Add(project1) formatter.Add(project2) @@ -118,15 +122,10 @@ func TestFormatter(t *testing.T) { t.Run("writer error by short formatter", func(t *testing.T) { project, err := parseProject(&context.MockContext{MGitHubHost: "github.com"}, "/go/src", "/go/src/github.com/kyoh86/foo") require.NoError(t, err) - formatter, err := ProjectListFormatShort.Formatter() + formatter := ShortFormatter() require.NoError(t, err) formatter.Add(project) require.EqualError(t, formatter.PrintAll(testutil.DefaultErrorWriter, ""), "error writer") }) - t.Run("invalid formatter", func(t *testing.T) { - _, err := ProjectListFormat("dummy").Formatter() - assert.Errorf(t, err, "%q is invalid project format", "dummy") - }) - } diff --git a/gogh/repo_test.go b/gogh/repo_test.go index 06ffa191..f78f3ea3 100644 --- a/gogh/repo_test.go +++ b/gogh/repo_test.go @@ -162,6 +162,7 @@ func TestCheckRepoHost(t *testing.T) { func TestRepoIsPublic(t *testing.T) { t.Run("public repo", func(t *testing.T) { + t.Skip("this test requires network connection...") r, err := ParseRepo("kyoh86/gogh") require.NoError(t, err) @@ -172,6 +173,7 @@ func TestRepoIsPublic(t *testing.T) { }) t.Run("private repo", func(t *testing.T) { + t.Skip("this test requires network connection...") r, err := ParseRepo("kyoh86/unknown") require.NoError(t, err) diff --git a/main.go b/main.go index 0933e189..89a358ee 100644 --- a/main.go +++ b/main.go @@ -232,19 +232,19 @@ func where(app *kingpin.Application) (string, func() error) { func list(app *kingpin.Application) (string, func() error) { var ( - format string + format command.ProjectListFormat primary bool isPublic bool query string ) cmd := app.Command("list", "List projects (local repositories)").Alias("ls") - cmd.Flag("format", "Format of each repository").Short('f').Default(gogh.ProjectListFormatRelPath.String()).EnumVar(&format, gogh.ProjectListFormats()...) + cmd.Flag("format", "Format of each repository").Short('f').Default(command.ProjectListFormatLabelRelPath).SetValue(&format) cmd.Flag("primary", "Only in primary root directory").Short('p').BoolVar(&primary) cmd.Flag("public", "Only projects which are referred to public repositories").BoolVar(&isPublic) cmd.Arg("query", "Project name query").StringVar(&query) return mainutil.WrapCommand(cmd, func(ctx gogh.Context) error { - return command.List(ctx, gogh.ProjectListFormat(format), primary, isPublic, query) + return command.List(ctx, format.Formatter(), primary, isPublic, query) }) } @@ -260,7 +260,7 @@ func dump(app *kingpin.Application) (string, func() error) { cmd.Arg("query", "Project name query").StringVar(&query) return mainutil.WrapCommand(cmd, func(ctx gogh.Context) error { - return command.List(ctx, gogh.ProjectListFormatURL, primary, isPublic, query) + return command.List(ctx, gogh.URLFormatter(), primary, isPublic, query) }) }