package puppy import ( "errors" "fmt" "net/http" "net/url" "regexp" "strconv" "strings" "time" ) type SearchField int type StrComparer int type strFieldGetter func(req *ProxyRequest) ([]string, error) type kvFieldGetter func(req *ProxyRequest) ([]*PairValue, error) type RequestChecker func(req *ProxyRequest) bool // Searchable fields const ( FieldAll SearchField = iota FieldRequestBody FieldResponseBody FieldAllBody FieldWSMessage FieldRequestHeaders FieldResponseHeaders FieldBothHeaders FieldMethod FieldHost FieldPath FieldURL FieldStatusCode FieldTag FieldBothParam FieldURLParam FieldPostParam FieldResponseCookie FieldRequestCookie FieldBothCookie FieldAfter FieldBefore FieldTimeRange FieldInvert FieldId ) // Operators for string values const ( StrIs StrComparer = iota StrContains StrContainsRegexp StrLengthGreaterThan StrLengthLessThan StrLengthEqualTo ) // A struct representing the data to be searched for a pair such as a header or url param type PairValue struct { key string value string } // A list of queries. Will match if any queries match the request type QueryPhrase [][]interface{} // A list of phrases. Will match if all the phrases match the request type MessageQuery []QueryPhrase // A list of queries in string form. Will match if any queries match the request type StrQueryPhrase [][]string // A list of phrases in string form. Will match if all the phrases match the request type StrMessageQuery []StrQueryPhrase // Return a function that returns whether a request matches the given conditions func NewRequestChecker(args ...interface{}) (RequestChecker, error) { // Generates a request checker from the given search arguments if len(args) == 0 { return nil, errors.New("search requires a search field") } field, ok := args[0].(SearchField) if !ok { return nil, fmt.Errorf("first argument must hava a type of SearchField") } switch field { // Normal string fields case FieldAll, FieldRequestBody, FieldResponseBody, FieldAllBody, FieldWSMessage, FieldMethod, FieldHost, FieldPath, FieldStatusCode, FieldTag, FieldId: getter, err := createstrFieldGetter(field) if err != nil { return nil, fmt.Errorf("error performing search: %s", err.Error()) } if len(args) != 3 { return nil, errors.New("searches through strings must have one checker and one value") } comparer, ok := args[1].(StrComparer) if !ok { return nil, errors.New("comparer must be a StrComparer") } return genStrFieldChecker(getter, comparer, args[2]) // Normal key/value fields case FieldRequestHeaders, FieldResponseHeaders, FieldBothHeaders, FieldBothParam, FieldURLParam, FieldPostParam, FieldResponseCookie, FieldRequestCookie, FieldBothCookie: getter, err := createKvPairGetter(field) if err != nil { return nil, fmt.Errorf("error performing search: %s", err.Error()) } if len(args) == 3 { // Get comparer and value out of function arguments comparer, ok := args[1].(StrComparer) if !ok { return nil, errors.New("comparer must be a StrComparer") } // Create a strFieldGetter out of our key/value getter strgetter := func(req *ProxyRequest) ([]string, error) { pairs, err := getter(req) if err != nil { return nil, err } return pairsToStrings(pairs), nil } // return a str field checker using our new str getter return genStrFieldChecker(strgetter, comparer, args[2]) } else if len(args) == 5 { // Get comparer and value out of function arguments comparer1, ok := args[1].(StrComparer) if !ok { return nil, errors.New("first comparer must be a StrComparer") } val1, ok := args[2].(string) if !ok { return nil, errors.New("first val must be a list of bytes") } comparer2, ok := args[3].(StrComparer) if !ok { return nil, errors.New("second comparer must be a StrComparer") } val2, ok := args[4].(string) if !ok { return nil, errors.New("second val must be a list of bytes") } // Create a checker out of our getter, comparers, and vals return genKvFieldChecker(getter, comparer1, val1, comparer2, val2) } else { return nil, errors.New("invalid number of arguments for a key/value search") } // Other fields case FieldAfter: if len(args) != 2 { return nil, errors.New("searching by 'after' takes exactly on parameter") } val, ok := args[1].(time.Time) if !ok { return nil, errors.New("search argument must be a time.Time") } return func(req *ProxyRequest) bool { return req.StartDatetime.After(val) }, nil case FieldBefore: if len(args) != 2 { return nil, errors.New("searching by 'before' takes exactly one parameter") } val, ok := args[1].(time.Time) if !ok { return nil, errors.New("search argument must be a time.Time") } return func(req *ProxyRequest) bool { return req.StartDatetime.Before(val) }, nil case FieldTimeRange: if len(args) != 3 { return nil, errors.New("searching by time range takes exactly two parameters") } begin, ok := args[1].(time.Time) if !ok { return nil, errors.New("search arguments must be a time.Time") } end, ok := args[2].(time.Time) if !ok { return nil, errors.New("search arguments must be a time.Time") } return func(req *ProxyRequest) bool { return req.StartDatetime.After(begin) && req.StartDatetime.Before(end) }, nil case FieldInvert: orig, err := NewRequestChecker(args[1:]...) if err != nil { return nil, fmt.Errorf("error with query to invert: %s", err.Error()) } return func(req *ProxyRequest) bool { return !orig(req) }, nil default: return nil, errors.New("invalid field") } } func createstrFieldGetter(field SearchField) (strFieldGetter, error) { switch field { case FieldAll: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) strs = append(strs, string(req.FullMessage())) if req.ServerResponse != nil { strs = append(strs, string(req.ServerResponse.FullMessage())) } for _, wsm := range req.WSMessages { strs = append(strs, string(wsm.Message)) } return strs, nil }, nil case FieldRequestBody: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) strs = append(strs, string(req.BodyBytes())) return strs, nil }, nil case FieldResponseBody: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) if req.ServerResponse != nil { strs = append(strs, string(req.ServerResponse.BodyBytes())) } return strs, nil }, nil case FieldAllBody: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) strs = append(strs, string(req.BodyBytes())) if req.ServerResponse != nil { strs = append(strs, string(req.ServerResponse.BodyBytes())) } return strs, nil }, nil case FieldWSMessage: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) for _, wsm := range req.WSMessages { strs = append(strs, string(wsm.Message)) } return strs, nil }, nil case FieldMethod: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) strs = append(strs, req.Method) return strs, nil }, nil case FieldHost: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) strs = append(strs, req.DestHost) strs = append(strs, req.Host) return strs, nil }, nil case FieldPath: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) strs = append(strs, req.URL.Path) return strs, nil }, nil case FieldURL: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) strs = append(strs, req.FullURL().String()) return strs, nil }, nil case FieldStatusCode: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 0) if req.ServerResponse != nil { strs = append(strs, strconv.Itoa(req.ServerResponse.StatusCode)) } return strs, nil }, nil case FieldTag: return func(req *ProxyRequest) ([]string, error) { return req.Tags(), nil }, nil case FieldId: return func(req *ProxyRequest) ([]string, error) { strs := make([]string, 1) strs[0] = req.DbId return strs, nil }, nil default: return nil, errors.New("field is not a string") } } func genStrChecker(cmp StrComparer, argval interface{}) (func(str string) bool, error) { switch cmp { case StrContains: val, ok := argval.(string) if !ok { return nil, errors.New("argument must be a string") } return func(str string) bool { if strings.Contains(str, val) { return true } return false }, nil case StrIs: val, ok := argval.(string) if !ok { return nil, errors.New("argument must be a string") } return func(str string) bool { if str == val { return true } return false }, nil case StrContainsRegexp: val, ok := argval.(string) if !ok { return nil, errors.New("argument must be a string") } regex, err := regexp.Compile(string(val)) if err != nil { return nil, fmt.Errorf("could not compile regular expression: %s", err.Error()) } return func(str string) bool { return regex.MatchString(string(str)) }, nil case StrLengthGreaterThan: val, ok := argval.(int) if !ok { return nil, errors.New("argument must be an integer") } return func(str string) bool { if len(str) > val { return true } return false }, nil case StrLengthLessThan: val, ok := argval.(int) if !ok { return nil, errors.New("argument must be an integer") } return func(str string) bool { if len(str) < val { return true } return false }, nil case StrLengthEqualTo: val, ok := argval.(int) if !ok { return nil, errors.New("argument must be an integer") } return func(str string) bool { if len(str) == val { return true } return false }, nil default: return nil, errors.New("invalid comparer") } } func genStrFieldChecker(strGetter strFieldGetter, cmp StrComparer, val interface{}) (RequestChecker, error) { getter := strGetter comparer, err := genStrChecker(cmp, val) if err != nil { return nil, err } return func(req *ProxyRequest) bool { strs, err := getter(req) if err != nil { panic(err) } for _, str := range strs { if comparer(str) { return true } } return false }, nil } func pairValuesFromHeader(header http.Header) []*PairValue { // Returns a list of pair values from a http.Header pairs := make([]*PairValue, 0) for k, vs := range header { for _, v := range vs { pair := &PairValue{string(k), string(v)} pairs = append(pairs, pair) } } return pairs } func pairValuesFromURLQuery(values url.Values) []*PairValue { // Returns a list of pair values from a http.Header pairs := make([]*PairValue, 0) for k, vs := range values { for _, v := range vs { pair := &PairValue{string(k), string(v)} pairs = append(pairs, pair) } } return pairs } func pairValuesFromCookies(cookies []*http.Cookie) []*PairValue { pairs := make([]*PairValue, 0) for _, c := range cookies { pair := &PairValue{string(c.Name), string(c.Value)} pairs = append(pairs, pair) } return pairs } func pairsToStrings(pairs []*PairValue) []string { // Converts a list of pairs into a list of strings containing all keys and values // k1: v1, k2: v2 -> ["k1", "v1", "k2", "v2"] strs := make([]string, 0) for _, p := range pairs { strs = append(strs, p.key) strs = append(strs, p.value) } return strs } func createKvPairGetter(field SearchField) (kvFieldGetter, error) { switch field { case FieldRequestHeaders: return func(req *ProxyRequest) ([]*PairValue, error) { return pairValuesFromHeader(req.Header), nil }, nil case FieldResponseHeaders: return func(req *ProxyRequest) ([]*PairValue, error) { var pairs []*PairValue if req.ServerResponse != nil { pairs = pairValuesFromHeader(req.ServerResponse.Header) } else { pairs = make([]*PairValue, 0) } return pairs, nil }, nil case FieldBothHeaders: return func(req *ProxyRequest) ([]*PairValue, error) { pairs := pairValuesFromHeader(req.Header) if req.ServerResponse != nil { pairs = append(pairs, pairValuesFromHeader(req.ServerResponse.Header)...) } return pairs, nil }, nil case FieldBothParam: return func(req *ProxyRequest) ([]*PairValue, error) { pairs := pairValuesFromURLQuery(req.URL.Query()) params, err := req.PostParameters() if err == nil { pairs = append(pairs, pairValuesFromURLQuery(params)...) } return pairs, nil }, nil case FieldURLParam: return func(req *ProxyRequest) ([]*PairValue, error) { return pairValuesFromURLQuery(req.URL.Query()), nil }, nil case FieldPostParam: return func(req *ProxyRequest) ([]*PairValue, error) { params, err := req.PostParameters() if err != nil { return nil, err } return pairValuesFromURLQuery(params), nil }, nil case FieldResponseCookie: return func(req *ProxyRequest) ([]*PairValue, error) { pairs := make([]*PairValue, 0) if req.ServerResponse != nil { cookies := req.ServerResponse.Cookies() pairs = append(pairs, pairValuesFromCookies(cookies)...) } return pairs, nil }, nil case FieldRequestCookie: return func(req *ProxyRequest) ([]*PairValue, error) { return pairValuesFromCookies(req.Cookies()), nil }, nil case FieldBothCookie: return func(req *ProxyRequest) ([]*PairValue, error) { pairs := pairValuesFromCookies(req.Cookies()) if req.ServerResponse != nil { cookies := req.ServerResponse.Cookies() pairs = append(pairs, pairValuesFromCookies(cookies)...) } return pairs, nil }, nil default: return nil, errors.New("not implemented") } } func genKvFieldChecker(kvGetter kvFieldGetter, cmp1 StrComparer, val1 string, cmp2 StrComparer, val2 string) (RequestChecker, error) { getter := kvGetter cmpfunc1, err := genStrChecker(cmp1, val1) if err != nil { return nil, err } cmpfunc2, err := genStrChecker(cmp2, val2) if err != nil { return nil, err } return func(req *ProxyRequest) bool { pairs, err := getter(req) if err != nil { return false } for _, p := range pairs { if cmpfunc1(p.key) && cmpfunc2(p.value) { return true } } return false }, nil } func checkerFromPhrase(phrase QueryPhrase) (RequestChecker, error) { checkers := make([]RequestChecker, len(phrase)) for i, args := range phrase { newChecker, err := NewRequestChecker(args...) if err != nil { return nil, fmt.Errorf("error with search %d: %s", i, err.Error()) } checkers[i] = newChecker } ret := func(req *ProxyRequest) bool { for _, checker := range checkers { if checker(req) { return true } } return false } return ret, nil } // Creates a RequestChecker from a MessageQuery func CheckerFromMessageQuery(query MessageQuery) (RequestChecker, error) { checkers := make([]RequestChecker, len(query)) for i, phrase := range query { newChecker, err := checkerFromPhrase(phrase) if err != nil { return nil, fmt.Errorf("error with phrase %d: %s", i, err.Error()) } checkers[i] = newChecker } ret := func(req *ProxyRequest) bool { for _, checker := range checkers { if !checker(req) { return false } } return true } return ret, nil } /* StringSearch conversions */ func fieldGoToString(field SearchField) (string, error) { switch field { case FieldAll: return "all", nil case FieldRequestBody: return "reqbody", nil case FieldResponseBody: return "rspbody", nil case FieldAllBody: return "body", nil case FieldWSMessage: return "wsmessage", nil case FieldRequestHeaders: return "reqheader", nil case FieldResponseHeaders: return "rspheader", nil case FieldBothHeaders: return "header", nil case FieldMethod: return "method", nil case FieldHost: return "host", nil case FieldPath: return "path", nil case FieldURL: return "url", nil case FieldStatusCode: return "statuscode", nil case FieldBothParam: return "param", nil case FieldURLParam: return "urlparam", nil case FieldPostParam: return "postparam", nil case FieldResponseCookie: return "rspcookie", nil case FieldRequestCookie: return "reqcookie", nil case FieldBothCookie: return "cookie", nil case FieldTag: return "tag", nil case FieldAfter: return "after", nil case FieldBefore: return "before", nil case FieldTimeRange: return "timerange", nil case FieldInvert: return "invert", nil case FieldId: return "dbid", nil default: return "", errors.New("invalid field") } } func fieldStrToGo(field string) (SearchField, error) { switch strings.ToLower(field) { case "all": return FieldAll, nil case "reqbody", "reqbd", "qbd", "qdata", "qdt": return FieldRequestBody, nil case "rspbody", "rspbd", "sbd", "sdata", "sdt": return FieldResponseBody, nil case "body", "bd", "data", "dt": return FieldAllBody, nil case "wsmessage", "wsm": return FieldWSMessage, nil case "reqheader", "reqhd", "qhd": return FieldRequestHeaders, nil case "rspheader", "rsphd", "shd": return FieldResponseHeaders, nil case "header", "hd": return FieldBothHeaders, nil case "method", "verb", "vb": return FieldMethod, nil case "host", "domain", "hs", "dm": return FieldHost, nil case "path", "pt": return FieldPath, nil case "url": return FieldURL, nil case "statuscode", "sc": return FieldStatusCode, nil case "param", "pm": return FieldBothParam, nil case "urlparam", "uparam": return FieldURLParam, nil case "postparam", "pparam": return FieldPostParam, nil case "rspcookie", "rspck", "sck": return FieldResponseCookie, nil case "reqcookie", "reqck", "qck": return FieldRequestCookie, nil case "cookie", "ck": return FieldBothCookie, nil case "tag": return FieldTag, nil case "after", "af": return FieldAfter, nil case "before", "b4": return FieldBefore, nil case "timerange": return FieldTimeRange, nil case "invert", "inv": return FieldInvert, nil case "dbid": return FieldId, nil default: return 0, fmt.Errorf("invalid field: %s", field) } } // Converts a StrComparer and a value into a comparer and value that can be used in string queries func cmpValGoToStr(comparer StrComparer, val interface{}) (string, string, error) { var cmpStr string switch comparer { case StrIs: cmpStr = "is" val, ok := val.(string) if !ok { return "", "", errors.New("val must be a string") } return cmpStr, val, nil case StrContains: cmpStr = "contains" val, ok := val.(string) if !ok { return "", "", errors.New("val must be a string") } return cmpStr, val, nil case StrContainsRegexp: cmpStr = "containsregexp" val, ok := val.(string) if !ok { return "", "", errors.New("val must be a string") } return cmpStr, val, nil case StrLengthGreaterThan: cmpStr = "lengt" val, ok := val.(int) if !ok { return "", "", errors.New("val must be an int") } return cmpStr, strconv.Itoa(val), nil case StrLengthLessThan: cmpStr = "lenlt" val, ok := val.(int) if !ok { return "", "", errors.New("val must be an int") } return cmpStr, strconv.Itoa(val), nil case StrLengthEqualTo: cmpStr = "leneq" val, ok := val.(int) if !ok { return "", "", errors.New("val must be an int") } return cmpStr, strconv.Itoa(val), nil default: return "", "", errors.New("invalid comparer") } } func cmpValStrToGo(strArgs []string) (StrComparer, interface{}, error) { if len(strArgs) != 2 { return 0, "", fmt.Errorf("parsing a comparer/val requires one comparer and one value. Got %d arguments.", len(strArgs)) } switch strArgs[0] { case "is": return StrIs, strArgs[1], nil case "contains", "ct": return StrContains, strArgs[1], nil case "containsregexp", "ctr": return StrContainsRegexp, strArgs[1], nil case "lengt": i, err := strconv.Atoi(strArgs[1]) if err != nil { return 0, nil, err } return StrLengthGreaterThan, i, nil case "lenlt": i, err := strconv.Atoi(strArgs[1]) if err != nil { return 0, nil, err } return StrLengthLessThan, i, nil case "leneq": i, err := strconv.Atoi(strArgs[1]) if err != nil { return 0, nil, err } return StrLengthEqualTo, i, nil default: return 0, "", fmt.Errorf("invalid comparer: %s", strArgs[0]) } } func CheckArgsStrToGo(strArgs []string) ([]interface{}, error) { args := make([]interface{}, 0) if len(strArgs) == 0 { return nil, errors.New("missing field") } // Parse the field field, err := fieldStrToGo(strArgs[0]) if err != nil { return nil, err } args = append(args, field) remaining := strArgs[1:] // Parse the query arguments switch args[0] { // Normal string fields case FieldAll, FieldRequestBody, FieldResponseBody, FieldAllBody, FieldWSMessage, FieldMethod, FieldHost, FieldPath, FieldStatusCode, FieldTag, FieldId: if len(remaining) != 2 { return nil, errors.New("string field searches require one comparer and one value") } cmp, val, err := cmpValStrToGo(remaining) if err != nil { return nil, err } args = append(args, cmp) args = append(args, val) // Normal key/value fields case FieldRequestHeaders, FieldResponseHeaders, FieldBothHeaders, FieldBothParam, FieldURLParam, FieldPostParam, FieldResponseCookie, FieldRequestCookie, FieldBothCookie: if len(remaining) == 2 { cmp, val, err := cmpValStrToGo(remaining) if err != nil { return nil, err } args = append(args, cmp) args = append(args, val) } else if len(remaining) == 4 { cmp, val, err := cmpValStrToGo(remaining[0:2]) if err != nil { return nil, err } args = append(args, cmp) args = append(args, val) cmp, val, err = cmpValStrToGo(remaining[2:4]) if err != nil { return nil, err } args = append(args, cmp) args = append(args, val) } else { return nil, errors.New("key/value field searches require either one comparer and one value or two comparer/value pairs") } // Other fields case FieldAfter, FieldBefore: if len(remaining) != 1 { return nil, errors.New("before/after take exactly one argument") } nanoseconds, err := strconv.ParseInt(remaining[0], 10, 64) if err != nil { return nil, errors.New("error parsing time") } timeVal := time.Unix(0, nanoseconds) args = append(args, timeVal) case FieldTimeRange: if len(remaining) != 2 { return nil, errors.New("time range takes exactly two arguments") } startNanoseconds, err := strconv.ParseInt(remaining[0], 10, 64) if err != nil { return nil, errors.New("error parsing start time") } startTimeVal := time.Unix(0, startNanoseconds) args = append(args, startTimeVal) endNanoseconds, err := strconv.ParseInt(remaining[1], 10, 64) if err != nil { return nil, errors.New("error parsing end time") } endTimeVal := time.Unix(0, endNanoseconds) args = append(args, endTimeVal) case FieldInvert: remainingArgs, err := CheckArgsStrToGo(remaining) if err != nil { return nil, fmt.Errorf("error with query to invert: %s", err.Error()) } args = append(args, remainingArgs...) default: return nil, fmt.Errorf("field not yet implemented: %s", strArgs[0]) } return args, nil } func CheckArgsGoToStr(args []interface{}) ([]string, error) { if len(args) == 0 { return nil, errors.New("no arguments") } retargs := make([]string, 0) field, ok := args[0].(SearchField) if !ok { return nil, errors.New("first argument is not a field") } strField, err := fieldGoToString(field) if err != nil { return nil, err } retargs = append(retargs, strField) switch field { case FieldAll, FieldRequestBody, FieldResponseBody, FieldAllBody, FieldWSMessage, FieldMethod, FieldHost, FieldPath, FieldStatusCode, FieldTag, FieldId: if len(args) != 3 { return nil, errors.New("string fields require exactly two arguments") } comparer, ok := args[1].(StrComparer) if !ok { return nil, errors.New("comparer must be a StrComparer") } cmpStr, valStr, err := cmpValGoToStr(comparer, args[2]) if err != nil { return nil, err } retargs = append(retargs, cmpStr) retargs = append(retargs, valStr) return retargs, nil case FieldRequestHeaders, FieldResponseHeaders, FieldBothHeaders, FieldBothParam, FieldURLParam, FieldPostParam, FieldResponseCookie, FieldRequestCookie, FieldBothCookie: if len(args) == 3 { comparer, ok := args[1].(StrComparer) if !ok { return nil, errors.New("comparer must be a StrComparer") } cmpStr, valStr, err := cmpValGoToStr(comparer, args[2]) if err != nil { return nil, err } retargs = append(retargs, cmpStr) retargs = append(retargs, valStr) return retargs, nil } else if len(args) == 5 { comparer1, ok := args[1].(StrComparer) if !ok { return nil, errors.New("comparer1 must be a StrComparer") } cmpStr1, valStr1, err := cmpValGoToStr(comparer1, args[2]) if err != nil { return nil, err } retargs = append(retargs, cmpStr1) retargs = append(retargs, valStr1) comparer2, ok := args[1].(StrComparer) if !ok { return nil, errors.New("comparer2 must be a StrComparer") } cmpStr2, valStr2, err := cmpValGoToStr(comparer2, args[2]) if err != nil { return nil, err } retargs = append(retargs, cmpStr2) retargs = append(retargs, valStr2) return retargs, nil } else { return nil, errors.New("key/value queries take exactly two or four arguments") } case FieldAfter, FieldBefore: if len(args) != 2 { return nil, errors.New("before/after fields require exactly one argument") } time, ok := args[1].(time.Time) if !ok { return nil, errors.New("argument must have a type of time.Time") } nanoseconds := time.UnixNano() retargs = append(retargs, strconv.FormatInt(nanoseconds, 10)) return retargs, nil case FieldTimeRange: if len(args) != 3 { return nil, errors.New("time range fields require exactly two arguments") } time1, ok := args[1].(time.Time) if !ok { return nil, errors.New("arguments must have a type of time.Time") } nanoseconds1 := time1.UnixNano() retargs = append(retargs, strconv.FormatInt(nanoseconds1, 10)) time2, ok := args[2].(time.Time) if !ok { return nil, errors.New("arguments must have a type of time.Time") } nanoseconds2 := time2.UnixNano() retargs = append(retargs, strconv.FormatInt(nanoseconds2, 10)) return retargs, nil case FieldInvert: strs, err := CheckArgsGoToStr(args[1:]) if err != nil { return nil, err } retargs = append(retargs, strs...) return retargs, nil default: return nil, fmt.Errorf("invalid field") } } func strPhraseToGoPhrase(phrase StrQueryPhrase) (QueryPhrase, error) { goPhrase := make(QueryPhrase, len(phrase)) for i, strArgs := range phrase { var err error goPhrase[i], err = CheckArgsStrToGo(strArgs) if err != nil { return nil, fmt.Errorf("Error with argument set %d: %s", i, err.Error()) } } return goPhrase, nil } func goPhraseToStrPhrase(phrase QueryPhrase) (StrQueryPhrase, error) { strPhrase := make(StrQueryPhrase, len(phrase)) for i, goArgs := range phrase { var err error strPhrase[i], err = CheckArgsGoToStr(goArgs) if err != nil { return nil, fmt.Errorf("Error with argument set %d: %s", i, err.Error()) } } return strPhrase, nil } // Converts a StrMessageQuery into a MessageQuery func StrQueryToMsgQuery(query StrMessageQuery) (MessageQuery, error) { goQuery := make(MessageQuery, len(query)) for i, phrase := range query { var err error goQuery[i], err = strPhraseToGoPhrase(phrase) if err != nil { return nil, fmt.Errorf("Error with phrase %d: %s", i, err.Error()) } } return goQuery, nil } // Converts a MessageQuery into a StrMessageQuery func MsgQueryToStrQuery(query MessageQuery) (StrMessageQuery, error) { strQuery := make(StrMessageQuery, len(query)) for i, phrase := range query { var err error strQuery[i], err = goPhraseToStrPhrase(phrase) if err != nil { return nil, fmt.Errorf("Error with phrase %d: %s", i, err.Error()) } } return strQuery, nil }