Files
mq/handlers/split_join_handler.go
2025-08-06 08:25:43 +05:45

260 lines
7.2 KiB
Go

package handlers
import (
"context"
"fmt"
"reflect"
"regexp"
"strings"
"github.com/oarkflow/json"
"github.com/oarkflow/mq"
"github.com/oarkflow/mq/dag"
)
// SplitJoinHandler handles splitting strings into arrays and joining arrays into strings
type SplitJoinHandler struct {
dag.Operation
OpType string `json:"op_type"` // "split" or "join"
SourceField string `json:"source_field"` // field to operate on
TargetField string `json:"target_field"` // field to store result
Delimiter string `json:"delimiter"` // delimiter for split/join
Options SplitJoinOptions `json:"options"`
}
type SplitJoinOptions struct {
TrimSpaces bool `json:"trim_spaces"` // trim spaces from elements (split only)
RemoveEmpty bool `json:"remove_empty"` // remove empty elements (split only)
MaxSplit int `json:"max_split"` // maximum number of splits (-1 for unlimited)
UseRegex bool `json:"use_regex"` // treat delimiter as regex pattern (split only)
CaseInsensitive bool `json:"case_insensitive"` // case insensitive regex (split only)
Prefix string `json:"prefix"` // prefix for joined string (join only)
Suffix string `json:"suffix"` // suffix for joined string (join only)
}
func (s *SplitJoinHandler) ProcessTask(ctx context.Context, task *mq.Task) mq.Result {
var data map[string]any
if err := json.Unmarshal(task.Payload, &data); err != nil {
return mq.Result{Error: fmt.Errorf("failed to unmarshal data: %v", err), Ctx: ctx}
}
// Get source value
sourceValue, exists := data[s.SourceField]
if !exists {
return mq.Result{Error: fmt.Errorf("source field '%s' not found", s.SourceField), Ctx: ctx}
}
var result any
var err error
switch s.OpType {
case "split":
result, err = s.performSplit(sourceValue)
case "join":
result, err = s.performJoin(sourceValue)
default:
return mq.Result{Error: fmt.Errorf("unsupported operation: %s", s.OpType), Ctx: ctx}
}
if err != nil {
return mq.Result{Error: err, Ctx: ctx}
}
// Set target field
targetField := s.TargetField
if targetField == "" {
targetField = s.SourceField // overwrite source if no target specified
}
data[targetField] = result
bt, _ := json.Marshal(data)
return mq.Result{Payload: bt, Ctx: ctx}
}
func (s *SplitJoinHandler) performSplit(value any) ([]string, error) {
// Convert value to string
str := fmt.Sprintf("%v", value)
var parts []string
if s.Options.UseRegex {
// Use regex for splitting
flags := ""
if s.Options.CaseInsensitive {
flags = "(?i)"
}
pattern := flags + s.Delimiter
re, err := regexp.Compile(pattern)
if err != nil {
return nil, fmt.Errorf("invalid regex pattern '%s': %v", pattern, err)
}
if s.Options.MaxSplit > 0 {
parts = re.Split(str, s.Options.MaxSplit+1)
} else {
parts = re.Split(str, -1)
}
} else {
// Use simple string splitting
if s.Options.MaxSplit > 0 {
parts = strings.SplitN(str, s.Delimiter, s.Options.MaxSplit+1)
} else {
parts = strings.Split(str, s.Delimiter)
}
}
// Process the parts based on options
var processedParts []string
for _, part := range parts {
if s.Options.TrimSpaces {
part = strings.TrimSpace(part)
}
if s.Options.RemoveEmpty && part == "" {
continue
}
processedParts = append(processedParts, part)
}
return processedParts, nil
}
func (s *SplitJoinHandler) performJoin(value any) (string, error) {
// Convert value to slice of strings
parts, err := s.convertToStringSlice(value)
if err != nil {
return "", err
}
// Join the parts
joined := strings.Join(parts, s.Delimiter)
// Add prefix/suffix if specified
if s.Options.Prefix != "" {
joined = s.Options.Prefix + joined
}
if s.Options.Suffix != "" {
joined = joined + s.Options.Suffix
}
return joined, nil
}
func (s *SplitJoinHandler) convertToStringSlice(value any) ([]string, error) {
rv := reflect.ValueOf(value)
if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array {
return nil, fmt.Errorf("value must be an array or slice for join operation")
}
var parts []string
for i := 0; i < rv.Len(); i++ {
element := rv.Index(i).Interface()
parts = append(parts, fmt.Sprintf("%v", element))
}
return parts, nil
}
// Advanced split/join handler for complex operations
type AdvancedSplitJoinHandler struct {
dag.Operation
Operations []SplitJoinOperation `json:"operations"` // chain of split/join operations
}
type SplitJoinOperation struct {
Type string `json:"type"` // "split" or "join"
SourceField string `json:"source_field"` // field to operate on
TargetField string `json:"target_field"` // field to store result
Delimiter string `json:"delimiter"` // delimiter for operation
Options SplitJoinOptions `json:"options"` // operation options
}
func (a *AdvancedSplitJoinHandler) ProcessTask(ctx context.Context, task *mq.Task) mq.Result {
var data map[string]any
if err := json.Unmarshal(task.Payload, &data); err != nil {
return mq.Result{Error: fmt.Errorf("failed to unmarshal data: %v", err), Ctx: ctx}
}
// Execute operations in sequence
for i, op := range a.Operations {
handler := &SplitJoinHandler{
Operation: dag.Operation{
ID: fmt.Sprintf("%s_op_%d", a.ID, i),
Key: "temp_split_join",
Type: dag.Function,
Tags: []string{"data", "temp"},
},
OpType: op.Type,
SourceField: op.SourceField,
TargetField: op.TargetField,
Delimiter: op.Delimiter,
Options: op.Options,
}
// Create a temporary task for this operation
tempData, _ := json.Marshal(data)
tempTask := &mq.Task{Payload: tempData}
result := handler.ProcessTask(ctx, tempTask)
if result.Error != nil {
return mq.Result{Error: fmt.Errorf("operation %d failed: %v", i+1, result.Error), Ctx: ctx}
}
// Update data with the result
if err := json.Unmarshal(result.Payload, &data); err != nil {
return mq.Result{Error: fmt.Errorf("failed to unmarshal result from operation %d: %v", i+1, err), Ctx: ctx}
}
}
bt, _ := json.Marshal(data)
return mq.Result{Payload: bt, Ctx: ctx}
}
// Factory functions
func NewSplitHandler(id, sourceField, targetField, delimiter string, options SplitJoinOptions) *SplitJoinHandler {
return &SplitJoinHandler{
Operation: dag.Operation{
ID: id,
Key: "split_string",
Type: dag.Function,
Tags: []string{"data", "string", "split"},
},
OpType: "split",
SourceField: sourceField,
TargetField: targetField,
Delimiter: delimiter,
Options: options,
}
}
func NewJoinHandler(id, sourceField, targetField, delimiter string, options SplitJoinOptions) *SplitJoinHandler {
return &SplitJoinHandler{
Operation: dag.Operation{
ID: id,
Key: "join_array",
Type: dag.Function,
Tags: []string{"data", "array", "join"},
},
OpType: "join",
SourceField: sourceField,
TargetField: targetField,
Delimiter: delimiter,
Options: options,
}
}
func NewAdvancedSplitJoinHandler(id string, operations []SplitJoinOperation) *AdvancedSplitJoinHandler {
return &AdvancedSplitJoinHandler{
Operation: dag.Operation{
ID: id,
Key: "advanced_split_join",
Type: dag.Function,
Tags: []string{"data", "string", "array", "advanced"},
},
Operations: operations,
}
}