203 lines
5.2 KiB
Go
203 lines
5.2 KiB
Go
package spec
|
|
|
|
import "git.kmsign.ru/royalcat/repogen/internal/code"
|
|
|
|
// UpdateOperation is a method specification for update operations
|
|
type UpdateOperation struct {
|
|
Update Update
|
|
Mode QueryMode
|
|
Query QuerySpec
|
|
}
|
|
|
|
// Name returns "Update" operation name
|
|
func (o UpdateOperation) Name() string {
|
|
return "Update"
|
|
}
|
|
|
|
// Update is an interface of update operation type
|
|
type Update interface {
|
|
Name() string
|
|
NumberOfArguments() int
|
|
}
|
|
|
|
// UpdateModel is a type of update operation that update the whole model
|
|
type UpdateModel struct {
|
|
}
|
|
|
|
// Name returns UpdateModel name 'Model'
|
|
func (u UpdateModel) Name() string {
|
|
return "Model"
|
|
}
|
|
|
|
// NumberOfArguments returns 1
|
|
func (u UpdateModel) NumberOfArguments() int {
|
|
return 1
|
|
}
|
|
|
|
// UpdateFields is a type of update operation that update specific fields
|
|
type UpdateFields []UpdateField
|
|
|
|
// Name returns UpdateFields name 'Fields'
|
|
func (u UpdateFields) Name() string {
|
|
return "Fields"
|
|
}
|
|
|
|
// NumberOfArguments returns number of update fields
|
|
func (u UpdateFields) NumberOfArguments() int {
|
|
return len(u)
|
|
}
|
|
|
|
// UpdateField stores mapping between field name in the model and the parameter
|
|
// index.
|
|
type UpdateField struct {
|
|
FieldReference FieldReference
|
|
ParamIndex int
|
|
Operator UpdateOperator
|
|
}
|
|
|
|
// UpdateOperator is a custom type that declares update operator to be used in
|
|
// an update operation
|
|
type UpdateOperator string
|
|
|
|
// UpdateOperator constants
|
|
const (
|
|
UpdateOperatorSet UpdateOperator = "SET"
|
|
UpdateOperatorPush UpdateOperator = "PUSH"
|
|
UpdateOperatorInc UpdateOperator = "INC"
|
|
)
|
|
|
|
// NumberOfArguments returns number of arguments required to perform an update operation
|
|
func (o UpdateOperator) NumberOfArguments() int {
|
|
return 1
|
|
}
|
|
|
|
// ArgumentType returns type that is required for function parameter
|
|
func (o UpdateOperator) ArgumentType(fieldType code.Type) code.Type {
|
|
switch o {
|
|
case UpdateOperatorPush:
|
|
arrayType := fieldType.(code.ArrayType)
|
|
return arrayType.ContainedType
|
|
default:
|
|
return fieldType
|
|
}
|
|
}
|
|
|
|
func (p interfaceMethodParser) parseUpdateOperation(tokens []string) (Operation, error) {
|
|
mode, err := p.extractIntOrBoolReturns(p.Method.Returns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.validateContextParam(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
updateTokens, queryTokens := p.splitUpdateAndQueryTokens(tokens)
|
|
|
|
update, err := p.parseUpdate(updateTokens)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
querySpec, err := p.parseQuery(queryTokens, 1+update.NumberOfArguments())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.validateQueryFromParams(p.Method.Params[update.NumberOfArguments()+1:], querySpec); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return UpdateOperation{
|
|
Update: update,
|
|
Mode: mode,
|
|
Query: querySpec,
|
|
}, nil
|
|
}
|
|
|
|
func (p interfaceMethodParser) parseUpdate(tokens []string) (Update, error) {
|
|
if len(tokens) == 0 {
|
|
requiredType := code.PointerType{ContainedType: p.StructModel.ReferencedType()}
|
|
if len(p.Method.Params) <= 1 || p.Method.Params[1].Type != requiredType {
|
|
return nil, ErrInvalidUpdateFields
|
|
}
|
|
return UpdateModel{}, nil
|
|
}
|
|
|
|
updateFieldTokens, ok := splitByAnd(tokens)
|
|
if !ok {
|
|
return nil, ErrInvalidUpdateFields
|
|
}
|
|
|
|
var updateFields UpdateFields
|
|
|
|
paramIndex := 1
|
|
for _, updateFieldToken := range updateFieldTokens {
|
|
updateField, err := p.parseUpdateField(updateFieldToken, paramIndex)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
updateFields = append(updateFields, updateField)
|
|
paramIndex += updateField.Operator.NumberOfArguments()
|
|
}
|
|
|
|
for _, field := range updateFields {
|
|
if len(p.Method.Params) < field.ParamIndex+field.Operator.NumberOfArguments() {
|
|
return nil, ErrInvalidUpdateFields
|
|
}
|
|
|
|
requiredType := field.Operator.ArgumentType(field.FieldReference.ReferencedField().Type)
|
|
|
|
for i := 0; i < field.Operator.NumberOfArguments(); i++ {
|
|
if requiredType != p.Method.Params[field.ParamIndex+i].Type {
|
|
return nil, NewArgumentTypeNotMatchedError(field.FieldReference.ReferencingCode(), requiredType,
|
|
p.Method.Params[field.ParamIndex+i].Type)
|
|
}
|
|
}
|
|
}
|
|
|
|
return updateFields, nil
|
|
}
|
|
|
|
func (p interfaceMethodParser) parseUpdateField(t []string,
|
|
paramIndex int) (UpdateField, error) {
|
|
|
|
if len(t) > 1 && t[len(t)-1] == "Push" {
|
|
return p.createUpdateField(t[:len(t)-1], UpdateOperatorPush, paramIndex)
|
|
}
|
|
if len(t) > 1 && t[len(t)-1] == "Inc" {
|
|
return p.createUpdateField(t[:len(t)-1], UpdateOperatorInc, paramIndex)
|
|
}
|
|
return p.createUpdateField(t, UpdateOperatorSet, paramIndex)
|
|
}
|
|
|
|
func (p interfaceMethodParser) createUpdateField(t []string,
|
|
operator UpdateOperator, paramIndex int) (UpdateField, error) {
|
|
|
|
fieldReference, ok := p.fieldResolver.ResolveStructField(p.StructModel, t)
|
|
if !ok {
|
|
return UpdateField{}, NewStructFieldNotFoundError(t)
|
|
}
|
|
|
|
if !p.validateUpdateOperator(fieldReference.ReferencedField().Type, operator) {
|
|
return UpdateField{}, NewIncompatibleUpdateOperatorError(operator, fieldReference)
|
|
}
|
|
|
|
return UpdateField{
|
|
FieldReference: fieldReference,
|
|
ParamIndex: paramIndex,
|
|
Operator: operator,
|
|
}, nil
|
|
}
|
|
|
|
func (p interfaceMethodParser) validateUpdateOperator(referencedType code.Type, operator UpdateOperator) bool {
|
|
switch operator {
|
|
case UpdateOperatorPush:
|
|
_, ok := referencedType.(code.ArrayType)
|
|
return ok
|
|
case UpdateOperatorInc:
|
|
return referencedType.IsNumber()
|
|
}
|
|
return true
|
|
}
|