add param parsing methods

related: #41
This commit is contained in:
sentriz
2020-04-25 17:37:54 +01:00
committed by Senan Kelly
parent 07e1c53d69
commit 4f519d71ed
5 changed files with 296 additions and 54 deletions

View File

@@ -50,7 +50,7 @@ func writeResp(w http.ResponseWriter, r *http.Request, resp *spec.Response) erro
res := metaResponse{Response: resp}
params := r.Context().Value(CtxParams).(params.Params)
ew := &errWriter{w: w}
switch params.Get("f") {
switch v, _ := params.Get("f"); v {
case "json":
w.Header().Set("Content-Type", "application/json")
data, err := json.Marshal(res)

View File

@@ -79,7 +79,7 @@ func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response {
Find(&childTracks)
for _, c := range childTracks {
toAppend := spec.NewTCTrackByFolder(c, folder)
if params.Get("c") == "Jamstash" {
if v, _ := params.Get("c"); v == "Jamstash" {
// jamstash thinks it can't play flacs
toAppend.ContentType = "audio/mpeg"
toAppend.Suffix = "mp3"

View File

@@ -1,79 +1,262 @@
package params
// {get, get first, get or} * {str, int, id} * {list, not list} = 18
import (
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
)
type Params struct {
values url.Values
var (
ErrKeyNotFound = errors.New("key(s) not found")
ErrIDInvalid = errors.New("invalid id")
ErrIDNotAnInt = errors.New("not an int")
)
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 *[]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)
}
}
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
}
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 {
return Params{params}
if err := r.ParseForm(); err == nil {
for k, v := range r.Form {
params[k] = v
}
}
for k, v := range r.Form {
params[k] = v
}
return Params{params}
return Params(params)
}
func (p Params) Get(key string) string {
return p.values.Get(key)
func (p Params) get(key string) []string {
return p[key]
}
func (p Params) GetOr(key, or string) string {
val := p.Get(key)
if val == "" {
return or
}
return val
}
func (p Params) GetInt(key string) (int, error) {
strVal := p.values.Get(key)
if strVal == "" {
return 0, fmt.Errorf("no param with key `%s`", key)
}
val, err := strconv.Atoi(strVal)
if err != nil {
return 0, fmt.Errorf("not an int `%s`", strVal)
}
return val, nil
}
func (p Params) GetIntOr(key string, or int) int {
val, err := p.GetInt(key)
if err != nil {
return or
}
return val
}
func (p Params) GetFirstList(keys ...string) []string {
for _, key := range keys {
if v, ok := p.values[key]; ok && len(v) > 0 {
func (p Params) getFirst(keys []string) []string {
for _, k := range keys {
if v, ok := p[k]; ok {
return v
}
}
return nil
}
func (p Params) GetFirstListInt(keys ...string) []int {
v := p.GetFirstList(keys...)
if v == nil {
return nil
}
ret := make([]int, 0, len(v))
for _, p := range v {
i, _ := strconv.Atoi(p)
ret = append(ret, i)
}
return ret
// ** 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) 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
}

View File

@@ -0,0 +1,55 @@
package params
import (
"errors"
"testing"
)
func TestParseID(t *testing.T) {
tcases := []struct {
param string
expType IDType
expValue int
expErr error
}{
{param: "al-45", expType: IDTypeAlbum, expValue: 45},
{param: "ar-2", expType: IDTypeArtist, expValue: 2},
{param: "tr-43", expType: IDTypeTrack, expValue: 43},
{param: "xx-1", expErr: ErrIDInvalid},
{param: "al-howdy", expErr: ErrIDNotAnInt},
}
for _, tcase := range tcases {
t.Run(tcase.param, func(t *testing.T) {
act, err := parseID(tcase.param)
if err != nil && !errors.Is(err, tcase.expErr) {
t.Fatalf("expected err %q, got %q", tcase.expErr, err)
}
if act.Value != tcase.expValue {
t.Errorf("expected value %d, got %d", tcase.expValue, act.Value)
}
if act.Type != tcase.expType {
t.Errorf("expected type %v, got %v", tcase.expType, act.Type)
}
})
}
}
// TODO?
func TestGet(t *testing.T) {}
func TestGetFirst(t *testing.T) {}
func TestGetOr(t *testing.T) {}
func TestGetList(t *testing.T) {}
func TestGetFirstList(t *testing.T) {}
func TestGetOrList(t *testing.T) {}
func TestGetInt(t *testing.T) {}
func TestGetFirstInt(t *testing.T) {}
func TestGetOrInt(t *testing.T) {}
func TestGetIntList(t *testing.T) {}
func TestGetFirstIntList(t *testing.T) {}
func TestGetOrIntList(t *testing.T) {}
func TestGetID(t *testing.T) {}
func TestGetFirstID(t *testing.T) {}
func TestGetOrID(t *testing.T) {}
func TestGetIDList(t *testing.T) {}
func TestGetFirstIDList(t *testing.T) {}
func TestGetOrIDList(t *testing.T) {}