parth

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Jan 29, 2025 License: MIT Imports: 6 Imported by: 0

README

parth

go get github.com/daved/parth

Package parth provides path parsing for segment unmarshaling and slicing. In other words, parth provides simple and flexible access to (URL) path parameters.

Along with string, all basic non-alias types are supported. An interface is available for implementation by user-defined types. When handling an int, uint, or float of any size, the first valid value within the specified segment will be used.

Usage

Variables
func Segment(path string, i int, v interface{}) error
func Sequent(path, key string, v interface{}) error
func Span(path string, i, j int) (string, error)
func SubSeg(path, key string, i int, v interface{}) error
func SubSpan(path, key string, i, j int) (string, error)
type Parth
    func New(path string) *Parth
    func NewBySpan(path string, i, j int) *Parth
    func NewBySubSpan(path, key string, i, j int) *Parth
    func (p *Parth) Err() error
    func (p *Parth) Segment(i int, v interface{})
    func (p *Parth) Sequent(key string, v interface{})
    func (p *Parth) Span(i, j int) string
    func (p *Parth) SubSeg(key string, i int, v interface{})
    func (p *Parth) SubSpan(key string, i, j int) string
type Unmarshaler
Setup ("By Index")
import (
    "fmt"

    "github.com/daved/parth"
)

func handler(w http.ResponseWriter, r *http.Request) {
    var s string
    if err := parth.Segment(r.URL.Path, 4, &s); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    fmt.Println(r.URL.Path)
    fmt.Printf("%v (%T)\n", s, s)

    // Output:
    // /some/path/things/42/others/3
    // others (string)
}
Setup ("By Key")
import (
    "fmt"

    "github.com/daved/parth"
)

func handler(w http.ResponseWriter, r *http.Request) {
    var i int64
    if err := parth.Sequent(r.URL.Path, "things", &i); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    fmt.Println(r.URL.Path)
    fmt.Printf("%v (%T)\n", i, i)

    // Output:
    // /some/path/things/42/others/3
    // 42 (int64)
}
Setup (Parth Type)
import (
    "fmt"

    "github.com/daved/parth"
)

func handler(w http.ResponseWriter, r *http.Request) {
    var s string
    var f float32

    p := parth.New(r.URL.Path)
    p.Segment(2, &s)
    p.SubSeg("key", 1, &f)
    if err := p.Err(); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    fmt.Println(r.URL.Path)
    fmt.Printf("%v (%T)\n", s, s)
    fmt.Printf("%v (%T)\n", f, f)

    // Output:
    // /zero/one/two/key/four/5.5/six
    // two (string)
    // 5.5 (float32)
}
Setup (Unmarshaler)
import (
    "fmt"

    "github.com/daved/parth"
)

func handler(w http.ResponseWriter, r *http.Request) {
    /*
        type mytype []byte

        func (m *mytype) UnmarshalSegment(seg string) error {
            *m = []byte(seg)
        }
    */

    var m mytype
    if err := parth.Segment(r.URL.Path, 4, &m); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    fmt.Println(r.URL.Path)
    fmt.Printf("%v == %q (%T)\n", m, m, m)

    // Output:
    // /zero/one/two/key/four/5.5/six
    // [102 111 117 114] == "four" (mypkg.mytype)
}

More Info

Keep Using http.HandlerFunc And Minimize context.Context Usage

The most obvious use case for parth is when working with any URL path such as the one found at http.Request.URL.Path. parth is fast enough that it can be used multiple times in place of a single use of similar router-parameter schemes or even context.Context. There is no need to use an alternate http handler function definition in order to pass data that is already being passed. The http.Request type already holds URL data and parth is great at handling it. Additionally, parth takes care of parsing selected path segments into the types actually needed. Parth not only does more, it's usually faster and less intrusive than the alternatives.

Indexes

