urlvalues

package module
v1.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 13, 2023 License: Apache-2.0 Imports: 8 Imported by: 0

README

urlvalues

Copyright 2022, 2023 Johan Rönkkö

Licensing

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

About The Project

Go package for unmarshalling URL values into struct values.

All of the documentation can be found on the go.dev website.

Is it Good? Yes.

Alternatives

Alternatives and similar projects:

Credits

A special thanks to Ardan Labs whose conf project inspired the field extracting and processing implementation.

Documentation

Overview

Package urlvalues unmarshals url.Values into struct values.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInvalidStruct = errors.New("urlvalues: target must be a struct pointer")

ErrInvalidStruct indicates that the Unmarshal target is not of correct type.

Functions

func Unmarshal

func Unmarshal(data url.Values, v any, setParseOpts ...SetParseOptionFunc) error

Unmarshal unmarshals data into the value pointed to by v. If v is nil or not a struct pointer, Unmarshal returns an ErrInvalidStruct error.

Slices are decoded by splitting values by a delimiter and parsing each item individually. The delimiter defaults to semicolon (;), but can by customized by passing the WithDelimiter SetParseOptionFunc. Key-value pairs of maps are split using the same delimiter. Keys and their values are separated by a colon (:), with the key to the left and the value to the right of the colon.

Fields with types implementing encoding.TextUnmarshaler and/or encoding.BinaryUnmarshaler will be decoded using those interfaces, respectively. If a type implements both interfaces, the encoding.TextUnmarshaler interface is used to decode the value.

The decoding of each struct field can be customized by the name string stored under the "urlvalue" key in the struct field's tag. The name string acts as a key into data, possibly followed by a comma-separated list of options. The name may be empty, in which case the field name of the struct will act as as key into data in its stead.

The "default" option allows for setting a default value on a field in case corresponding URL value is not present in data, or if the value is the zero value for the field's type.

The "layout" option only applies to fields of type time.Time and allows for customizing how values should be parsed by providing layouts understood by time.Parse. See https://pkg.go.dev/time#pkg-constants for a complete list of the predefined layouts.

As a special case, if the field tag is "-", the field is always omitted. Note that a field with name "-" can still be generated using the tag "-,".

Examples of struct field tags and their meanings:

// Field defaults to 42.
Field int `urlvalue:"myName,default:42"`

// Field is parsed using the RFC850 layout.
Field time.Time `urlvalue:"myName,layout:RFC850"`

// Field is ignored by this package.
Field int `urlvalue:"-"`

// Field appears in URL values with key "-".
Field int `urlvalue:"-,"`

// Field is decoded as time.Now().AddDate(3, -4, 9).
Field time.Time `urlvalue:"myName,default:now+3y-4m+9d"`

The parsing of time.Time is extended to support a "now" based parsing. It parses the value "now" to time.Now. Furthermore, it extends this syntax by allowing the consumer to subtract or add days (d), months (m) and years (y) to "now". This is done by prepending the date identifiers (d,m,y) with a minus (-) or plus (+) sign.

Any error that occurs while processing struct fields results in a FieldError. ParseError wraps around FieldError and is returned if any error occurs while parsing the url.Values that was passed into Unmarshal. ParseError is never returned from errors occuring while parsing default values.

Example
package main

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/nahojer/urlvalues"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "http://localhost?since=now-3m&directors=Quentin%20Tarantino&directors=Christopher%20Nolan", nil)
	if err != nil {
		panic(err)
	}

	var params struct {
		Since          time.Time `urlvalue:"since,default:now-3m,layout:2006-01-02"`
		Until          time.Time `urlvalue:"until,default:now,layout:2006-01-02"`
		Genres         []string  `urlvalue:"genres,default:action;drama"`
		Directors      []string  `urlvalue:"directors"`
		NameFilter     *string   `urlvalue:"name_filter"`
		OscarNominated *bool     `urlvalue:"oscar_nominated,default:false"`
	}
	if err := urlvalues.Unmarshal(req.URL.Query(), &params); err != nil {
		panic(err)
	}

	fmt.Printf("Genres: %s\n", strings.Join(params.Genres, ", "))
	fmt.Printf("Directors: %s\n", strings.Join(params.Directors, ", "))
	fmt.Printf("Name filter: %v\n", params.NameFilter)
	fmt.Printf("Oscar nominated: %v\n", *params.OscarNominated)
}
Output:

Genres: action, drama
Directors: Quentin Tarantino, Christopher Nolan
Name filter: <nil>
Oscar nominated: false
Example (ParseError)
package main

import (
	"errors"
	"fmt"
	"net/url"

	"github.com/nahojer/urlvalues"
)

func main() {
	data := url.Values{"meaning_of_life": {"What do I know?"}}
	var theBiggestQuestion struct {
		FortyTwo int `urlvalue:"meaning_of_life"`
	}
	if err := urlvalues.Unmarshal(data, &theBiggestQuestion); err != nil {
		var parseErr *urlvalues.ParseError
		switch {
		case errors.As(err, &parseErr):
			fmt.Println(parseErr)
			fmt.Printf("Field name: %s\n", parseErr.FieldName)
			fmt.Printf("Key: %s\n", parseErr.Key)
		default:
			panic("never reached")
		}
	}
}
Output:

error parsing value of meaning_of_life: strconv.ParseInt: parsing "What do I know?": invalid syntax
Field name: FortyTwo
Key: meaning_of_life

Types

type FieldError

type FieldError struct {
	// contains filtered or unexported fields
}

FieldError occurs when an error occurs updating an individual field in the provided struct value.

func (*FieldError) Error

func (err *FieldError) Error() string

type ParseError

type ParseError struct {
	// Name of struct field.
	FieldName string
	// Key into URL values.
	Key string
	// contains filtered or unexported fields
}

ParseError occurs when a url.Values item failed to be parsed into a struct field's type.

func (*ParseError) Error

func (e *ParseError) Error() string

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

Unwrap returns the underlying FieldError.

type ParseOptions

type ParseOptions struct {
	// contains filtered or unexported fields
}

ParseOptions holds all the options that allows for customizing the parsing behaviour when unmarshalling url.Values.

func (*ParseOptions) Delim

func (o *ParseOptions) Delim() string

Delim returns the delimiter used to convert slices and maps from and into their string representation. Defaults to semicolon (;) if not set or set to the empty string.

type SetParseOptionFunc

type SetParseOptionFunc func(*ParseOptions)

SetParseOptionFunc allows for overriding the parsing behaviour of URL values.

func WithDelimiter

func WithDelimiter(s string) SetParseOptionFunc

WithDelimiter returns a SetParseOptionFunc that sets the delimiter used to convert slices and maps from and into their string representation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL