326 lines
6.6 KiB
Go
326 lines
6.6 KiB
Go
package params
|
|
|
|
// {get, get first, get or} * {str, int, id, bool} * {list, not list} = 24 funcs
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
ErrKeyNotFound = errors.New("key(s) not found")
|
|
ErrIDInvalid = errors.New("invalid id")
|
|
ErrIDNotAnInt = errors.New("not an int")
|
|
ErrIDNotABool = errors.New("not an bool")
|
|
)
|
|
|
|
const IDSeparator = "-"
|
|
|
|
type IDType string
|
|
|
|
const (
|
|
// type values copied from subsonic
|
|
IDTypeArtist IDType = "ar"
|
|
IDTypeAlbum IDType = "al"
|
|
IDTypeTrack IDType = "tr"
|
|
)
|
|
|
|
type ID struct {
|
|
Type IDType
|
|
Value int
|
|
}
|
|
|
|
func IDArtist(id int) string { return fmt.Sprintf("%d-%s", id, IDTypeArtist) }
|
|
func IDAlbum(id int) string { return fmt.Sprintf("%d-%s", id, IDTypeAlbum) }
|
|
func IDTrack(id int) string { return fmt.Sprintf("%d-%s", id, IDTypeTrack) }
|
|
|
|
// ** begin type parsing, support {[],}{string,int,ID} => 6 types
|
|
|
|
func parse(values []string, i interface{}) error {
|
|
if len(values) == 0 {
|
|
return ErrKeyNotFound
|
|
}
|
|
var err error
|
|
switch v := i.(type) {
|
|
case *string:
|
|
*v, err = parseStr(values[0])
|
|
case *int:
|
|
*v, err = parseInt(values[0])
|
|
case *ID:
|
|
*v, err = parseID(values[0])
|
|
case *bool:
|
|
*v, err = parseBool(values[0])
|
|
case *[]string:
|
|
for _, val := range values {
|
|
parsed, err := parseStr(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*v = append(*v, parsed)
|
|
}
|
|
case *[]int:
|
|
for _, val := range values {
|
|
parsed, err := parseInt(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*v = append(*v, parsed)
|
|
}
|
|
case *[]ID:
|
|
for _, val := range values {
|
|
parsed, err := parseID(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*v = append(*v, parsed)
|
|
}
|
|
case *[]bool:
|
|
for _, val := range values {
|
|
parsed, err := parseBool(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*v = append(*v, parsed)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// ** begin parse funcs
|
|
|
|
func parseStr(in string) (string, error) {
|
|
return in, nil
|
|
}
|
|
|
|
func parseInt(in string) (int, error) {
|
|
if v, err := strconv.Atoi(in); err == nil {
|
|
return v, nil
|
|
}
|
|
return 0, ErrIDNotAnInt
|
|
}
|
|
|
|
func parseID(in string) (ID, error) {
|
|
parts := strings.Split(in, IDSeparator)
|
|
if len(parts) != 2 {
|
|
return ID{}, fmt.Errorf("bad separator: %w", ErrIDInvalid)
|
|
}
|
|
partType := parts[0]
|
|
partValue := parts[1]
|
|
val, err := parseInt(partValue)
|
|
if err != nil {
|
|
return ID{}, fmt.Errorf("%s: %w", partValue, err)
|
|
}
|
|
switch partType {
|
|
case string(IDTypeArtist):
|
|
return ID{Type: IDTypeArtist, Value: val}, nil
|
|
case string(IDTypeAlbum):
|
|
return ID{Type: IDTypeAlbum, Value: val}, nil
|
|
case string(IDTypeTrack):
|
|
return ID{Type: IDTypeTrack, Value: val}, nil
|
|
}
|
|
return ID{}, ErrIDInvalid
|
|
}
|
|
|
|
func parseBool(in string) (bool, error) {
|
|
if v, err := strconv.ParseBool(in); err == nil {
|
|
return v, nil
|
|
}
|
|
return false, ErrIDNotABool
|
|
}
|
|
|
|
type Params url.Values
|
|
|
|
func New(r *http.Request) Params {
|
|
// first load params from the url
|
|
params := r.URL.Query()
|
|
// also if there's any in the post body, use those too
|
|
if err := r.ParseForm(); err == nil {
|
|
for k, v := range r.Form {
|
|
params[k] = v
|
|
}
|
|
}
|
|
return Params(params)
|
|
}
|
|
|
|
func (p Params) get(key string) []string {
|
|
return p[key]
|
|
}
|
|
|
|
func (p Params) getFirst(keys []string) []string {
|
|
for _, k := range keys {
|
|
if v, ok := p[k]; ok {
|
|
return v
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ** begin str {get, get first, get or}
|
|
|
|
func (p Params) Get(key string) (string, error) {
|
|
var ret string
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirst(keys ...string) (string, error) {
|
|
var ret string
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOr(key string, or string) string {
|
|
var ret string
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|
|
|
|
// ** begin []str {get, get first, get or}
|
|
|
|
func (p Params) GetList(key string) ([]string, error) {
|
|
var ret []string
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirstList(keys ...string) ([]string, error) {
|
|
var ret []string
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOrList(key string, or []string) []string {
|
|
var ret []string
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|
|
|
|
// ** begin int {get, get first, get or}
|
|
|
|
func (p Params) GetInt(key string) (int, error) {
|
|
var ret int
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirstInt(keys ...string) (int, error) {
|
|
var ret int
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOrInt(key string, or int) int {
|
|
var ret int
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|
|
|
|
// ** begin []int {get, get first, get or}
|
|
|
|
func (p Params) GetIntList(key string) ([]int, error) {
|
|
var ret []int
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirstIntList(keys ...string) ([]int, error) {
|
|
var ret []int
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOrIntList(key string, or []int) []int {
|
|
var ret []int
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|
|
|
|
// ** begin ID {get, get first, get or}
|
|
|
|
func (p Params) GetID(key string) (ID, error) {
|
|
var ret ID
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetIDDefault() (ID, error) {
|
|
var ret ID
|
|
return ret, parse(p.get("id"), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirstID(keys ...string) (ID, error) {
|
|
var ret ID
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOrID(key string, or ID) ID {
|
|
var ret ID
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|
|
|
|
// ** begin []ID {get, get first, get or}
|
|
|
|
func (p Params) GetIDList(key string) ([]ID, error) {
|
|
var ret []ID
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirstIDList(keys ...string) ([]ID, error) {
|
|
var ret []ID
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOrIDList(key string, or []ID) []ID {
|
|
var ret []ID
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|
|
|
|
// ** begin bool {get, get first, get or}
|
|
|
|
func (p Params) GetBool(key string) (bool, error) {
|
|
var ret bool
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirstBool(keys ...string) (bool, error) {
|
|
var ret bool
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOrBool(key string, or bool) bool {
|
|
var ret bool
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|
|
|
|
// ** begin []bool {get, get first, get or}
|
|
|
|
func (p Params) GetBoolList(key string) ([]bool, error) {
|
|
var ret []bool
|
|
return ret, parse(p.get(key), &ret)
|
|
}
|
|
|
|
func (p Params) GetFirstBoolList(keys ...string) ([]bool, error) {
|
|
var ret []bool
|
|
return ret, parse(p.getFirst(keys), &ret)
|
|
}
|
|
|
|
func (p Params) GetOrBoolList(key string, or []bool) []bool {
|
|
var ret []bool
|
|
if err := parse(p.get(key), &ret); err == nil {
|
|
return ret
|
|
}
|
|
return or
|
|
}
|