addressable

package module
v0.0.0-...-a062ad1 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

README

go-ruby-addressable/addressable

addressable — go-ruby-addressable

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's addressable gem — RFC 3986 URI parsing / normalization / reference-resolution and RFC 6570 URI Templates (all four levels), byte-exact to the gem. It is a drop-in URI backend for go-embedded-ruby, but a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-regexp and go-ruby-public-suffix.

Features

Faithful port of the two headline classes, validated against the addressable gem on every supported platform:

Addressable::URI
  • Parse any URI reference into scheme / userinfo / host / port / path / query / fragment, plus reconstructed authority.
  • Normalize per RFC 3986 §6: lowercase scheme + host, IDN → Punycode (normalized_host), percent-encoding normalization (decode unreserved, uppercase escapes), dot-segment removal, default-port stripping.
  • Reference resolution (Join / +) per RFC 3986 §5.2 — passes the full §5.4 normal + abnormal example set.
  • query_values (ordered pairs and last-wins hash) and the assignment side.
  • omit, origin (RFC 6454).
Addressable::Template (RFC 6570, levels 1-4)
  • expand(vars) for every operator — {var} {+var} {#var} {.var} {/var} {;var} {?var} {&var} — with the :n prefix and * explode modifiers, over string / list / associative-array values.
  • extract(uri) — reverse-match a URI back to its variable bindings.
import "github.com/go-ruby-addressable/addressable"

u := addressable.Parse("HTTP://Example.COM:80/a/./b/../c/")
u.Normalize().String() // "http://example.com/a/c/"

t := addressable.NewTemplate("http://example.com/search{?q,lang}")
t.Expand(map[string]addressable.Value{"q": "hello", "lang": "en"})
// "http://example.com/search?q=hello&lang=en"
t.Extract("http://example.com/search?q=hello&lang=en")
// map[q:hello lang:en]

Tests & coverage

The test suite is 100% coverage and holds that gate with no Ruby present — the deterministic golden vectors (drawn from the RFC 3986 §5.4 and RFC 6570 example sets, captured byte-for-byte from the gem) drive every branch. A differential oracle additionally runs the addressable gem where available (gated on RUBY_VERSION >= "4.0"), confirming continued parity. CGO=0; validated on all six 64-bit Go targets (amd64 / arm64 / riscv64 / loong64 / ppc64le / s390x) and three operating systems.

GOWORK=off go test -race -cover ./...

License

BSD-3-Clause — see LICENSE. Copyright © 2026, the go-ruby-addressable/addressable authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package addressable is a pure-Go (no cgo) reimplementation of the Ruby `addressable` gem: RFC 3986 URI parsing/normalization/reference-resolution and RFC 6570 URI Templates (all four levels), byte-exact to the gem.

Index

Constants

View Source
const (
	// ClassUnreserved is RFC 3986 unreserved: ALPHA / DIGIT / "-" / "." / "_" / "~".
	ClassUnreserved = "A-Za-z0-9\\-._~"
	// ClassReserved is RFC 3986 reserved (gen-delims + sub-delims).
	ClassReserved = ":/?#\\[\\]@!$&'()*+,;="
	// ClassPath is the set kept literal inside a normalized path.
	ClassPath = ClassUnreserved + ":@!$&'()*+,;=/"
	// ClassQuery is the set kept literal inside a normalized query.
	ClassQuery = ClassUnreserved + ":@!$'()*+,;=/?"
	// ClassFragment is the set kept literal inside a normalized fragment.
	ClassFragment = ClassUnreserved + ":@!$&'()*+,;=/?"
	// ClassAuthority is the set kept literal inside a normalized authority.
	ClassAuthority = ClassUnreserved + ":@!$&'()*+,;=\\[\\]"
	// ClassHost is the set kept literal inside a normalized host.
	ClassHost = ClassUnreserved + "!$&'()*+,;=\\[\\]"
	// ClassScheme is the set kept literal inside a scheme.
	ClassScheme = "A-Za-z0-9\\-+."
)

Character classes mirror Addressable::URI::CharacterClasses. Each set names the characters that are allowed to appear *unescaped* within a component; every other byte is percent-encoded by EncodeComponent.

Variables

This section is empty.

Functions

func EncodeComponent

func EncodeComponent(s, characterClass string) string

EncodeComponent percent-encodes every byte of s that is not present in the given character-class spec. It mirrors Addressable::URI.encode_component with a string character class.

func UnencodeComponent

func UnencodeComponent(s string) string

UnencodeComponent percent-decodes every %XX sequence in s. Invalid escapes are left verbatim, matching Addressable::URI.unencode_component.

Types

type Template

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

Template is a compiled RFC 6570 URI Template (all four levels), mirroring Addressable::Template.

func NewTemplate

func NewTemplate(pattern string) *Template

NewTemplate compiles a template pattern.

func (*Template) Expand

func (t *Template) Expand(vars map[string]Value) string

Expand expands the template with the given variable bindings, returning the resulting URI string. Mirrors Addressable::Template#expand(vars).to_s.

func (*Template) Extract

func (t *Template) Extract(uri string) map[string]any

Extract reverse-matches a URI against the template, returning the variable bindings, or nil when the URI does not match. Mirrors Addressable::Template#extract(uri). List/hash values are returned as []string / map[string]string where the template variable exploded.

func (*Template) Pattern

func (t *Template) Pattern() string

Pattern returns the original template string.

type URI

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

URI is a parsed, mutable URI, mirroring Addressable::URI. A nil-valued component is represented by an empty *string (Go's zero *string is nil = "component absent"); the path is always a plain string (never nil, matching the gem, which reports "" rather than nil for a missing path).

func Parse

func Parse(uri string) *URI

Parse parses a URI reference into a *URI, mirroring Addressable::URI.parse.

func (*URI) Authority

func (u *URI) Authority() *string

Authority reconstructs the authority ("user@host:port"), or nil when the URI has no host.

func (*URI) Fragment

func (u *URI) Fragment() *string

Fragment returns the fragment component, or nil if absent.

func (*URI) Host

func (u *URI) Host() *string

Host returns the host component, or nil if absent.

func (*URI) Join

func (u *URI) Join(reference string) *URI

Join resolves reference against the (base) URI per RFC 3986 §5.2, returning a new absolute URI. Mirrors Addressable::URI#join / #+.

func (*URI) JoinURI

func (u *URI) JoinURI(r *URI) *URI

JoinURI is Join with an already-parsed reference.

func (*URI) Normalize

func (u *URI) Normalize() *URI

Normalize returns a normalized copy of the URI per RFC 3986 §6: lowercased scheme/host, IDN punycode, percent-encoding normalization, dot-segment removal, and default-port stripping. Mirrors Addressable::URI#normalize.

func (*URI) NormalizedHost

func (u *URI) NormalizedHost() *string

NormalizedHost returns the host lowercased and IDN-punycoded (ASCII-Compatible Encoding), mirroring Addressable::URI#normalized_host.

func (*URI) Omit

func (u *URI) Omit(components ...string) *URI

Omit returns a copy of the URI with the named components removed. Recognized names: "scheme", "userinfo", "host", "port", "path", "query", "fragment". Mirrors Addressable::URI#omit.

func (*URI) Origin

func (u *URI) Origin() string

Origin returns the RFC 6454 origin ("scheme://host[:port]"), or "null" when the URI is not a hierarchical http(s)-style URI. Mirrors Addressable::URI#origin.

func (*URI) Path

func (u *URI) Path() string

Path returns the path component (never nil; "" when empty).

func (*URI) Port

func (u *URI) Port() *int

Port returns the explicit port, or nil if absent.

func (*URI) Query

func (u *URI) Query() *string

Query returns the query component, or nil if absent.

func (*URI) QueryValues

func (u *URI) QueryValues() [][2]string

QueryValues parses the query string into ordered key/value pairs. Duplicate keys are preserved in order. Mirrors Addressable::URI#query_values(Array).

func (*URI) QueryValuesHash

func (u *URI) QueryValuesHash() map[string]string

QueryValuesHash collapses QueryValues into a map keeping the *last* value for a duplicated key, mirroring Addressable::URI#query_values (Hash, the default).

func (*URI) Scheme

func (u *URI) Scheme() *string

Scheme returns the scheme component, or nil if absent.

func (*URI) SetQueryValues

func (u *URI) SetQueryValues(pairs [][2]string)

SetQueryValues sets the query component from ordered pairs, percent-encoding keys and values. A value list under one key repeats the key. Mirrors the assignment side of Addressable::URI#query_values=.

func (*URI) SetQueryValuesMap

func (u *URI) SetQueryValuesMap(m map[string]any)

SetQueryValuesMap sets the query from a map. Keys are emitted in sorted order for determinism; a []string value under a key repeats the key. This matches the gem's observable output for the sorted-hash cases the golden vectors cover.

func (*URI) String

func (u *URI) String() string

String serializes the URI back to its RFC 3986 string form (Addressable::URI#to_s).

func (*URI) Userinfo

func (u *URI) Userinfo() *string

Userinfo returns the userinfo component, or nil if absent.

type Value

type Value any

Value is the dynamic value bound to a template variable: a string, a []string (list), or a [][2]string (ordered key/value pairs, an "associative array").

Jump to

Keyboard shortcuts

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