356 lines
9.3 KiB
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
|
|
}
|