Add method specification document

This commit is contained in:
sunboyy 2021-02-07 12:51:51 +07:00
parent 8b19829a3c
commit 9b04e89697
4 changed files with 237 additions and 138 deletions

View file

@ -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,
}
}

View file

@ -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{