162 lines
3.1 KiB
Go
162 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
_ "embed"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"log/slog"
|
|
|
|
"github.com/fatih/color"
|
|
)
|
|
|
|
var findAll = flag.Bool("all", false, "Find all occurrences")
|
|
var findType = flag.String("type", "string", "The type of value to find. Must be one of 'string' or 'number'")
|
|
|
|
type JSON = interface{}
|
|
type JSONObject = map[string]interface{}
|
|
type JSONArray = []interface{}
|
|
|
|
func init() {
|
|
flag.Parse()
|
|
}
|
|
|
|
func main() {
|
|
var inputData []byte
|
|
|
|
if flag.NArg() == 1 || flag.Arg(1) == "-" {
|
|
stdin, err := io.ReadAll(os.Stdin)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
inputData = stdin
|
|
} else if flag.NArg() == 2 {
|
|
file, err := os.ReadFile(flag.Arg(1))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
inputData = file
|
|
} else {
|
|
fmt.Printf("Usage: %s [-all] [-type=string|number] <match> [<file> | -]\n", filepath.Base(os.Args[0]))
|
|
os.Exit(0)
|
|
}
|
|
|
|
data := make(JSONObject)
|
|
json.Unmarshal(inputData, &data)
|
|
switch *findType {
|
|
case "string":
|
|
// "EpToggle_PAYMENTINSURANCE_CAR_Description"
|
|
findValue(data, flag.Arg(0))
|
|
case "number":
|
|
parsedNum, err := strconv.ParseFloat(flag.Arg(0), 64)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
findValue(data, parsedNum)
|
|
}
|
|
}
|
|
|
|
func findValue[T comparable](haystack JSON, text T) {
|
|
switch x := haystack.(type) {
|
|
case JSONObject:
|
|
findInObject(x, text, "")
|
|
case JSONArray:
|
|
findInList(x, text, "")
|
|
default:
|
|
slog.Error("JSON is not a nested structure.")
|
|
}
|
|
}
|
|
|
|
func findInObject[T comparable](haystack JSONObject, value T, path string) {
|
|
for k, v := range haystack {
|
|
switch x := v.(type) {
|
|
case string:
|
|
if *findType != "string" {
|
|
continue
|
|
}
|
|
if stringContains(x, value) {
|
|
printPathAndMatch(path, x)
|
|
}
|
|
|
|
case float64:
|
|
if *findType != "number" {
|
|
continue
|
|
}
|
|
if floatEquals(x, value) {
|
|
printPathAndMatch(path, x)
|
|
}
|
|
case JSONObject:
|
|
findInObject(x, value, path+"."+k)
|
|
case []interface{}:
|
|
findInList(x, value, fmt.Sprintf("%s.%s", path, k))
|
|
}
|
|
}
|
|
}
|
|
|
|
func findInList[T comparable](list []interface{}, value T, path string) {
|
|
for i, v := range list {
|
|
switch x := v.(type) {
|
|
case string:
|
|
if *findType != "string" {
|
|
continue
|
|
}
|
|
if stringContains(x, value) {
|
|
printPathAndMatch(path, x)
|
|
}
|
|
|
|
case float64:
|
|
if *findType != "number" {
|
|
continue
|
|
}
|
|
if floatEquals(x, value) {
|
|
printPathAndMatch(path, x)
|
|
}
|
|
|
|
case JSONObject:
|
|
findInObject(x, value, fmt.Sprintf("%s[%d]", path, i))
|
|
case []interface{}:
|
|
findInList(x, value, fmt.Sprintf("%s[%d]", path, i))
|
|
}
|
|
}
|
|
}
|
|
|
|
func printPathAndMatch(path string, match interface{}) {
|
|
if strings.HasPrefix(path, ".") {
|
|
path = strings.Replace(path, ".", "", 1)
|
|
}
|
|
|
|
c := color.New(color.Bold)
|
|
c.Printf("%s", path)
|
|
fmt.Printf(": ")
|
|
c = color.New(color.FgHiBlack)
|
|
c.Printf("%v\n", match)
|
|
|
|
if !*findAll {
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
|
|
func stringContains[T comparable](haystackString string, match T) bool {
|
|
matchString, ok := any(match).(string)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return strings.Contains(haystackString, matchString)
|
|
}
|
|
|
|
func floatEquals[T comparable](haystackFloat float64, match T) bool {
|
|
matchFloat, ok := any(match).(float64)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return haystackFloat == matchFloat
|
|
}
|