rename query package to qs
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
package qs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Query error.
|
||||
var (
|
||||
ErrInvalidSort = errors.New("invalid sort")
|
||||
ErrTooManySorts = errors.New("too many sorts")
|
||||
)
|
||||
|
||||
var sortRegexp = regexp.MustCompile("^([A-z0-9]+) (asc|desc)$")
|
||||
|
||||
// ReadSortsOptions configures the behaviour of ReadSorts.
|
||||
type ReadSortsOptions struct {
|
||||
Key string // Query string key. The default value is "sort"
|
||||
MaxSorts int // If this is > 0, a maximum number of sorts is imposed
|
||||
}
|
||||
|
||||
// Sort represents a sort order for, most likely, a database query.
|
||||
type Sort struct {
|
||||
Field string `json:"field"` // Field by which to sort.
|
||||
Direction string `json:"direction"` // Direction in which to sort, namely asc or desc.
|
||||
}
|
||||
|
||||
// Sorts is a slice of Sort structs.
|
||||
type Sorts []Sort
|
||||
|
||||
// Field returns a new Sorts slice containing only sorts for the specified field.
|
||||
// The original order of sorts is preserved.
|
||||
func (sorts Sorts) Field(field string) Sorts {
|
||||
ff := Sorts{}
|
||||
for _, sort := range sorts {
|
||||
if sort.Field == field {
|
||||
ff = append(ff, sort)
|
||||
}
|
||||
}
|
||||
return ff
|
||||
}
|
||||
|
||||
// Fields returns a new Sorts slice containing sorts for any of the specified fields.
|
||||
// The original order of sorts is preserved.
|
||||
func (sorts Sorts) Fields(fields ...string) Sorts {
|
||||
ff := Sorts{}
|
||||
for _, sort := range sorts {
|
||||
for _, field := range fields {
|
||||
if sort.Field == field {
|
||||
ff = append(ff, sort)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ff
|
||||
}
|
||||
|
||||
// HasField returns true if the Sorts slice includes any sorts for the specified field.
|
||||
func (sorts Sorts) HasField(field string) bool {
|
||||
for _, sort := range sorts {
|
||||
if sort.Field == field {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadRequestSorts parses a request's query string into a slice of sorts.
|
||||
// This function returns nil if no sorts are found.
|
||||
func ReadRequestSorts(req *http.Request, opt *ReadSortsOptions) (Sorts, error) {
|
||||
return ReadSorts(req.URL.Query(), opt)
|
||||
}
|
||||
|
||||
// ReadSorts parses URL values into a slice of sorts.
|
||||
// This function returns nil if no sorts are found.
|
||||
func ReadSorts(values url.Values, opt *ReadSortsOptions) (Sorts, error) {
|
||||
opt = initSortsOptions(opt)
|
||||
|
||||
if !values.Has(opt.Key) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if opt.MaxSorts > 0 && len(values[opt.Key]) > opt.MaxSorts {
|
||||
return nil, ErrTooManySorts
|
||||
}
|
||||
|
||||
sorts := []Sort{}
|
||||
for _, sortStr := range values[opt.Key] {
|
||||
match := sortRegexp.FindStringSubmatch(sortStr)
|
||||
if match == nil {
|
||||
return nil, ErrInvalidSort
|
||||
}
|
||||
|
||||
sort := Sort{
|
||||
Field: match[1],
|
||||
Direction: match[2],
|
||||
}
|
||||
sorts = append(sorts, sort)
|
||||
}
|
||||
|
||||
return sorts, nil
|
||||
}
|
||||
|
||||
// ReadStringSorts parses a query string literal into a slice of sorts.
|
||||
// This function returns nil if no sorts are found.
|
||||
func ReadStringSorts(qs string, opt *ReadSortsOptions) (Sorts, error) {
|
||||
values, err := url.ParseQuery(qs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ReadSorts(values, opt)
|
||||
}
|
||||
|
||||
func initSortsOptions(opt *ReadSortsOptions) *ReadSortsOptions {
|
||||
def := &ReadSortsOptions{
|
||||
Key: "sort",
|
||||
}
|
||||
|
||||
if opt != nil {
|
||||
if len(opt.Key) > 0 {
|
||||
def.Key = opt.Key
|
||||
}
|
||||
|
||||
if opt.MaxSorts > def.MaxSorts {
|
||||
def.MaxSorts = opt.MaxSorts
|
||||
}
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
Reference in New Issue
Block a user