(element xml.StartElement, decoder *XMLDecoder, elementLine int)
| 936 | } |
| 937 | } |
| 938 | } |
| 939 | |
| 940 | func parseModify(element xml.StartElement, decoder *XMLDecoder, elementLine int) (Modify, error) { |
| 941 | var modifyElem Modify |
| 942 | |
| 943 | // Parse attributes: field is optional |
| 944 | for _, attr := range element.Attr { |
| 945 | switch attr.Name.Local { |
| 946 | case "type": |
| 947 | mtype := strings.TrimSpace(attr.Value) |
| 948 | if mtype != "" && mtype != "PLUGIN" { |
| 949 | return modifyElem, fmt.Errorf("modify type must be empty or 'PLUGIN', got '%s' at line %d", mtype, elementLine) |
| 950 | } |
| 951 | modifyElem.Type = mtype |
| 952 | case "field": |
| 953 | modifyElem.FieldName = strings.TrimSpace(attr.Value) |
| 954 | } |
| 955 | } |
| 956 | |
| 957 | // Parse content |
| 958 | for { |
| 959 | token, err := decoder.Token() |
| 960 | if err != nil { |
| 961 | return modifyElem, err |
| 962 | } |
| 963 | |
| 964 | switch t := token.(type) { |
| 965 | case xml.CharData: |
| 966 | value := strings.TrimSpace(string(t)) |
| 967 | modifyElem.Value = value |
| 968 | case xml.EndElement: |
| 969 | if t.Name.Local == "modify" { |
| 970 | // If type is PLUGIN, validate plugin call |
| 971 | if modifyElem.Type == "PLUGIN" { |
| 972 | if strings.TrimSpace(modifyElem.Value) == "" { |
| 973 | return modifyElem, fmt.Errorf("modify value (plugin call) cannot be empty at line %d", elementLine) |
| 974 | } |
| 975 | pluginName, args, err := ParseFunctionCall(modifyElem.Value) |
| 976 | if err != nil { |
| 977 | return modifyElem, fmt.Errorf("invalid plugin call syntax at line %d: %v", elementLine, err) |
| 978 | } |
| 979 | pluginInstance, ok := plugin.GetPlugin(pluginName) |
| 980 | if !ok { |
| 981 | if _, tempExists := plugin.GetPluginNew(pluginName); tempExists { |
| 982 | return modifyElem, fmt.Errorf("cannot reference temporary plugin '%s' at line %d, please save it first", pluginName, elementLine) |
| 983 | } |
| 984 | return modifyElem, fmt.Errorf("plugin not found: %s at line %d", pluginName, elementLine) |
| 985 | } |
| 986 | modifyElem.Plugin = pluginInstance |
| 987 | modifyElem.PluginArgs = args |
| 988 | } else { |
| 989 | // Literal mode: field must not be empty |
| 990 | if strings.TrimSpace(modifyElem.FieldName) == "" { |
| 991 | return modifyElem, fmt.Errorf("modify field cannot be empty when type is empty at line %d", elementLine) |
| 992 | } |
| 993 | // Value may be empty string; no plugin validation |
| 994 | } |
| 995 |
no test coverage detected