Add method specification document
This commit is contained in:
parent
8b19829a3c
commit
9b04e89697
4 changed files with 237 additions and 138 deletions
internal/spec
|
@ -21,45 +21,59 @@ type interfaceMethodParser struct {
|
|||
}
|
||||
|
||||
func (p interfaceMethodParser) Parse() (MethodSpec, error) {
|
||||
methodNameTokens := camelcase.Split(p.Method.Name)
|
||||
switch methodNameTokens[0] {
|
||||
case "Insert":
|
||||
return p.parseInsertMethod(methodNameTokens[1:])
|
||||
case "Find":
|
||||
return p.parseFindMethod(methodNameTokens[1:])
|
||||
case "Update":
|
||||
return p.parseUpdateMethod(methodNameTokens[1:])
|
||||
case "Delete":
|
||||
return p.parseDeleteMethod(methodNameTokens[1:])
|
||||
case "Count":
|
||||
return p.parseCountMethod(methodNameTokens[1:])
|
||||
}
|
||||
return MethodSpec{}, UnknownOperationError
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseInsertMethod(tokens []string) (MethodSpec, error) {
|
||||
mode, err := p.extractInsertReturns(p.Method.Returns)
|
||||
operation, err := p.parseMethod()
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
}
|
||||
|
||||
return MethodSpec{
|
||||
Name: p.Method.Name,
|
||||
Params: p.Method.Params,
|
||||
Returns: p.Method.Returns,
|
||||
Operation: operation,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseMethod() (Operation, error) {
|
||||
methodNameTokens := camelcase.Split(p.Method.Name)
|
||||
switch methodNameTokens[0] {
|
||||
case "Insert":
|
||||
return p.parseInsertOperation(methodNameTokens[1:])
|
||||
case "Find":
|
||||
return p.parseFindOperation(methodNameTokens[1:])
|
||||
case "Update":
|
||||
return p.parseUpdateOperation(methodNameTokens[1:])
|
||||
case "Delete":
|
||||
return p.parseDeleteOperation(methodNameTokens[1:])
|
||||
case "Count":
|
||||
return p.parseCountOperation(methodNameTokens[1:])
|
||||
}
|
||||
return nil, UnknownOperationError
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseInsertOperation(tokens []string) (Operation, error) {
|
||||
mode, err := p.extractInsertReturns(p.Method.Returns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateContextParam(); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pointerType := code.PointerType{ContainedType: p.StructModel.ReferencedType()}
|
||||
if mode == QueryModeOne && p.Method.Params[1].Type != pointerType {
|
||||
return MethodSpec{}, InvalidParamError
|
||||
return nil, InvalidParamError
|
||||
}
|
||||
|
||||
arrayType := code.ArrayType{ContainedType: pointerType}
|
||||
if mode == QueryModeMany && p.Method.Params[1].Type != arrayType {
|
||||
return MethodSpec{}, InvalidParamError
|
||||
return nil, InvalidParamError
|
||||
}
|
||||
|
||||
return p.createMethodSpec(InsertOperation{
|
||||
return InsertOperation{
|
||||
Mode: mode,
|
||||
}), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) extractInsertReturns(returns []code.Type) (QueryMode, error) {
|
||||
|
@ -91,33 +105,29 @@ func (p interfaceMethodParser) extractInsertReturns(returns []code.Type) (QueryM
|
|||
return "", UnsupportedReturnError
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseFindMethod(tokens []string) (MethodSpec, error) {
|
||||
if len(tokens) == 0 {
|
||||
return MethodSpec{}, UnsupportedNameError
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseFindOperation(tokens []string) (Operation, error) {
|
||||
mode, err := p.extractModelOrSliceReturns(p.Method.Returns)
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
querySpec, err := parseQuery(tokens, 1)
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateContextParam(); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateQueryFromParams(p.Method.Params[1:], querySpec); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.createMethodSpec(FindOperation{
|
||||
return FindOperation{
|
||||
Mode: mode,
|
||||
Query: querySpec,
|
||||
}), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) extractModelOrSliceReturns(returns []code.Type) (QueryMode, error) {
|
||||
|
@ -153,27 +163,22 @@ func (p interfaceMethodParser) extractModelOrSliceReturns(returns []code.Type) (
|
|||
return "", UnsupportedReturnError
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseUpdateMethod(tokens []string) (MethodSpec, error) {
|
||||
if len(tokens) == 0 {
|
||||
return MethodSpec{}, UnsupportedNameError
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseUpdateOperation(tokens []string) (Operation, error) {
|
||||
mode, err := p.extractIntOrBoolReturns(p.Method.Returns)
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateFieldTokens, queryTokens := p.splitUpdateFieldAndQueryTokens(tokens)
|
||||
|
||||
paramIndex := 1
|
||||
var fields []UpdateField
|
||||
var aggregatedToken string
|
||||
for i, token := range tokens {
|
||||
if token == "By" || token == "All" {
|
||||
tokens = tokens[i:]
|
||||
break
|
||||
} else if token != "And" {
|
||||
for _, token := range updateFieldTokens {
|
||||
if token != "And" {
|
||||
aggregatedToken += token
|
||||
} else if len(aggregatedToken) == 0 {
|
||||
return MethodSpec{}, InvalidUpdateFieldsError
|
||||
return nil, InvalidUpdateFieldsError
|
||||
} else {
|
||||
fields = append(fields, UpdateField{Name: aggregatedToken, ParamIndex: paramIndex})
|
||||
paramIndex++
|
||||
|
@ -181,95 +186,103 @@ func (p interfaceMethodParser) parseUpdateMethod(tokens []string) (MethodSpec, e
|
|||
}
|
||||
}
|
||||
if len(aggregatedToken) == 0 {
|
||||
return MethodSpec{}, InvalidUpdateFieldsError
|
||||
return nil, InvalidUpdateFieldsError
|
||||
}
|
||||
fields = append(fields, UpdateField{Name: aggregatedToken, ParamIndex: paramIndex})
|
||||
|
||||
querySpec, err := parseQuery(tokens, 1+len(fields))
|
||||
querySpec, err := parseQuery(queryTokens, 1+len(fields))
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateContextParam(); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
structField, ok := p.StructModel.Fields.ByName(field.Name)
|
||||
if !ok {
|
||||
return MethodSpec{}, StructFieldNotFoundError
|
||||
return nil, StructFieldNotFoundError
|
||||
}
|
||||
|
||||
if structField.Type != p.Method.Params[field.ParamIndex].Type {
|
||||
return MethodSpec{}, InvalidParamError
|
||||
return nil, InvalidParamError
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.validateQueryFromParams(p.Method.Params[len(fields)+1:], querySpec); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.createMethodSpec(UpdateOperation{
|
||||
return UpdateOperation{
|
||||
Fields: fields,
|
||||
Mode: mode,
|
||||
Query: querySpec,
|
||||
}), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseDeleteMethod(tokens []string) (MethodSpec, error) {
|
||||
if len(tokens) == 0 {
|
||||
return MethodSpec{}, UnsupportedNameError
|
||||
func (p interfaceMethodParser) splitUpdateFieldAndQueryTokens(tokens []string) ([]string, []string) {
|
||||
var updateFieldTokens []string
|
||||
var queryTokens []string
|
||||
|
||||
for i, token := range tokens {
|
||||
if token == "By" || token == "All" {
|
||||
queryTokens = tokens[i:]
|
||||
break
|
||||
} else {
|
||||
updateFieldTokens = append(updateFieldTokens, token)
|
||||
}
|
||||
}
|
||||
|
||||
return updateFieldTokens, queryTokens
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseDeleteOperation(tokens []string) (Operation, error) {
|
||||
mode, err := p.extractIntOrBoolReturns(p.Method.Returns)
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
querySpec, err := parseQuery(tokens, 1)
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateContextParam(); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateQueryFromParams(p.Method.Params[1:], querySpec); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.createMethodSpec(DeleteOperation{
|
||||
return DeleteOperation{
|
||||
Mode: mode,
|
||||
Query: querySpec,
|
||||
}), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseCountMethod(tokens []string) (MethodSpec, error) {
|
||||
if len(tokens) == 0 {
|
||||
return MethodSpec{}, UnsupportedNameError
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) parseCountOperation(tokens []string) (Operation, error) {
|
||||
if err := p.validateCountReturns(p.Method.Returns); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
querySpec, err := parseQuery(tokens, 1)
|
||||
if err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateContextParam(); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.validateQueryFromParams(p.Method.Params[1:], querySpec); err != nil {
|
||||
return MethodSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.createMethodSpec(CountOperation{
|
||||
return CountOperation{
|
||||
Query: querySpec,
|
||||
}), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) validateCountReturns(returns []code.Type) error {
|
||||
|
@ -341,12 +354,3 @@ func (p interfaceMethodParser) validateQueryFromParams(params []code.Param, quer
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p interfaceMethodParser) createMethodSpec(operation Operation) MethodSpec {
|
||||
return MethodSpec{
|
||||
Name: p.Method.Name,
|
||||
Params: p.Method.Params,
|
||||
Returns: p.Method.Returns,
|
||||
Operation: operation,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -965,13 +965,6 @@ func TestParseInterfaceMethod_Insert_Invalid(t *testing.T) {
|
|||
|
||||
func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
|
||||
testTable := []ParseInterfaceMethodInvalidTestCase{
|
||||
{
|
||||
Name: "unsupported find method name",
|
||||
Method: code.Method{
|
||||
Name: "Find",
|
||||
},
|
||||
ExpectedError: spec.UnsupportedNameError,
|
||||
},
|
||||
{
|
||||
Name: "invalid number of returns",
|
||||
Method: code.Method{
|
||||
|
@ -1006,6 +999,17 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
|
|||
},
|
||||
ExpectedError: spec.UnsupportedReturnError,
|
||||
},
|
||||
{
|
||||
Name: "find method without query",
|
||||
Method: code.Method{
|
||||
Name: "Find",
|
||||
Returns: []code.Type{
|
||||
code.PointerType{ContainedType: code.SimpleType("UserModel")},
|
||||
code.SimpleType("error"),
|
||||
},
|
||||
},
|
||||
ExpectedError: spec.InvalidQueryError,
|
||||
},
|
||||
{
|
||||
Name: "misplaced operator token (leftmost)",
|
||||
Method: code.Method{
|
||||
|
@ -1140,13 +1144,6 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
|
|||
|
||||
func TestParseInterfaceMethod_Update_Invalid(t *testing.T) {
|
||||
testTable := []ParseInterfaceMethodInvalidTestCase{
|
||||
{
|
||||
Name: "unsupported update method name",
|
||||
Method: code.Method{
|
||||
Name: "Update",
|
||||
},
|
||||
ExpectedError: spec.UnsupportedNameError,
|
||||
},
|
||||
{
|
||||
Name: "invalid number of returns",
|
||||
Method: code.Method{
|
||||
|
@ -1203,6 +1200,17 @@ func TestParseInterfaceMethod_Update_Invalid(t *testing.T) {
|
|||
},
|
||||
ExpectedError: spec.InvalidUpdateFieldsError,
|
||||
},
|
||||
{
|
||||
Name: "update method without query",
|
||||
Method: code.Method{
|
||||
Name: "UpdateCity",
|
||||
Returns: []code.Type{
|
||||
code.SimpleType("bool"),
|
||||
code.SimpleType("error"),
|
||||
},
|
||||
},
|
||||
ExpectedError: spec.InvalidQueryError,
|
||||
},
|
||||
{
|
||||
Name: "ambiguous query",
|
||||
Method: code.Method{
|
||||
|
@ -1292,13 +1300,6 @@ func TestParseInterfaceMethod_Update_Invalid(t *testing.T) {
|
|||
|
||||
func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
|
||||
testTable := []ParseInterfaceMethodInvalidTestCase{
|
||||
{
|
||||
Name: "unsupported delete method name",
|
||||
Method: code.Method{
|
||||
Name: "Delete",
|
||||
},
|
||||
ExpectedError: spec.UnsupportedNameError,
|
||||
},
|
||||
{
|
||||
Name: "invalid number of returns",
|
||||
Method: code.Method{
|
||||
|
@ -1333,6 +1334,17 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
|
|||
},
|
||||
ExpectedError: spec.UnsupportedReturnError,
|
||||
},
|
||||
{
|
||||
Name: "delete method without query",
|
||||
Method: code.Method{
|
||||
Name: "Delete",
|
||||
Returns: []code.Type{
|
||||
code.SimpleType("int"),
|
||||
code.SimpleType("error"),
|
||||
},
|
||||
},
|
||||
ExpectedError: spec.InvalidQueryError,
|
||||
},
|
||||
{
|
||||
Name: "misplaced operator token (leftmost)",
|
||||
Method: code.Method{
|
||||
|
@ -1467,25 +1479,6 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
|
|||
|
||||
func TestParseInterfaceMethod_Count_Invalid(t *testing.T) {
|
||||
testTable := []ParseInterfaceMethodInvalidTestCase{
|
||||
{
|
||||
Name: "unsupported count method name",
|
||||
Method: code.Method{
|
||||
Name: "Count",
|
||||
},
|
||||
ExpectedError: spec.UnsupportedNameError,
|
||||
},
|
||||
{
|
||||
Name: "invalid number of returns",
|
||||
Method: code.Method{
|
||||
Name: "CountAll",
|
||||
Returns: []code.Type{
|
||||
code.SimpleType("int"),
|
||||
code.SimpleType("error"),
|
||||
code.SimpleType("bool"),
|
||||
},
|
||||
},
|
||||
ExpectedError: spec.UnsupportedReturnError,
|
||||
},
|
||||
{
|
||||
Name: "invalid number of returns",
|
||||
Method: code.Method{
|
||||
|
@ -1520,6 +1513,17 @@ func TestParseInterfaceMethod_Count_Invalid(t *testing.T) {
|
|||
},
|
||||
ExpectedError: spec.UnsupportedReturnError,
|
||||
},
|
||||
{
|
||||
Name: "count method without query",
|
||||
Method: code.Method{
|
||||
Name: "Count",
|
||||
Returns: []code.Type{
|
||||
code.SimpleType("int"),
|
||||
code.SimpleType("error"),
|
||||
},
|
||||
},
|
||||
ExpectedError: spec.InvalidQueryError,
|
||||
},
|
||||
{
|
||||
Name: "invalid query",
|
||||
Method: code.Method{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue