eq2go/internal/packets/parser/conditions.go

356 lines
9.3 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.evaluateVariableSetCondition(fieldTag.IfVariableSet) {
return false
}
if fieldTag.IfVariableNotSet != "" && !p.evaluateVariableNotSetCondition(fieldTag.IfVariableNotSet) {
return false
}
if fieldTag.IfFlag != "" && !p.evaluateFlagCondition(fieldTag.IfFlag) {
return false
}
if fieldTag.IfFlagNotSet != "" && !p.evaluateFlagNotSetCondition(fieldTag.IfFlagNotSet) {
return false
}
if fieldTag.IfEquals != "" {
if !p.evaluateEqualsConditions(fieldTag.IfEquals) {
return false
}
}
if fieldTag.IfNotEquals != "" {
if !p.evaluateNotEqualsConditions(fieldTag.IfNotEquals) {
return false
}
}
return true
}
// evaluateVariableSetCondition handles comma-separated variable set conditions
func (p *Parser) evaluateVariableSetCondition(variables string) bool {
varList := p.splitCommaSeparated(variables)
// Return true if ANY variable is set
for _, variable := range varList {
if p.isVariableSet(variable) {
return true
}
}
return false
}
// evaluateVariableNotSetCondition handles comma-separated variable not set conditions
func (p *Parser) evaluateVariableNotSetCondition(variables string) bool {
varList := p.splitCommaSeparated(variables)
// Return true if ALL variables are not set
for _, variable := range varList {
if p.isVariableSet(variable) {
return false
}
}
return true
}
// evaluateFlagCondition handles comma-separated flag conditions
func (p *Parser) evaluateFlagCondition(flags string) bool {
flagList := p.splitCommaSeparated(flags)
// Return true if ANY flag is set
for _, flag := range flagList {
if p.flags[flag] {
return true
}
}
return false
}
// evaluateFlagNotSetCondition handles comma-separated flag not set conditions
func (p *Parser) evaluateFlagNotSetCondition(flags string) bool {
flagList := p.splitCommaSeparated(flags)
// Return true if ALL flags are not set
for _, flag := range flagList {
if p.flags[flag] {
return false
}
}
return true
}
// evaluateEqualsConditions handles comma-separated equals conditions
func (p *Parser) evaluateEqualsConditions(conditions string) bool {
conditionList := p.splitCommaSeparated(conditions)
// Return true if ANY condition is true
for _, condition := range conditionList {
parts := strings.Split(condition, "=")
if len(parts) == 2 && p.evaluateEqualsCondition(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])) {
return true
}
}
return false
}
// evaluateNotEqualsConditions handles comma-separated not equals conditions
func (p *Parser) evaluateNotEqualsConditions(conditions string) bool {
conditionList := p.splitCommaSeparated(conditions)
// Return true if ALL conditions are true (all not equal)
for _, condition := range conditionList {
parts := strings.Split(condition, "=")
if len(parts) == 2 {
if p.evaluateEqualsCondition(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])) {
return false // This condition is false (they are equal)
}
}
}
return true
}
// splitCommaSeparated splits a comma-separated string and trims whitespace
func (p *Parser) splitCommaSeparated(input string) []string {
if input == "" {
return []string{}
}
parts := strings.Split(input, ",")
result := make([]string, len(parts))
for i, part := range parts {
result[i] = strings.TrimSpace(part)
}
return result
}
// 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
}