mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-05 16:06:55 +08:00
260 lines
7.2 KiB
Go
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,
|
|
}
|
|
}
|