package domain import ( "bytes" "encoding/base64" "net/url" "strings" ) func parseURL(rawURL string) (*url.URL, error) { u, err := url.ParseRequestURI(rawURL) if err != nil { return nil, InvalidURLError{ URL: rawURL, } } return u, nil } const ( cursorSeparator = "," cursorKeyValueSeparator = "=" ) func encodeCursor(s [][2]string) string { if len(s) == 0 { return "" } n := len(cursorSeparator) * (len(s) - 1) for _, el := range s { n += len(el[0]) + len(el[1]) + len(cursorKeyValueSeparator) } var b bytes.Buffer b.Grow(n) for _, el := range s { if b.Len() > 0 { _, _ = b.WriteString(cursorSeparator) } _, _ = b.WriteString(el[0]) _, _ = b.WriteString(cursorKeyValueSeparator) _, _ = b.WriteString(el[1]) } return base64.StdEncoding.EncodeToString(b.Bytes()) } const ( encodedCursorMinLength = 1 encodedCursorMaxLength = 1000 ) func decodeCursor(s string) (map[string]string, error) { if err := validateStringLen(s, encodedCursorMinLength, encodedCursorMaxLength); err != nil { return nil, err } decodedBytes, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, ErrInvalidCursor } decoded := string(decodedBytes) kvs := strings.Split(decoded, cursorSeparator) m := make(map[string]string, len(kvs)) for _, kv := range kvs { k, v, found := strings.Cut(kv, cursorKeyValueSeparator) if !found { return nil, ErrInvalidCursor } m[k] = v } return m, nil }