зеркало из https://github.com/Azure/buffalo-azure.git
Adding ImportBag type (#72)
* Adding ImportBag type * Fixing formatting * Updating default from unknown to the path base. * Removing unused helper function
This commit is contained in:
Родитель
853f6ecd1d
Коммит
0d9fee5e94
|
@ -0,0 +1,234 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PackageSpecifier is a string that represents the name that will be used to refer to
|
||||
// exported functions and variables from a package.
|
||||
type PackageSpecifier string
|
||||
|
||||
// PackagePath is a string that refers to the location of Go package.
|
||||
type PackagePath string
|
||||
|
||||
// ImportBag captures all of the imports in a Go source file, and attempts
|
||||
// to ease the process of working with them.
|
||||
type ImportBag struct {
|
||||
bySpec map[PackageSpecifier]PackagePath
|
||||
blankIdent map[PackagePath]struct{}
|
||||
localIdent map[PackagePath]struct{}
|
||||
}
|
||||
|
||||
// NewImportBag instantiates an empty ImportBag.
|
||||
func NewImportBag() *ImportBag {
|
||||
return &ImportBag{
|
||||
bySpec: make(map[PackageSpecifier]PackagePath),
|
||||
blankIdent: make(map[PackagePath]struct{}),
|
||||
localIdent: make(map[PackagePath]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewImportBagFromFile reads a Go source file, finds all imports,
|
||||
// and returns them as an instantiated ImportBag.
|
||||
func NewImportBagFromFile(filepath string) (*ImportBag, error) {
|
||||
f, err := parser.ParseFile(token.NewFileSet(), filepath, nil, parser.ImportsOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ib := NewImportBag()
|
||||
|
||||
for _, spec := range f.Imports {
|
||||
pkgPath := PackagePath(strings.Trim(spec.Path.Value, `"`))
|
||||
if spec.Name == nil {
|
||||
ib.AddImport(pkgPath)
|
||||
} else {
|
||||
ib.AddImportWithSpecifier(pkgPath, PackageSpecifier(spec.Name.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return ib, nil
|
||||
}
|
||||
|
||||
// AddImport includes a package, and returns the name that was selected to
|
||||
// be the specified for working with this path. It first attempts to use the
|
||||
// package name as the specifier. Should that cause a conflict, it determines
|
||||
// a unique name to be used as the specifier.
|
||||
//
|
||||
// If the path provided has already been imported, the existing name for it
|
||||
// is returned, but err is non-nil.
|
||||
func (ib *ImportBag) AddImport(pkgPath PackagePath) PackageSpecifier {
|
||||
spec, err := FindSpecifier(pkgPath)
|
||||
if err != nil {
|
||||
spec = PackageSpecifier(path.Base(string(pkgPath)))
|
||||
}
|
||||
specLen := len(spec)
|
||||
suffix := uint(1)
|
||||
for {
|
||||
err = ib.AddImportWithSpecifier(pkgPath, spec)
|
||||
if err == nil {
|
||||
break
|
||||
} else if err != ErrDuplicateImport {
|
||||
panic(err)
|
||||
}
|
||||
spec = PackageSpecifier(fmt.Sprintf("%s%d", spec[:specLen], suffix))
|
||||
suffix++
|
||||
}
|
||||
|
||||
return spec
|
||||
}
|
||||
|
||||
// ErrDuplicateImport is the error that will be returned when two packages are both requested
|
||||
// to be imported using the same specifier.
|
||||
var ErrDuplicateImport = errors.New("specifier already in use in ImportBag")
|
||||
|
||||
// ErrMultipleLocalImport is the error that will be returned when the same package has been imported
|
||||
// to the specifer "." more than once.
|
||||
var ErrMultipleLocalImport = errors.New("package already imported into the local namespace")
|
||||
|
||||
// AddImportWithSpecifier will add an import with a given name. If it would lead
|
||||
// to conflicting package specifiers, it returns an error.
|
||||
func (ib *ImportBag) AddImportWithSpecifier(pkgPath PackagePath, specifier PackageSpecifier) error {
|
||||
if specifier == "_" {
|
||||
ib.blankIdent[pkgPath] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
if specifier == "." {
|
||||
if _, ok := ib.localIdent[pkgPath]; ok {
|
||||
return ErrMultipleLocalImport
|
||||
}
|
||||
ib.localIdent[pkgPath] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
if impPath, ok := ib.bySpec[specifier]; ok && pkgPath != impPath {
|
||||
return ErrDuplicateImport
|
||||
}
|
||||
|
||||
ib.bySpec[specifier] = pkgPath
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindSpecifier finds the specifier assocatied with a particular package.
|
||||
//
|
||||
// If the package was not imported, the empty string and false are returned.
|
||||
//
|
||||
// If multiple specifiers are assigned to the package, one is returned at
|
||||
// random.
|
||||
//
|
||||
// If the same package is imported with a named specifier, and the blank
|
||||
// identifier, the name is returned.
|
||||
func (ib ImportBag) FindSpecifier(pkgPath PackagePath) (PackageSpecifier, bool) {
|
||||
for k, v := range ib.bySpec {
|
||||
if v == pkgPath {
|
||||
return k, true
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := ib.blankIdent[pkgPath]; ok {
|
||||
return "_", true
|
||||
}
|
||||
|
||||
if _, ok := ib.localIdent[pkgPath]; ok {
|
||||
return ".", true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// List returns each import statement as a slice of strings sorted alphabetically by
|
||||
// their import paths.
|
||||
func (ib *ImportBag) List() []string {
|
||||
specs := ib.ListAsImportSpec()
|
||||
retval := make([]string, len(specs))
|
||||
|
||||
builder := bytes.NewBuffer([]byte{})
|
||||
|
||||
for i, s := range specs {
|
||||
if s.Name != nil {
|
||||
builder.WriteString(s.Name.Name)
|
||||
builder.WriteRune(' ')
|
||||
}
|
||||
builder.WriteString(s.Path.Value)
|
||||
retval[i] = builder.String()
|
||||
builder.Reset()
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
// ListAsImportSpec returns the imports from the ImportBag as a slice of ImportSpecs
|
||||
// sorted alphabetically by their import paths.
|
||||
func (ib *ImportBag) ListAsImportSpec() []*ast.ImportSpec {
|
||||
retval := make([]*ast.ImportSpec, 0, len(ib.bySpec)+len(ib.localIdent)+len(ib.blankIdent))
|
||||
|
||||
getLit := func(pkgPath PackagePath) *ast.BasicLit {
|
||||
return &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: fmt.Sprintf("%q", string(pkgPath)),
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range ib.bySpec {
|
||||
var name *ast.Ident
|
||||
|
||||
if path.Base(string(v)) != string(k) {
|
||||
name = ast.NewIdent(string(k))
|
||||
}
|
||||
|
||||
retval = append(retval, &ast.ImportSpec{
|
||||
Name: name,
|
||||
Path: getLit(v),
|
||||
})
|
||||
}
|
||||
|
||||
for s := range ib.localIdent {
|
||||
retval = append(retval, &ast.ImportSpec{
|
||||
Name: ast.NewIdent("."),
|
||||
Path: getLit(s),
|
||||
})
|
||||
}
|
||||
|
||||
for s := range ib.blankIdent {
|
||||
retval = append(retval, &ast.ImportSpec{
|
||||
Name: ast.NewIdent("_"),
|
||||
Path: getLit(s),
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(retval, func(i, j int) bool {
|
||||
return strings.Compare(retval[i].Path.Value, retval[j].Path.Value) < 0
|
||||
})
|
||||
|
||||
return retval
|
||||
}
|
||||
|
||||
var impFinder = importer.Default()
|
||||
|
||||
// FindSpecifier finds the name of a package by loading it in from GOPATH
|
||||
// or a vendor folder.
|
||||
func FindSpecifier(pkgPath PackagePath) (PackageSpecifier, error) {
|
||||
var pkg *types.Package
|
||||
var err error
|
||||
|
||||
if cast, ok := impFinder.(types.ImporterFrom); ok {
|
||||
pkg, err = cast.ImportFrom(string(pkgPath), "", 0)
|
||||
} else {
|
||||
pkg, err = impFinder.Import(string(pkgPath))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return PackageSpecifier(pkg.Name()), nil
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package common_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/Azure/buffalo-azure/generators/common"
|
||||
)
|
||||
|
||||
func TestImportBag_AddImport_Unique(t *testing.T) {
|
||||
testCases := [][]PackagePath{
|
||||
{
|
||||
"github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid",
|
||||
},
|
||||
{
|
||||
"github.com/Azure/buffalo-azure/generators/common",
|
||||
"github.com/Azure/buffalo-azure/sdk/eventgrid",
|
||||
},
|
||||
{
|
||||
"fmt",
|
||||
"github.com/Azure/buffalo-azure/generators/common",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
|
||||
seen := map[PackageSpecifier]struct{}{}
|
||||
subject := NewImportBag()
|
||||
|
||||
for _, pkgPath := range tc {
|
||||
spec := subject.AddImport(pkgPath)
|
||||
if _, ok := seen[spec]; ok {
|
||||
t.Logf("duplicate spec returned")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportBag_List(t *testing.T) {
|
||||
testCases := []struct {
|
||||
inputs []PackagePath
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
inputs: []PackagePath{
|
||||
"github.com/Azure/buffalo-azure/generators/common",
|
||||
"github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid",
|
||||
},
|
||||
expected: []string{
|
||||
`"github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid"`,
|
||||
`"github.com/Azure/buffalo-azure/generators/common"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: []PackagePath{
|
||||
"github.com/Azure/buffalo-azure/sdk/eventgrid",
|
||||
"github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid",
|
||||
},
|
||||
expected: []string{
|
||||
`eventgrid1 "github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid"`,
|
||||
`"github.com/Azure/buffalo-azure/sdk/eventgrid"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: []PackagePath{
|
||||
"github.com/Azure/buffalo-azure/sdk/eventgrid",
|
||||
"github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid",
|
||||
"github.com/Azure/buffalo-azure/generators/eventgrid",
|
||||
},
|
||||
expected: []string{
|
||||
`eventgrid1 "github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid"`,
|
||||
`eventgrid2 "github.com/Azure/buffalo-azure/generators/eventgrid"`,
|
||||
`"github.com/Azure/buffalo-azure/sdk/eventgrid"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
inputs: []PackagePath{
|
||||
"github.com/Azure/buffalo-azure/sdk/eventgrid",
|
||||
"github.com/Azure/buffalo-azure/sdk/eventgrid",
|
||||
},
|
||||
expected: []string{
|
||||
`"github.com/Azure/buffalo-azure/sdk/eventgrid"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
subject := NewImportBag()
|
||||
for _, i := range tc.inputs {
|
||||
subject.AddImport(i)
|
||||
}
|
||||
|
||||
got := subject.List()
|
||||
|
||||
if len(got) != len(tc.expected) {
|
||||
t.Logf("got: %d imports want: %d imports", len(got), len(tc.expected))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
for i, val := range tc.expected {
|
||||
if got[i] != val {
|
||||
t.Logf("at %d\n\tgot: %s\n\twant: %s", i, got[i], val)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportBag_NewImportBagFromFile(t *testing.T) {
|
||||
testCases := []struct {
|
||||
inputFile string
|
||||
expected []string
|
||||
}{
|
||||
{"./testdata/main.go", []string{`"fmt"`, `_ "github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid"`}},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
subject, err := NewImportBagFromFile(tc.inputFile)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
got := subject.List()
|
||||
|
||||
for i, imp := range tc.expected {
|
||||
if imp != got[i] {
|
||||
t.Logf("at: %d\n\tgot: %s\n\twant: %s", i, got[i], imp)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
if len(got) != len(tc.expected) {
|
||||
t.Logf("got: %d imports want: %d imports", len(got), len(tc.expected))
|
||||
t.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, world!!!")
|
||||
}
|
|
@ -11,6 +11,8 @@ import (
|
|||
"github.com/gobuffalo/buffalo/meta"
|
||||
"github.com/gobuffalo/makr"
|
||||
"github.com/markbates/inflect"
|
||||
|
||||
"github.com/Azure/buffalo-azure/generators/common"
|
||||
)
|
||||
|
||||
//go:generate go run ./builder/builder.go -o ./static_templates.go ./templates
|
||||
|
@ -26,15 +28,24 @@ func (eg *Generator) Run(app meta.App, name string, types map[string]reflect.Typ
|
|||
type TypeMapping struct {
|
||||
Identifier string
|
||||
inflect.Name
|
||||
PackageSpecifier string
|
||||
PkgPath string
|
||||
PkgSpec common.PackageSpecifier
|
||||
}
|
||||
flatTypes := make([]TypeMapping, 0, len(types))
|
||||
|
||||
ib := common.NewImportBag()
|
||||
ib.AddImport("encoding/json")
|
||||
ib.AddImport("errors")
|
||||
ib.AddImport("net/http")
|
||||
ib.AddImportWithSpecifier("github.com/Azure/buffalo-azure/sdk/eventgrid", "eg")
|
||||
ib.AddImport("github.com/gobuffalo/buffalo")
|
||||
|
||||
for i, n := range types {
|
||||
flatTypes = append(flatTypes, TypeMapping{
|
||||
Identifier: i,
|
||||
PackageSpecifier: path.Base(n.PkgPath()),
|
||||
Name: inflect.Name(n.Name()),
|
||||
Identifier: i,
|
||||
PkgPath: n.PkgPath(),
|
||||
PkgSpec: ib.AddImport(common.PackagePath(n.PkgPath())),
|
||||
Name: inflect.Name(n.Name()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -60,6 +71,7 @@ func (eg *Generator) Run(app meta.App, name string, types map[string]reflect.Typ
|
|||
d := make(makr.Data)
|
||||
d["name"] = iName
|
||||
d["types"] = flatTypes
|
||||
d["imports"] = ib.List()
|
||||
|
||||
return g.Run(app.Root, d)
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,22 +1,18 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/buffalo-azure/sdk/eventgrid"
|
||||
"github.com/gobuffalo/buffalo"
|
||||
{{ range $i := .imports }} {{$i}}
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
// My{{$.name.Camel}}Subscriber gathers responds to all Requests sent to a particular endpoint.
|
||||
type {{$.name.Camel}}Subscriber struct {
|
||||
eventgrid.Subscriber
|
||||
eg.Subscriber
|
||||
}
|
||||
|
||||
// New{{$.name.Camel}}Subscriber instantiates {{$.name.Camel}}Subscriber for use in a `buffalo.App`.
|
||||
func New{{$.name.Camel}}Subscriber(parent eventgrid.Subscriber) (created *{{$.name.Camel}}Subscriber) {
|
||||
dispatcher := eventgrid.NewTypeDispatchSubscriber(parent)
|
||||
func New{{$.name.Camel}}Subscriber(parent eg.Subscriber) (created *{{$.name.Camel}}Subscriber) {
|
||||
dispatcher := eg.NewTypeDispatchSubscriber(parent)
|
||||
|
||||
created = &{{$.name.Camel}}Subscriber{
|
||||
Subscriber: dispatcher,
|
||||
|
@ -25,15 +21,15 @@ func New{{$.name.Camel}}Subscriber(parent eventgrid.Subscriber) (created *{{$.na
|
|||
{{ range $t := .types}}
|
||||
dispatcher.Bind("{{$t.Identifier}}", created.Receive{{$t.Name.Camel}})
|
||||
{{end}}
|
||||
dispatcher.Bind(eventgrid.EventTypeWildcard, created.ReceiveDefault)
|
||||
dispatcher.Bind(eg.EventTypeWildcard, created.ReceiveDefault)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
{{ range $t := .types }}
|
||||
// Receive{{$t.Name.Camel}} will respond to an `eventgrid.Event` carrying a serialized `{{$t.Name.Camel}}` as its payload.
|
||||
func (s *{{$.name.Camel}}Subscriber) Receive{{$t.Name.Camel}}(c buffalo.Context, e eventgrid.Event) error {
|
||||
var payload {{$t.PackageSpecifier}}.{{$t.Name.Camel}}
|
||||
func (s *{{$.name.Camel}}Subscriber) Receive{{$t.Name.Camel}}(c buffalo.Context, e eg.Event) error {
|
||||
var payload {{$t.PkgSpec}}.{{$t.Name.Camel}}
|
||||
if err := json.Unmarshal(e.Data, &payload); err != nil {
|
||||
return c.Error(http.StatusBadRequest, errors.New("unable to unmarshal request data"))
|
||||
}
|
||||
|
@ -44,6 +40,6 @@ func (s *{{$.name.Camel}}Subscriber) Receive{{$t.Name.Camel}}(c buffalo.Context,
|
|||
{{end}}
|
||||
|
||||
// ReceiveDefault will respond to an `eventgrid.Event` carrying any EventType as its payload.
|
||||
func (s *{{$.name.Camel}}Subscriber) ReceiveDefault(c buffalo.Context, e eventgrid.Event) error {
|
||||
func (s *{{$.name.Camel}}Subscriber) ReceiveDefault(c buffalo.Context, e eg.Event) error {
|
||||
return c.Error(http.StatusInternalServerError, errors.New("not implemented"))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
egdp "github.com/Azure/azure-sdk-for-go/services/eventgrid/2018-01-01/eventgrid"
|
||||
"github.com/Azure/buffalo-azure/sdk/eventgrid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%#v\n", eventgrid.TypeDispatchSubscriber{})
|
||||
fmt.Printf("%#v\n", egdp.StorageBlobCreatedEventData{})
|
||||
}
|
Загрузка…
Ссылка в новой задаче