Commits
Diff
diff --git a/search/main.go b/search/main.go
index 8b9d5ef..4d351cb 100644
--- a/search/main.go
+++ b/search/main.go
@@ -5,2 +4,0 @@ import (
- "os"
- "path/filepath"
@@ -13 +11 @@ import (
-func One(data interface{}, filters, removes, replaces []string, path string) interface{} {
+func All(data []interface{}, filters, removes, replaces []string, replaceFile string) []interface{} {
@@ -21 +18,0 @@ func One(data interface{}, filters, removes, replaces []string, path string) int
- compiledRegex := []*regexp.Regexp{}
@@ -23,106 +20,2 @@ func One(data interface{}, filters, removes, replaces []string, path string) int
- if strings.HasSuffix(v, ".rf") {
- target, fp := splitFilePath(v)
- fb, _ := os.ReadFile(filepath.Join(path, fp))
- f := string(fb)
-
- lines := strings.Split(f, "\n")
-
- for _, l := range lines {
- // Add the target back to the lines
- // the .rf file should only contain the /match/replace/ statements
- if len(strings.TrimSpace(l)) != 0 && strings.TrimSpace(l)[0] != '#' {
- target, match, replacement := splitReplaceString(target + "=" + l)
- replace = append(replace, []string{target, match, replacement})
- re, err := regexp.Compile(match) // Compile the regex.
- if err != nil {
- panic(err)
- }
- compiledRegex = append(compiledRegex, re)
- }
- }
- } else {
- target, match, replacement := splitReplaceString(v)
- replace = append(replace, []string{target, match, replacement})
- re, err := regexp.Compile(match) // Compile the regex.
- if err != nil {
- panic(err)
- }
- compiledRegex = append(compiledRegex, re)
- }
- }
-
- remove := []string{}
- for _, v := range removes {
- remove = append(remove, v)
- }
-
-
- val := reflect.ValueOf(data)
-
- if val.Kind() == reflect.Ptr && val.Elem().Kind() == reflect.Struct {
- // Already a pointer to struct
- removeFields(data, remove)
- m := filterFields(data, filter)
- if m {
- replaceFields(data, replace, compiledRegex)
- }
- } else if val.Kind() == reflect.Struct {
- // It's a struct, we need to work with a pointer
- // Create a copy we can modify
- ptrToNewCopy := reflect.New(val.Type())
- ptrToNewCopy.Elem().Set(val)
-
- // Modify the copy
- removeFields(ptrToNewCopy.Interface(), remove)
- m := filterFields(ptrToNewCopy.Interface(), filter)
-
- if m {
- replaceFields(ptrToNewCopy.Interface(), replace, compiledRegex)
- data = ptrToNewCopy.Elem().Interface()
- }
- } else {
- fmt.Printf("Skipping element not a struct or pointer to struct\n")
- }
-
- return data
-}
-
-func All(data []interface{}, filters, removes, replaces []string, path string) []interface{} {
- filter := [][]string{}
- for _, v := range filters {
- target, match := splitFilterString(v)
- filter = append(filter, []string{target, match})
- }
- // !ISSUE: Load file first
- replace := [][]string{}
- compiledRegex := []*regexp.Regexp{}
- for _, v := range replaces {
- if strings.HasSuffix(v, ".rf") {
- target, fp := splitFilePath(v)
- fb, _ := os.ReadFile(filepath.Join(path, fp))
- f := string(fb)
-
- lines := strings.Split(f, "\n")
-
- for _, l := range lines {
- // Add the target back to the lines
- // the .rf file should only contain the /match/replace/ statements
- if len(strings.TrimSpace(l)) != 0 && strings.TrimSpace(l)[0] != '#' {
- target, match, replacement := splitReplaceString(target + "=" + l)
- replace = append(replace, []string{target, match, replacement})
- re, err := regexp.Compile(match) // Compile the regex.
- if err != nil {
- panic(err)
- }
- compiledRegex = append(compiledRegex, re)
- }
- }
- } else {
- target, match, replacement := splitReplaceString(v)
- replace = append(replace, []string{target, match, replacement})
- re, err := regexp.Compile(match) // Compile the regex.
- if err != nil {
- panic(err)
- }
- compiledRegex = append(compiledRegex, re)
- }
+ target, match, replacement := splitReplaceString(v)
+ replace = append(replace, []string{target, match, replacement})
@@ -146 +39 @@ func All(data []interface{}, filters, removes, replaces []string, path string) [
- replaceFields(data[i], replace, compiledRegex)
+ replaceFields(data[i], replace)
@@ -160 +53 @@ func All(data []interface{}, filters, removes, replaces []string, path string) [
- replaceFields(ptrToNewCopy.Interface(), replace, compiledRegex)
+ replaceFields(ptrToNewCopy.Interface(), replace)
@@ -277 +170 @@ func filterFields(data interface{}, query [][]string) bool {
-func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.Regexp) {
+func replaceFields(data interface{}, query [][]string) bool {
@@ -282,0 +176 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
+ return false
@@ -286,0 +181 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
+ matches := false // Track if any replacements were made
@@ -295 +190 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
- for b, v := range query {
+ for _, v := range query {
@@ -298 +193 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
- rep := unescape(v[2])
+ rep := v[2]
@@ -309 +204,3 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
- replaceFields(field.Addr().Interface(), nestedQuery, compiledRegex)
+ if replaceFields(field.Addr().Interface(), nestedQuery) {
+ matches = true // Track if nested call made a replacement
+ }
@@ -313,2 +210,5 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
- re := compiledRegex[b]
-
+ re, err := regexp.Compile(exp) // Compile the regex.
+ if err != nil {
+ fmt.Printf("replaceFields: invalid regex: %s, error: %v\n", exp, err)
+ continue // Skip this query item if the regex is invalid.
+ }
@@ -331,0 +232 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
+ matches = true
@@ -346,0 +248 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
+ matches = true // Indicate a replacement was made.
@@ -351,32 +253 @@ func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.R
-}
-
-func unescape(str string) string {
- return strings.ReplaceAll(str, `\/`, "/")
-}
-
-func splitFilePath(str string) (string, string) {
- var path, target string
- targetDone := false
-
- escaped := false
-
- for _, v := range str {
-
- if !targetDone {
- if v != 61 || (v == 61 && escaped) {
- target += string(v)
- } else if !escaped {
- targetDone = true
- }
- } else if targetDone {
- path += string(v)
- }
- if escaped {
- escaped = false
- }
- if v == 92 {
- escaped = true
- }
- }
- return target, path
-
+ return matches // Return whether any replacements occurred.
package search
import (
"fmt"
"os"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
)
func One(data interface{}, filters, removes, replaces []string, path string) interface{} {
filter := [][]string{}
for _, v := range filters {
target, match := splitFilterString(v)
filter = append(filter, []string{target, match})
}
// !ISSUE: Load file first
replace := [][]string{}
compiledRegex := []*regexp.Regexp{}
for _, v := range replaces {
if strings.HasSuffix(v, ".rf") {
target, fp := splitFilePath(v)
fb, _ := os.ReadFile(filepath.Join(path, fp))
f := string(fb)
lines := strings.Split(f, "\n")
for _, l := range lines {
// Add the target back to the lines
// the .rf file should only contain the /match/replace/ statements
if len(strings.TrimSpace(l)) != 0 && strings.TrimSpace(l)[0] != '#' {
target, match, replacement := splitReplaceString(target + "=" + l)
replace = append(replace, []string{target, match, replacement})
re, err := regexp.Compile(match) // Compile the regex.
if err != nil {
panic(err)
}
compiledRegex = append(compiledRegex, re)
}
}
} else {
target, match, replacement := splitReplaceString(v)
replace = append(replace, []string{target, match, replacement})
re, err := regexp.Compile(match) // Compile the regex.
if err != nil {
panic(err)
}
compiledRegex = append(compiledRegex, re)
}
}
remove := []string{}
for _, v := range removes {
remove = append(remove, v)
}
val := reflect.ValueOf(data)
if val.Kind() == reflect.Ptr && val.Elem().Kind() == reflect.Struct {
// Already a pointer to struct
removeFields(data, remove)
m := filterFields(data, filter)
if m {
replaceFields(data, replace, compiledRegex)
}
} else if val.Kind() == reflect.Struct {
// It's a struct, we need to work with a pointer
// Create a copy we can modify
ptrToNewCopy := reflect.New(val.Type())
ptrToNewCopy.Elem().Set(val)
// Modify the copy
removeFields(ptrToNewCopy.Interface(), remove)
m := filterFields(ptrToNewCopy.Interface(), filter)
if m {
replaceFields(ptrToNewCopy.Interface(), replace, compiledRegex)
data = ptrToNewCopy.Elem().Interface()
}
} else {
fmt.Printf("Skipping element not a struct or pointer to struct\n")
}
return data
}
func All(data []interface{}, filters, removes, replaces []string, path string) []interface{} {
filter := [][]string{}
for _, v := range filters {
target, match := splitFilterString(v)
filter = append(filter, []string{target, match})
}
// !ISSUE: Load file first
replace := [][]string{}
compiledRegex := []*regexp.Regexp{}
for _, v := range replaces {
if strings.HasSuffix(v, ".rf") {
target, fp := splitFilePath(v)
fb, _ := os.ReadFile(filepath.Join(path, fp))
f := string(fb)
lines := strings.Split(f, "\n")
for _, l := range lines {
// Add the target back to the lines
// the .rf file should only contain the /match/replace/ statements
if len(strings.TrimSpace(l)) != 0 && strings.TrimSpace(l)[0] != '#' {
target, match, replacement := splitReplaceString(target + "=" + l)
replace = append(replace, []string{target, match, replacement})
re, err := regexp.Compile(match) // Compile the regex.
if err != nil {
panic(err)
}
compiledRegex = append(compiledRegex, re)
}
}
} else {
target, match, replacement := splitReplaceString(v)
replace = append(replace, []string{target, match, replacement})
re, err := regexp.Compile(match) // Compile the regex.
if err != nil {
panic(err)
}
compiledRegex = append(compiledRegex, re)
}
}
remove := []string{}
for _, v := range removes {
remove = append(remove, v)
}
grouped := []interface{}{}
for i := range data {
val := reflect.ValueOf(data[i])
if val.Kind() == reflect.Ptr && val.Elem().Kind() == reflect.Struct {
// Already a pointer to struct
removeFields(data[i], remove)
m := filterFields(data[i], filter)
if m {
replaceFields(data[i], replace, compiledRegex)
grouped = append(grouped, data[i])
}
} else if val.Kind() == reflect.Struct {
// It's a struct, we need to work with a pointer
// Create a copy we can modify
ptrToNewCopy := reflect.New(val.Type())
ptrToNewCopy.Elem().Set(val)
// Modify the copy
removeFields(ptrToNewCopy.Interface(), remove)
m := filterFields(ptrToNewCopy.Interface(), filter)
if m {
replaceFields(ptrToNewCopy.Interface(), replace, compiledRegex)
data[i] = ptrToNewCopy.Elem().Interface()
grouped = append(grouped, data[i])
}
} else {
fmt.Printf("Skipping element at index %d: not a struct or pointer to struct\n", i)
}
}
return grouped
}
func removeFields(data interface{}, query []string) {
val := reflect.ValueOf(data)
if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
fmt.Println("removeFields: data must be a pointer to a struct")
return
}
val = val.Elem() // Get the struct value the pointer points to
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
name := fieldType.Name
// Check if this field needs to be zeroed
for _, v := range query {
if strings.Contains(v, ".") {
// Handle nested fields
vs := strings.Split(v, ".")
if strings.EqualFold(name, strings.TrimSpace(vs[0])) {
remainingPath := strings.Join(vs[1:], ".")
if field.Kind() == reflect.Ptr && !field.IsNil() && field.Elem().Kind() == reflect.Struct {
// Field is a pointer to struct
removeFields(field.Interface(), []string{remainingPath})
} else if field.Kind() == reflect.Struct {
if field.CanAddr() {
// Field is a struct that we can get address of
removeFields(field.Addr().Interface(), []string{remainingPath})
}
}
}
} else if strings.EqualFold(name, strings.TrimSpace(v)) {
// Zero out this field
if field.CanSet() {
field.Set(reflect.Zero(field.Type()))
} else {
fmt.Printf("Cannot set field %s\n", name)
}
}
}
// Recursively process nested structs (not already processed with dot notation)
if field.Kind() == reflect.Ptr && !field.IsNil() && field.Elem().Kind() == reflect.Struct {
removeFields(field.Interface(), query)
} else if field.Kind() == reflect.Struct {
if field.CanAddr() {
removeFields(field.Addr().Interface(), query)
}
}
}
}
func filterFields(data interface{}, query [][]string) bool {
if len(query) == 0 {
return true
}
val := reflect.ValueOf(data)
// Ensure we're working with a pointer to a struct
if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
fmt.Println("filterFields: data must be a pointer to a struct")
return false
}
val = val.Elem()
typ := val.Type()
matches := false
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
name := fieldType.Name
for _, v := range query {
key := v[0]
exp := v[1]
if strings.Contains(key, ".") {
vs := strings.Split(key, ".")
if strings.EqualFold(name, strings.TrimSpace(vs[0])) {
if field.Kind() == reflect.Struct && field.CanAddr() {
if filterFields(field.Addr().Interface(), [][]string{[]string{strings.Join(vs[1:], "."), exp}}) {
matches = true
break
}
}
}
} else if strings.EqualFold(name, strings.TrimSpace(key)) {
str := []byte(fmt.Sprintf("%v", field.Interface()))
matches, _ = regexp.Match(exp, str)
if matches {
break
}
}
}
if matches {
break
}
}
return matches
}
func replaceFields(data interface{}, query [][]string, compiledRegex []*regexp.Regexp) {
val := reflect.ValueOf(data)
// Ensure we're working with a pointer to a struct
if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
fmt.Println("replaceFields: data must be a pointer to a struct")
}
val = val.Elem() // Get the value the pointer points to.
typ := val.Type()
// Iterate over the fields of the struct.
for i := 0; i < val.NumField(); i++ {
field := val.Field(i) // The field's value.
fieldType := typ.Field(i) // The field's type.
name := fieldType.Name // The field's name.
// Iterate over the query items.
for b, v := range query {
key := v[0]
exp := v[1]
rep := unescape(v[2])
// Handle nested fields (e.g., "Nested.Field").
if strings.Contains(key, ".") {
parts := strings.Split(key, ".")
if strings.EqualFold(name, strings.TrimSpace(parts[0])) {
// Check if the field is a struct and addressable.
if field.Kind() == reflect.Struct && field.CanAddr() {
// Recursively call replaceFields on the nested struct.
nestedQuery := [][]string{
{strings.Join(parts[1:], "."), exp, rep}, // Pass the rest of the path.
}
replaceFields(field.Addr().Interface(), nestedQuery, compiledRegex)
}
}
} else if strings.EqualFold(name, strings.TrimSpace(key)) && field.CanSet() {
re := compiledRegex[b]
if field.Kind() == reflect.Slice {
sliceVal := reflect.ValueOf(field.Interface())
newSlice := reflect.MakeSlice(field.Type(), sliceVal.Len(), sliceVal.Len())
for j := 0; j < sliceVal.Len(); j++ {
sliceElem := sliceVal.Index(j)
str := fmt.Sprintf("%v", sliceElem.Interface())
newValueStr := re.ReplaceAllString(str, rep)
newValue, err := convertStringToType(newValueStr, sliceElem.Kind())
if err != nil {
fmt.Printf("replaceFields: error converting '%s' to %s for slice element of field '%s': %v\n", newValueStr, sliceElem.Type(), name, err)
continue // Skip on error, or handle as appropriate
}
newSlice.Index(j).Set(reflect.ValueOf(newValue).Convert(sliceElem.Type()))
}
field.Set(newSlice)
} else {
// Field name matches, and the field is settable.
str := fmt.Sprintf("%v", field.Interface()) // Convert field value to string.
newValueStr := re.ReplaceAllString(str, rep) // Apply the replacement.
// Convert the new string value back to the field's original type.
newValue, err := convertStringToType(newValueStr, field.Kind())
if err != nil {
fmt.Printf("replaceFields: error converting '%s' to %s for field '%s': %v\n", newValueStr, field.Type(), name, err)
continue // Skip if conversion fails.
}
field.Set(reflect.ValueOf(newValue)) // Set the field's new value.
}
}
}
}
}
func unescape(str string) string {
return strings.ReplaceAll(str, `\/`, "/")
}
func splitFilePath(str string) (string, string) {
var path, target string
targetDone := false
escaped := false
for _, v := range str {
if !targetDone {
if v != 61 || (v == 61 && escaped) {
target += string(v)
} else if !escaped {
targetDone = true
}
} else if targetDone {
path += string(v)
}
if escaped {
escaped = false
}
if v == 92 {
escaped = true
}
}
return target, path
}
func splitFilterString(str string) (string, string) {
var target, match string
targetDone, matchDone := false, false
escaped := false
slashCount := 0
for _, v := range str {
if !targetDone {
if v != 61 || (v == 61 && escaped) {
target += string(v)
} else if !escaped {
targetDone = true
}
} else if targetDone && !matchDone && slashCount == 1 {
if v != 47 || (v == 47 && escaped) {
match += string(v)
} else {
matchDone = true
}
} else if slashCount == 2 && targetDone && matchDone {
break
}
if v == 47 && !escaped {
slashCount++
}
if escaped {
escaped = false
}
if v == 92 {
escaped = true
}
}
return target, match
}
func splitReplaceString(str string) (string, string, string) {
var target, match, replace string
targetDone, matchDone, replaceDone := false, false, false
escaped := false
slashCount := 0
for _, v := range str {
if !targetDone {
if v != 61 || (v == 61 && escaped) {
target += string(v)
} else if !escaped {
targetDone = true
}
} else if targetDone && !matchDone && slashCount == 1 {
if v != 47 || (v == 47 && escaped) {
match += string(v)
} else {
matchDone = true
}
} else if targetDone && matchDone && !replaceDone && slashCount == 2 {
if v != 47 || (v == 47 && escaped) {
replace += string(v)
} else {
replaceDone = true
}
} else if slashCount == 3 && targetDone && matchDone && replaceDone {
break
}
if v == 47 && !escaped {
slashCount++
}
if escaped {
escaped = false
}
if v == 92 {
escaped = true
}
}
return target, match, replace
}
func convertStringToType(s string, kind reflect.Kind) (interface{}, error) {
switch kind {
case reflect.String:
return s, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return nil, err
}
// Further convert to the specific integer type if necessary.
switch kind {
case reflect.Int8:
return int8(i), nil
case reflect.Int16:
return int16(i), nil
case reflect.Int32:
return int32(i), nil
case reflect.Int64:
return i, nil
case reflect.Int:
return int(i), nil
}
return i, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
u, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return nil, err
}
switch kind {
case reflect.Uint8:
return uint8(u), nil
case reflect.Uint16:
return uint16(u), nil
case reflect.Uint32:
return uint32(u), nil
case reflect.Uint64:
return u, nil
case reflect.Uint:
return uint(u), nil
}
return u, nil
case reflect.Float32, reflect.Float64:
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, err
}
if kind == reflect.Float32 {
return float32(f), nil
}
return f, nil
case reflect.Bool:
b, err := strconv.ParseBool(s)
if err != nil {
return nil, err
}
return b, nil
default:
return nil, fmt.Errorf("unsupported type: %v", kind)
}
}