Implement creation of svcdef in truss

This commit is contained in:
Leland Batey 2016-10-23 11:18:11 -07:00
Родитель cb84ced170
Коммит dc580b847d
5 изменённых файлов: 107 добавлений и 63 удалений

Просмотреть файл

@ -19,7 +19,7 @@ type RuneReader struct {
func (self *RuneReader) ReadRune() (rune, error) {
var toret rune = 0
var err error = nil
var err error
if self.RunePos < self.ContentLen {
toret = self.Contents[self.RunePos]
@ -306,7 +306,7 @@ func (self *SvcScanner) FastForward() error {
// errors will be returned.
func (self *SvcScanner) ReadUnit() ([]rune, error) {
var rv []rune
var err error = nil
var err error
if self.UnitPos < len(self.Buf) {
unit := self.Buf[self.UnitPos]

Просмотреть файл

@ -18,6 +18,7 @@ import (
"github.com/TuneLab/go-truss/deftree"
"github.com/TuneLab/go-truss/gendoc"
"github.com/TuneLab/go-truss/gengokit"
"github.com/TuneLab/go-truss/svcdef"
)
var (
@ -43,7 +44,8 @@ func main() {
cfg, err := parseInput()
exitIfError(errors.Wrap(err, "cannot parse input"))
dt, err := parseServiceDefinition(cfg.DefPaths)
//dt, sd, err := parseServiceDefinition(cfg)
dt, _, err := parseServiceDefinition(cfg)
exitIfError(errors.Wrap(err, "cannot parse input definition proto files"))
genFiles, err := generateCode(cfg, dt)
@ -82,7 +84,7 @@ func parseInput() (*truss.Config, error) {
if err != nil {
return nil, errors.Wrapf(err, "Could not open package file %q", cfg.DefPaths[0])
}
svcName, err := parsepkgname.PackageNameFromFile(defFile)
svcName, err := parsepkgname.FromReader(defFile)
if err != nil {
return nil, errors.Wrapf(err, "cannot parse package name from file %q", cfg.DefPaths[0])
}
@ -113,41 +115,82 @@ func parseInput() (*truss.Config, error) {
return &cfg, nil
}
// parseServiceDefinition returns a deftree which contains all needed for all
// generating a truss service and documentation
func parseServiceDefinition(definitionPaths []string) (deftree.Deftree, error) {
protocOut, err := execprotoc.CodeGeneratorRequest(definitionPaths)
if err != nil {
return nil, errors.Wrap(err, "cannot use parse input files with protoc")
// parseServiceDefinition returns a deftree which contains all necessary
// information for generating a truss service and its documentation.
func parseServiceDefinition(cfg *truss.Config) (deftree.Deftree, *svcdef.Svcdef, error) {
svcPath := cfg.ServicePath()
protoDefPaths := cfg.DefPaths
// Create the ServicePath so the .pb.go files may be place within it
if cfg.PrevGen == nil {
err := os.Mkdir(svcPath, 0777)
if err != nil {
return nil, nil, errors.Wrap(err, "cannot create service directory")
}
}
svcFile, err := execprotoc.ServiceFile(protocOut, filepath.Dir(definitionPaths[0]))
err := execprotoc.GeneratePBDotGo(cfg.DefPaths, svcPath, cfg.PBPath())
if err != nil {
return nil, errors.Wrap(err, "cannot find service definition file")
return nil, nil, errors.Wrap(err, "cannot create .pb.go files")
}
// Open all .pb.go files and store in slice to be passed to svcdef.New()
//var openFiles func([]string) ([]io.Reader, error)
openFiles := func(paths []string) ([]io.Reader, error) {
rv := []io.Reader{}
for _, p := range paths {
reader, err := os.Open(p)
if err != nil {
return nil, errors.Wrapf(err, "couldn't open file %q", p)
}
rv = append(rv, reader)
}
return rv, nil
}
// Get path names of .pb.go files
pbgoPaths := []string{}
for _, p := range protoDefPaths {
base := filepath.Base(p)
barename := strings.TrimSuffix(base, filepath.Ext(p))
pbgp := filepath.Join(cfg.PBPath(), barename+".pb.go")
pbgoPaths = append(pbgoPaths, pbgp)
}
pbgoFiles, err := openFiles(pbgoPaths)
if err != nil {
return nil, nil, errors.Wrap(err, "Failed to open a .pb.go file")
}
pbFiles, err := openFiles(protoDefPaths)
if err != nil {
return nil, nil, errors.Wrap(err, "Failed to open a .proto file")
}
// Create the svcdef
sd, err := svcdef.New(pbgoFiles, pbFiles)
if err != nil {
return nil, nil, errors.Wrap(err, "Failed to create svcdef")
}
// Create the Deftree
protocOut, err := execprotoc.CodeGeneratorRequest(protoDefPaths)
if err != nil {
return nil, nil, errors.Wrap(err, "cannot parse input files with protoc")
}
svcFile, err := execprotoc.ServiceFile(protocOut, filepath.Dir(protoDefPaths[0]))
if err != nil {
return nil, nil, errors.Wrap(err, "cannot find service definition file")
}
dt, err := deftree.New(protocOut, svcFile)
if err != nil {
return nil, errors.Wrap(err, "cannot to construct service definition")
return nil, nil, errors.Wrap(err, "cannot to construct service definition")
}
return dt, nil
return dt, sd, nil
}
// generateCode returns a []truss.NamedReadWriter that represents a gokit
// service with documentation
func generateCode(cfg *truss.Config, dt deftree.Deftree) ([]truss.NamedReadWriter, error) {
if cfg.PrevGen == nil {
err := os.Mkdir(cfg.ServicePath(), 0777)
if err != nil {
return nil, errors.Wrap(err, "cannot create service directory")
}
}
err := execprotoc.GeneratePBDotGo(cfg.DefPaths, cfg.ServicePath(), cfg.PBPath())
if err != nil {
return nil, errors.Wrap(err, "cannot create .pb.go files")
}
genGokitFiles, err := gengokit.GenerateGokit(dt, cfg.ServicePackage, cfg.PBPackage, cfg.PrevGen)
if err != nil {

Просмотреть файл

@ -20,13 +20,13 @@ import (
"github.com/TuneLab/go-truss/deftree/svcparse"
)
type Token int
type token int
const (
IDENT Token = iota
WHITESPACE
COMMENT
OTHER
ident token = iota
whitespaceToken
comment
other
)
type Scanner interface {
@ -42,68 +42,70 @@ type Scanner interface {
ReadUnit() ([]rune, error)
}
func categorize(unit []rune) Token {
rv := OTHER
func categorize(unit []rune) token {
rv := other
r := unit[0]
switch {
case unicode.IsLetter(r):
rv = IDENT
rv = ident
case unicode.IsDigit(r):
rv = IDENT
rv = ident
case r == '_':
rv = IDENT
rv = ident
case unicode.IsSpace(r):
rv = WHITESPACE
rv = whitespaceToken
case r == '/' && len(unit) > 1:
rv = COMMENT
rv = comment
}
return rv
}
// PackageNameFromFile accepts an io.Reader, the contents of which should be a
// valid proto3 file, and returns the name of the protobuf package for that
// file.
func PackageNameFromFile(protofile io.Reader) (string, error) {
// FromReader accepts an io.Reader, the contents of which should be a
// valid proto3 file, and returns the name of the protobuf package which that
// file belongs to.
func FromReader(protofile io.Reader) (string, error) {
scanner := svcparse.NewSvcScanner(protofile)
return GetPackageName(scanner)
return FromScanner(scanner)
}
// GetPackageName accepts a Scanner for a protobuf file and returns the name of
// the protobuf package that the file lives within.
func GetPackageName(scanner Scanner) (string, error) {
// FromScanner accepts a Scanner for a protobuf file and returns the name of
// the protobuf package that the file belongs to.
func FromScanner(scanner Scanner) (string, error) {
foundpackage := false
// A nice way to ignore comments. Recursively calls itself until it
// recieves a unit from the scanner which is not a comment.
var readIgnoreComment func(Scanner) (Token, []rune, error)
readIgnoreComment = func(scn Scanner) (Token, []rune, error) {
unit, err := scanner.ReadUnit()
var readIgnoreComment func(Scanner) (token, []rune, error)
readIgnoreComment = func(scn Scanner) (token, []rune, error) {
unit, err := scn.ReadUnit()
if err != nil {
return OTHER, nil, err
return other, nil, err
}
tkn := categorize(unit)
if tkn == COMMENT {
if tkn == comment {
return readIgnoreComment(scn)
}
return tkn, unit, err
}
// A tiny state machine to find two sequential idents: the ident "package"
// and the ident immediately following. That second ident will be the name
// of the package.
for {
tkn, unit, err := readIgnoreComment(scanner)
// Err may only be io.EOF
if err != nil {
return "", err
}
if foundpackage {
if tkn == IDENT {
if tkn == ident {
return string(unit), nil
} else if tkn == WHITESPACE {
} else if tkn == whitespaceToken {
continue
} else {
foundpackage = false
}
} else {
if tkn == IDENT && string(unit) == "package" {
if tkn == ident && string(unit) == "package" {
foundpackage = true
}
}

Просмотреть файл

@ -16,9 +16,8 @@ func (t *testScanner) ReadUnit() ([]rune, error) {
rv := t.contents[t.position]
t.position += 1
return rv, nil
} else {
return nil, io.EOF
}
return nil, io.EOF
}
func NewTestScanner(units []string) *testScanner {
@ -29,7 +28,7 @@ func NewTestScanner(units []string) *testScanner {
return &rv
}
func TestGetPackageName_simple(t *testing.T) {
func TestFromScanner_simple(t *testing.T) {
basicContents := []string{
"\n",
"package",
@ -40,7 +39,7 @@ func TestGetPackageName_simple(t *testing.T) {
}
scn := NewTestScanner(basicContents)
want := "examplename"
got, err := GetPackageName(scn)
got, err := FromScanner(scn)
if err != nil {
t.Fatal(err)
}
@ -49,7 +48,7 @@ func TestGetPackageName_simple(t *testing.T) {
}
}
func TestGetPackageName_mid_comment(t *testing.T) {
func TestFromScanner_mid_comment(t *testing.T) {
contents := []string{
"\n",
"package",
@ -61,7 +60,7 @@ func TestGetPackageName_mid_comment(t *testing.T) {
}
scn := NewTestScanner(contents)
want := "examplename"
got, err := GetPackageName(scn)
got, err := FromScanner(scn)
if err != nil {
t.Fatal(err)
}
@ -70,14 +69,14 @@ func TestGetPackageName_mid_comment(t *testing.T) {
}
}
func TestPackageNameFromFile(t *testing.T) {
func TestFromReader(t *testing.T) {
code := `
// A comment about this proto file
package /* some mid-definition comment */ examplepackage;
// and the rest of the file goes here
`
name, err := PackageNameFromFile(strings.NewReader(code))
name, err := FromReader(strings.NewReader(code))
if err != nil {
t.Fatal(err)
}

Просмотреть файл

@ -27,7 +27,7 @@ func (c *Config) ServicePath() string {
return goSvcPath
}
// PBPath returns the full paht to Config.PBPackage
// PBPath returns the full path to Config.PBPackage
func (c *Config) PBPath() string {
pbPath := filepath.Join(c.GOPATH, "src", c.PBPackage)