eq2go/internal/packets/parser/conditions.go

268 lines
6.7 KiB
Go

package parser
import (
"reflect"
"strconv"
"strings"
)
// evaluateAllConditions checks all conditional logic
func (p *Parser) evaluateAllConditions(fieldTag *FieldTag, _ reflect.Value) bool {
if fieldTag.Condition != nil && !p.evaluateCondition(fieldTag.Condition) {
return false
}
if fieldTag.IfVariableSet != "" && !p.isVariableSet(fieldTag.IfVariableSet) {
return false
}
if fieldTag.IfVariableNotSet != "" && p.isVariableSet(fieldTag.IfVariableNotSet) {
return false
}
if fieldTag.IfFlag != "" && !p.flags[fieldTag.IfFlag] {
return false
}
if fieldTag.IfFlagNotSet != "" && p.flags[fieldTag.IfFlagNotSet] {
return false
}
if fieldTag.IfEquals != "" {
parts := strings.Split(fieldTag.IfEquals, "=")
if len(parts) == 2 && !p.evaluateEqualsCondition(parts[0], parts[1]) {
return false
}
}
if fieldTag.IfNotEquals != "" {
parts := strings.Split(fieldTag.IfNotEquals, "=")
if len(parts) == 2 && p.evaluateEqualsCondition(parts[0], parts[1]) {
return false
}
}
return true
}
// evaluateCondition evaluates conditions for conditional fields
func (p *Parser) evaluateCondition(condition *FieldCondition) bool {
if condition.Type == "simple" {
return p.isVariableSet(condition.Variable)
}
cachedValue, exists := p.resolveVariable(condition.Variable)
if !exists {
return false
}
compareValue, err := p.convertValue(condition.Value, cachedValue)
if err != nil {
return false
}
return p.compareValues(cachedValue, compareValue, condition.Operator)
}
// isVariableSet checks if a variable exists and has a truthy value
func (p *Parser) isVariableSet(variable string) bool {
cachedValue, exists := p.resolveVariable(variable)
if !exists {
return false
}
return p.isTruthy(cachedValue)
}
// evaluateEqualsCondition checks field equality
func (p *Parser) evaluateEqualsCondition(variable, value string) bool {
cachedValue, exists := p.resolveVariable(variable)
if !exists {
return false
}
compareValue, err := p.convertValue(value, cachedValue)
if err != nil {
return false
}
return p.compareValues(cachedValue, compareValue, "==")
}
// evaluateType2Criteria evaluates type2 criteria for alternative types with string length support
func (p *Parser) evaluateType2Criteria(criteria string) bool {
// String length operators: !>, !<, !>=, !<=, !=
stringLengthOps := []string{"!>=", "!<=", "!>", "!<", "!="}
for _, op := range stringLengthOps {
if idx := strings.Index(criteria, op); idx > 0 {
fieldName := strings.TrimSpace(criteria[:idx])
valueStr := strings.TrimSpace(criteria[idx+len(op):])
cachedValue, exists := p.resolveVariable(fieldName)
if !exists {
return false
}
return p.evaluateStringLengthCondition(cachedValue, valueStr, op)
}
}
// Standard comparison operators
operators := []string{">=", "<=", ">", "<", "=="}
for _, op := range operators {
if idx := strings.Index(criteria, op); idx > 0 {
fieldName := strings.TrimSpace(criteria[:idx])
valueStr := strings.TrimSpace(criteria[idx+len(op):])
cachedValue, exists := p.resolveVariable(fieldName)
if !exists {
return false
}
compareValue, err := p.convertValue(valueStr, cachedValue)
if err != nil {
return false
}
return p.compareValues(cachedValue, compareValue, op)
}
}
return false
}
// evaluateStringLengthCondition evaluates string length conditions
func (p *Parser) evaluateStringLengthCondition(value any, lengthStr, operator string) bool {
var stringVal string
// Extract string value from various types
switch v := value.(type) {
case string:
stringVal = v
default:
// Try to get string representation from struct fields
if reflect.TypeOf(value).Kind() == reflect.Struct {
val := reflect.ValueOf(value)
if dataField := val.FieldByName("Data"); dataField.IsValid() && dataField.Kind() == reflect.String {
stringVal = dataField.String()
} else {
return false
}
} else {
return false
}
}
targetLength, err := strconv.Atoi(lengthStr)
if err != nil {
return false
}
stringLength := len(stringVal)
switch operator {
case "!>":
return stringLength > targetLength
case "!<":
return stringLength < targetLength
case "!>=":
return stringLength >= targetLength
case "!<=":
return stringLength <= targetLength
case "!=":
return stringLength != targetLength
default:
return false
}
}
// resolveVariable resolves variables with support for array indices and complex patterns
func (p *Parser) resolveVariable(variable string) (any, bool) {
// Handle %i patterns by replacing with current array index
if strings.Contains(variable, "%i") {
currentIndex := p.GetCurrentArrayIndex()
if currentIndex >= 0 {
variable = strings.ReplaceAll(variable, "%i", strconv.Itoa(currentIndex))
}
}
// Check field cache first
if cachedValue, exists := p.fieldCache[variable]; exists {
return cachedValue, true
}
// Handle array index patterns like "header_info_mod_need_0"
if strings.Contains(variable, "_") {
if value, exists := p.resolveArrayIndexVariable(variable); exists {
return value, true
}
}
// Check current struct
if p.currentStruct.IsValid() {
if field := p.currentStruct.FieldByName(variable); field.IsValid() {
return field.Interface(), true
}
}
// Check struct stack for nested resolution
for i := len(p.structStack) - 1; i >= 0; i-- {
if field := p.structStack[i].FieldByName(variable); field.IsValid() {
return field.Interface(), true
}
}
return nil, false
}
// resolveArrayIndexVariable handles variables with array index suffixes
func (p *Parser) resolveArrayIndexVariable(variable string) (any, bool) {
parts := strings.Split(variable, "_")
if len(parts) < 2 {
return nil, false
}
// Try to extract index from last part
lastPart := parts[len(parts)-1]
if index, err := strconv.Atoi(lastPart); err == nil {
// Reconstruct base variable name without index
baseVar := strings.Join(parts[:len(parts)-1], "_")
// Look for array/slice field with this base name
if cachedValue, exists := p.fieldCache[baseVar]; exists {
return p.getArrayElement(cachedValue, index)
}
// Check current struct for array field
if p.currentStruct.IsValid() {
if field := p.currentStruct.FieldByName(baseVar); field.IsValid() {
return p.getArrayElement(field.Interface(), index)
}
}
// Check struct stack
for i := len(p.structStack) - 1; i >= 0; i-- {
if field := p.structStack[i].FieldByName(baseVar); field.IsValid() {
return p.getArrayElement(field.Interface(), index)
}
}
}
return nil, false
}
// getArrayElement safely extracts element from array/slice
func (p *Parser) getArrayElement(value any, index int) (any, bool) {
val := reflect.ValueOf(value)
switch val.Kind() {
case reflect.Slice, reflect.Array:
if index >= 0 && index < val.Len() {
return val.Index(index).Interface(), true
}
}
return nil, false
}