If an index is negative, the negative count begins with the last segment. Providing a 0 for the second index is a special case which acts as an alias for the end of the path. An error is returned if: 1. Any index is out of range of the path; 2. When there are two indexes, the first index does not precede the second index.

Keys

If a key is involved, functions will only handle the portion of the path subsequent to the provided key. An error is returned if the key cannot be found in the path.

First Whole, First Decimal (Restated - Important!)

When handling an int, uint, or float of any size, the first valid value within the specified segment will be used.

Documentation

View the GoDoc

Benchmarks

Go 1.11
benchmark                             iter       time/iter   bytes alloc        allocs
---------                             ----       ---------   -----------        ------
BenchmarkSegmentString-8          30000000     39.60 ns/op        0 B/op   0 allocs/op
BenchmarkSegmentInt-8             20000000     65.60 ns/op        0 B/op   0 allocs/op
BenchmarkSegmentIntNegIndex-8     20000000     86.60 ns/op        0 B/op   0 allocs/op
BenchmarkSpan-8                  100000000     18.20 ns/op        0 B/op   0 allocs/op
BenchmarkStdlibSegmentString-8     5000000    454.00 ns/op       50 B/op   2 allocs/op
BenchmarkStdlibSegmentInt-8        3000000    526.00 ns/op       50 B/op   2 allocs/op
BenchmarkStdlibSpan-8              3000000    518.00 ns/op       69 B/op   2 allocs/op
BenchmarkContextLookupSetGet-8     1000000   1984.00 ns/op      480 B/op   6 allocs/op

Documentation

Overview

Package parth provides path parsing for segment slicing and unmarshaling. In other words, parth provides simple and flexible access to (URL) path parameters.

Valid values are:

When handling any size of int, uint, or float, the first valid value within the specified segment will be used. Three important terms used in this package are "segment", "sequent", and "span". A segment is any single path section. A sequent is a segment that follows a "key" path section. Segments are able to be unmarshaled into variables. A span is any multiple path sections, and is handled as a string.

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

func main() {
	var segFour string
	if err := parth.Segment(&segFour, req.URL.Path, 4); err != nil {
		fmt.Println(err)
	}

	fmt.Printf("%[1]v (%[1]T)\n", segFour)

}
Output:

nn4.4nn (string)
Example (EncodingTextUnmarshaler)
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

type MyType []byte

// UnmarshalText implements encoding.TextUnmarshaler. Let's pretend something
// interesting is actually happening here.
func (m *MyType) UnmarshalText(text []byte) error {
	*m = text
	return nil
}

func main() {
	var m MyType
	if err := parth.Segment(&m, req.URL.Path, 4); err != nil {
		fmt.Println(err)
	}

	fmt.Printf("%[1]v == %[1]q (%[1]T)\n", m)

}
Output:

[110 110 52 46 52 110 110] == "nn4.4nn" (parth_test.MyType)

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrUnknownType = errors.New("unknown type provided")

	ErrFirstSegNotFound = errors.New("first segment not found by index")
	ErrLastSegNotFound  = errors.New("last segment not found by index")
	ErrSegOrderReversed = errors.New("first segment must precede last segment")
	ErrKeySegNotFound   = errors.New("segment not found by key")

	ErrDataUnparsable = errors.New("data cannot be parsed")
)

Err{Name} values facilitate error identification.

Functions

func Segment

func Segment(v any, path string, i int) error

Segment locates the path segment indicated by index i. If the index is negative, the negative count begins with the last segment.

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

func main() {
	var segFour string
	if err := parth.Segment(&segFour, req.URL.Path, 4); err != nil {
		fmt.Println(err)
	}

	fmt.Printf("%[1]v (%[1]T)\n", segFour)

}
Output:

nn4.4nn (string)

func Sequent

func Sequent(v any, path, key string) error

Sequent is similar to Segment, except that it locates the segment that is subsequent to the "key" segment.

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

