Skip to content

Commit

Permalink
Adding "func" types (#10)
Browse files Browse the repository at this point in the history
The `type` now allows functions, such as "func () bool". The `returns` can be used to return such a function and the contents can contain references to other services like all other Go expressions.
  • Loading branch information
elliotchance committed Jun 24, 2019
1 parent 4fdd3d9 commit cfd803f
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 18 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ properties:

### returns

The expression used to instantiate the service. You can provide any Go code
here, including referencing other services and environment variables.
The expression used to instantiate the service. You can provide any Go
expression here, including referencing other services and environment variables.

The `returns` can also return a function, since it is an expression. See `type`
for an example.

### scope

Expand All @@ -142,10 +145,21 @@ name that includes the package name if the type does not belong to this package.

Example

```:
```yml
type: '*github.com/go-redis/redis.Options'
```

The `type` may also be a function. Functions can refer to other services in the
same embedded way:

```yml
type: func () bool
returns: |
func () bool {
return @{Something}.IsReady()
}
```

## Using Services

As part of the generated file, `dingo.go`. There will be a module-level variable
Expand Down
32 changes: 23 additions & 9 deletions dingotest/dingo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,33 @@ import (
)

type Container struct {
CustomerWelcome *CustomerWelcome
OtherPkg *go_sub_pkg.Person
OtherPkg2 go_sub_pkg.Greeter
OtherPkg3 *go_sub_pkg.Person
SendEmail EmailSender
SendEmailError *SendEmail
SomeEnv *string
WithEnv1 *SendEmail
WithEnv2 *SendEmail
AFunc func(int, int) (bool, bool)
CustomerWelcome *CustomerWelcome
OtherPkg *go_sub_pkg.Person
OtherPkg2 go_sub_pkg.Greeter
OtherPkg3 *go_sub_pkg.Person
SendEmail EmailSender
SendEmailError *SendEmail
SomeEnv *string
WithEnv1 *SendEmail
WithEnv2 *SendEmail
}

var DefaultContainer = &Container{}

func (container *Container) GetAFunc() func(int, int) (bool, bool) {
if container.AFunc == nil {
service := func(a, b int) (c, d bool) {
c = (a + b) != 0
d = container.GetSomeEnv() != ""

return
}

container.AFunc = service
}
return container.AFunc
}
func (container *Container) GetCustomerWelcome() *CustomerWelcome {
if container.CustomerWelcome == nil {
service := NewCustomerWelcome(container.GetSendEmail())
Expand Down
10 changes: 10 additions & 0 deletions dingotest/dingo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@ services:
type: '*SendEmail'
returns: NewSendEmail()
error: panic(err)

AFunc:
type: func (int, int) (bool, bool)
returns: |
func (a, b int) (c, d bool) {
c = (a + b) != 0
d = @{SomeEnv} != ""
return
}
6 changes: 3 additions & 3 deletions service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (
"testing"
)

var serviceValidationTests = map[string]struct{
var serviceValidationTests = map[string]struct {
service *Service
err error
err error
}{
"empty": {
service: &Service{},
err: nil,
err: nil,
},
"scope_prototype": {
service: &Service{
Expand Down
78 changes: 76 additions & 2 deletions type.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
package main

import (
"fmt"
"regexp"
"strings"
)

type Type string

func (ty Type) String() string {
if ty.IsFunction() {
args, returns := ty.parseFunctionType()
switch len(returns) {
case 0:
return fmt.Sprintf("func (%s)", args)

case 1:
return fmt.Sprintf("func (%s) %s", args, strings.Join(returns, ","))

default:
return fmt.Sprintf("func (%s) (%s)", args,
strings.Join(returns, ","))
}
}

return string(ty)
}

func (ty Type) IsPointer() bool {
return strings.HasPrefix(string(ty), "*")
return strings.HasPrefix(string(ty), "*") || ty.IsFunction()
}

func (ty Type) PackageName() string {
if !strings.Contains(string(ty), ".") {
if ty.IsFunction() || !strings.Contains(string(ty), ".") {
return ""
}

Expand All @@ -25,6 +41,10 @@ func (ty Type) PackageName() string {
}

func (ty Type) UnversionedPackageName() string {
if ty.IsFunction() {
return ""
}

packageName := strings.Split(ty.PackageName(), "/")
if regexp.MustCompile(`^v\d+$`).MatchString(packageName[len(packageName)-1]) {
packageName = packageName[:len(packageName)-1]
Expand All @@ -34,25 +54,41 @@ func (ty Type) UnversionedPackageName() string {
}

func (ty Type) LocalPackageName() string {
if ty.IsFunction() {
return ""
}

pkgNameParts := strings.Split(ty.UnversionedPackageName(), "/")
lastPart := pkgNameParts[len(pkgNameParts)-1]

return strings.Replace(lastPart, "-", "_", -1)
}

func (ty Type) EntityName() string {
if ty.IsFunction() {
return ty.String()
}

parts := strings.Split(string(ty), ".")

return strings.TrimLeft(parts[len(parts)-1], "*")
}

func (ty Type) LocalEntityName() string {
if ty.IsFunction() {
return ty.String()
}

name := ty.LocalPackageName() + "." + ty.EntityName()

return strings.TrimLeft(name, ".")
}

func (ty Type) LocalEntityType() string {
if ty.IsFunction() {
return ty.String()
}

name := ty.LocalEntityName()
if ty.IsPointer() {
name = "*" + name
Expand All @@ -62,6 +98,10 @@ func (ty Type) LocalEntityType() string {
}

func (ty Type) CreateLocalEntityType() string {
if ty.IsFunction() {
return ty.String()
}

name := ty.LocalEntityName()
if ty.IsPointer() {
name = "&" + name
Expand All @@ -71,10 +111,44 @@ func (ty Type) CreateLocalEntityType() string {
}

func (ty Type) LocalEntityPointerType() string {
if ty.IsFunction() {
return ty.String()
}

name := ty.LocalEntityName()
if !strings.HasPrefix(name, "*") {
name = "*" + name
}

return name
}

// IsFunction returns true if the type represents a function pointer, like
// "func ()".
func (ty Type) IsFunction() bool {
return strings.HasPrefix(string(ty), "func")
}

var (
functionRegexp1 = regexp.MustCompile(`func\s*\((.*?)\)\s*\((.*)\)`)
functionRegexp2 = regexp.MustCompile(`func\s*\((.*?)\)\s*(.*)`)
)

func (ty Type) splitArgs(s string) []string {
if s == "" {
return nil
}

return strings.Split(s, ",")
}

func (ty Type) parseFunctionType() (string, []string) {
matches := functionRegexp1.FindStringSubmatch(string(ty))
if len(matches) > 0 {
return matches[1], ty.splitArgs(matches[2])
}

matches = functionRegexp2.FindStringSubmatch(string(ty))

return matches[1], ty.splitArgs(matches[2])
}

0 comments on commit cfd803f

Please sign in to comment.