-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: #48
- Loading branch information
Showing
10 changed files
with
319 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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:<advanced format>', 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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) | ||
}) | ||
} |
Oops, something went wrong.