2021-01-16 06:36:44 +00:00
|
|
|
package spec
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/fatih/camelcase"
|
|
|
|
"github.com/sunboyy/repogen/internal/code"
|
|
|
|
)
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
// ParseInterfaceMethod returns repository method spec from declared interface method
|
|
|
|
func ParseInterfaceMethod(structModel code.Struct, method code.Method) (MethodSpec, error) {
|
|
|
|
parser := interfaceMethodParser{
|
2021-01-16 06:36:44 +00:00
|
|
|
StructModel: structModel,
|
2021-01-21 11:56:30 +00:00
|
|
|
Method: method,
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return parser.Parse()
|
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
type interfaceMethodParser struct {
|
2021-01-16 06:36:44 +00:00
|
|
|
StructModel code.Struct
|
2021-01-21 11:56:30 +00:00
|
|
|
Method code.Method
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
func (p interfaceMethodParser) Parse() (MethodSpec, error) {
|
|
|
|
methodNameTokens := camelcase.Split(p.Method.Name)
|
2021-01-16 06:36:44 +00:00
|
|
|
switch methodNameTokens[0] {
|
|
|
|
case "Find":
|
2021-01-21 11:56:30 +00:00
|
|
|
return p.parseFindMethod(methodNameTokens[1:])
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
2021-01-21 11:56:30 +00:00
|
|
|
return MethodSpec{}, UnknownOperationError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
func (p interfaceMethodParser) parseFindMethod(tokens []string) (MethodSpec, error) {
|
2021-01-16 06:36:44 +00:00
|
|
|
if len(tokens) == 0 {
|
2021-01-21 11:56:30 +00:00
|
|
|
return MethodSpec{}, UnsupportedNameError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
mode, err := p.extractFindReturns(p.Method.Returns)
|
2021-01-16 06:36:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return MethodSpec{}, err
|
|
|
|
}
|
|
|
|
|
2021-01-17 03:29:50 +00:00
|
|
|
querySpec, err := p.parseQuery(tokens)
|
2021-01-16 06:36:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return MethodSpec{}, err
|
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
if err := p.validateMethodSignature(querySpec); err != nil {
|
|
|
|
return MethodSpec{}, err
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return MethodSpec{
|
2021-01-21 11:56:30 +00:00
|
|
|
Name: p.Method.Name,
|
|
|
|
Params: p.Method.Params,
|
|
|
|
Returns: p.Method.Returns,
|
2021-01-16 06:36:44 +00:00
|
|
|
Operation: FindOperation{
|
|
|
|
Mode: mode,
|
2021-01-17 03:29:50 +00:00
|
|
|
Query: querySpec,
|
2021-01-16 06:36:44 +00:00
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
func (p interfaceMethodParser) extractFindReturns(returns []code.Type) (QueryMode, error) {
|
2021-01-16 06:36:44 +00:00
|
|
|
if len(returns) != 2 {
|
2021-01-21 11:56:30 +00:00
|
|
|
return "", UnsupportedReturnError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if returns[1] != code.SimpleType("error") {
|
2021-01-21 11:56:30 +00:00
|
|
|
return "", UnsupportedReturnError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pointerType, ok := returns[0].(code.PointerType)
|
|
|
|
if ok {
|
|
|
|
simpleType := pointerType.ContainedType
|
|
|
|
if simpleType == code.SimpleType(p.StructModel.Name) {
|
|
|
|
return QueryModeOne, nil
|
|
|
|
}
|
2021-01-21 11:56:30 +00:00
|
|
|
return "", UnsupportedReturnError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
arrayType, ok := returns[0].(code.ArrayType)
|
|
|
|
if ok {
|
|
|
|
pointerType, ok := arrayType.ContainedType.(code.PointerType)
|
|
|
|
if ok {
|
|
|
|
simpleType := pointerType.ContainedType
|
|
|
|
if simpleType == code.SimpleType(p.StructModel.Name) {
|
|
|
|
return QueryModeMany, nil
|
|
|
|
}
|
2021-01-21 11:56:30 +00:00
|
|
|
return "", UnsupportedReturnError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
return "", UnsupportedReturnError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 11:56:30 +00:00
|
|
|
func (p interfaceMethodParser) parseQuery(tokens []string) (QuerySpec, error) {
|
2021-01-16 06:36:44 +00:00
|
|
|
if len(tokens) == 0 {
|
2021-01-21 11:56:30 +00:00
|
|
|
return QuerySpec{}, InvalidQueryError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(tokens) == 1 && tokens[0] == "All" {
|
2021-01-17 03:29:50 +00:00
|
|
|
return QuerySpec{}, nil
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if tokens[0] == "One" {
|
|
|
|
tokens = tokens[1:]
|
|
|
|
}
|
|
|
|
if tokens[0] == "By" {
|
|
|
|
tokens = tokens[1:]
|
|
|
|
}
|
|
|
|
|
2021-01-19 12:35:54 +00:00
|
|
|
if tokens[0] == "And" || tokens[0] == "Or" {
|
2021-01-21 11:56:30 +00:00
|
|
|
return QuerySpec{}, InvalidQueryError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
2021-01-19 12:26:26 +00:00
|
|
|
|
2021-01-19 12:35:54 +00:00
|
|
|
var operator Operator
|
2021-01-19 12:26:26 +00:00
|
|
|
var predicates []Predicate
|
|
|
|
var aggregatedToken predicateToken
|
2021-01-16 06:36:44 +00:00
|
|
|
for _, token := range tokens {
|
2021-01-19 12:35:54 +00:00
|
|
|
if token != "And" && token != "Or" {
|
2021-01-19 12:26:26 +00:00
|
|
|
aggregatedToken = append(aggregatedToken, token)
|
2021-01-21 11:56:30 +00:00
|
|
|
} else if len(aggregatedToken) == 0 {
|
|
|
|
return QuerySpec{}, InvalidQueryError
|
2021-01-19 12:35:54 +00:00
|
|
|
} else if token == "And" && operator != OperatorOr {
|
|
|
|
operator = OperatorAnd
|
|
|
|
predicates = append(predicates, aggregatedToken.ToPredicate())
|
|
|
|
aggregatedToken = predicateToken{}
|
|
|
|
} else if token == "Or" && operator != OperatorAnd {
|
|
|
|
operator = OperatorOr
|
2021-01-19 12:26:26 +00:00
|
|
|
predicates = append(predicates, aggregatedToken.ToPredicate())
|
|
|
|
aggregatedToken = predicateToken{}
|
2021-01-19 12:35:54 +00:00
|
|
|
} else {
|
2021-01-21 11:56:30 +00:00
|
|
|
return QuerySpec{}, InvalidQueryError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-19 12:26:26 +00:00
|
|
|
if len(aggregatedToken) == 0 {
|
2021-01-21 11:56:30 +00:00
|
|
|
return QuerySpec{}, InvalidQueryError
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
2021-01-19 12:26:26 +00:00
|
|
|
predicates = append(predicates, aggregatedToken.ToPredicate())
|
2021-01-16 06:36:44 +00:00
|
|
|
|
2021-01-19 12:35:54 +00:00
|
|
|
return QuerySpec{Operator: operator, Predicates: predicates}, nil
|
2021-01-16 06:36:44 +00:00
|
|
|
}
|
2021-01-21 11:56:30 +00:00
|
|
|
|
|
|
|
func (p interfaceMethodParser) validateMethodSignature(querySpec QuerySpec) error {
|
|
|
|
contextType := code.ExternalType{PackageAlias: "context", Name: "Context"}
|
|
|
|
if len(p.Method.Params) == 0 || p.Method.Params[0].Type != contextType {
|
|
|
|
return ContextParamRequiredError
|
|
|
|
}
|
|
|
|
|
|
|
|
if querySpec.NumberOfArguments()+1 != len(p.Method.Params) {
|
|
|
|
return InvalidParamError
|
|
|
|
}
|
|
|
|
|
|
|
|
currentParamIndex := 1
|
|
|
|
for _, predicate := range querySpec.Predicates {
|
|
|
|
structField, ok := p.StructModel.Fields.ByName(predicate.Field)
|
|
|
|
if !ok {
|
|
|
|
return StructFieldNotFoundError
|
|
|
|
}
|
|
|
|
|
|
|
|
if structField.Type != p.Method.Params[currentParamIndex].Type {
|
|
|
|
return InvalidParamError
|
|
|
|
}
|
|
|
|
|
|
|
|
currentParamIndex++
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|