func main() {
	var afterKey float32
	if err := parth.Sequent(&afterKey, req.URL.Path, "key"); err != nil {
		fmt.Println(err)
	}

	fmt.Printf("%[1]v (%[1]T)\n", afterKey)

}
Output:

4.4 (float32)

func Span

func Span(path string, i, j int) (string, error)

Span returns the path segments between indexes i and j, including the segment indicated by index i. If an index is negative, the negative count begins with the last segment. Providing a 0 for index j is a special case which acts as an alias for the end of the path. If the first segment does not begin with a slash and it is part of the requested span, no slash will be added. Index i must not precede index j.

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

func main() {
	span, err := parth.Span(req.URL.Path, 2, 4)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(span)

}
Output:

/2/key

func SubSeg

func SubSeg(v any, path, key string, i int) error

SubSeg is similar to both Sequent and Segment. It first locates the "key", then uses index i to locate a segment. For example, to access the segment immediately after the "key", an index of 0 should be provided (which is how Sequent is implemented). Technically, a negative index is valid, but it is nonsensical in this function.

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

func main() {
	var twoAfterKey float64
	if err := parth.SubSeg(&twoAfterKey, req.URL.Path, "key", 1); err != nil {
		fmt.Println(err)
	}

	fmt.Printf("%[1]v (%[1]T)\n", twoAfterKey)

}
Output:

5.5 (float64)

func SubSpan

func SubSpan(path, key string, i, j int) (string, error)

SubSpan is similar to Span, but only handles the portion of the path subsequent to the "key".

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

func main() {
	subSpanZero, err := parth.SubSpan(req.URL.Path, "zero", 2, 4)
	if err != nil {
		fmt.Println(err)
	}

	subSpanOne, err := parth.SubSpan(req.URL.Path, "1", 1, 3)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(subSpanZero)
	fmt.Println(subSpanOne)

}
Output:

/key/nn4.4nn
/key/nn4.4nn

Types

type Parth

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

Parth manages path and error data for processing a single path multiple times while handling errors only once. Only the first encountered error is stored since all subsequent calls to Parth methods will have no effect.

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/daved/parth"
)

var req, _ = http.NewRequest("GET", "/zero/1/2/key/nn4.4nn/5.5", nil)

func main() {
	var segZero string
	var twoAfterKey float32

	p := parth.New(req.URL.Path)
	p.Segment(&segZero, 0)
	p.SubSeg(&twoAfterKey, "key", 1)
	if err := p.Err(); err != nil {
		fmt.Println(err)
	}

	fmt.Printf("%[1]v (%[1]T)\n", segZero)
	fmt.Printf("%[1]v (%[1]T)\n", twoAfterKey)

}
Output:

zero (string)
5.5 (float32)

func New

func New(path string) *Parth

New constructs a pointer to an instance of Parth around the provided path.

func NewBySpan

func NewBySpan(path string, i, j int) *Parth

NewBySpan constructs a pointer to an instance of Parth after preprocessing the provided path with Span.

func NewBySubSpan

func NewBySubSpan(path, key string, i, j int) *Parth

NewBySubSpan constructs a pointer to an instance of Parth after preprocessing the provided path with SubSpan.

func (*Parth) Err

func (p *Parth) Err() error

Err returns the first error encountered by the *Parth instance.

func (*Parth) Segment

func (p *Parth) Segment(v any, i int)

Segment operates the same as the package-level function Segment.

func (*Parth) Sequent

func (p *Parth) Sequent(v any, key string)

Sequent operates the same as the package-level function Sequent.

func (*Parth) Span

func (p *Parth) Span(i, j int) string

Span operates the same as the package-level function Span.

func (*Parth) SubSeg

func (p *Parth) SubSeg(v any, key string, i int)

SubSeg operates the same as the package-level function SubSeg.

func (*Parth) SubSpan

func (p *Parth) SubSpan(key string, i, j int) string

SubSpan operates the same as the package-level function SubSpan.

Jump to

Keyboard shortcuts

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