Documentation
¶
Overview ¶
Package namer represent interface to templates creation.
Index ¶
Examples ¶
Constants ¶
const ObjectLocationMissing = ""
ObjectLocationMissing is the sentinel value passed as the objectLocation argument to New to omit the per-codec objectLocation segment from every generated key. The resulting layout is:
/<name> (value) /hashes/<hashLocation>/<name> (hash) /sig/<sigLocation>/<name> (sig)
In this mode object names whose first slash-separated segment equals "hashes" or "sig" are rejected by GenerateNames to avoid collision with the category markers.
Variables ¶
var ( // ErrEmptyHasherName is returned when a hash entry has an empty HasherName. ErrEmptyHasherName = errors.New("namer: hash entry has empty HasherName") // ErrEmptySignerName is returned when a sig entry has an empty SignerName. ErrEmptySignerName = errors.New("namer: sig entry has empty SignerName") // ErrDuplicateLocation is returned when a location segment is used more than once within a category. ErrDuplicateLocation = errors.New("namer: duplicate location segment") // ErrSegmentEmpty is returned when a location segment is empty. ErrSegmentEmpty = errors.New("namer: segment must not be empty") // ErrSegmentLeadingSlash is returned when a segment starts with '/'. ErrSegmentLeadingSlash = errors.New("namer: segment must not start with '/'") // ErrSegmentTrailingSlash is returned when a segment ends with '/'. ErrSegmentTrailingSlash = errors.New("namer: segment must not end with '/'") // ErrSegmentInnerSlash is returned when a segment contains '/'. ErrSegmentInnerSlash = errors.New("namer: segment must not contain '/'") // ErrObjectLocationReserved is returned when objectLocation's first segment // is one of the reserved category markers ("hashes" or "sig"), which would // collide with hash/sig key paths during parsing. ErrObjectLocationReserved = errors.New( "namer: objectLocation must not start with reserved segment \"hashes\" or \"sig\"") // ErrCompactSingleHashCardinality is returned when CompactSingleHash is set // but the configured hash list does not have exactly one entry. ErrCompactSingleHashCardinality = errors.New("namer: CompactSingleHash requires exactly one hash entry") // ErrCompactSingleSigCardinality is returned when CompactSingleSig is set // but the configured sig list does not have exactly one entry. ErrCompactSingleSigCardinality = errors.New("namer: CompactSingleSig requires exactly one sig entry") // ErrKeyPrefixNoLeadingSlash is returned when WithKeyPrefix is given a // non-empty value that does not start with '/'. ErrKeyPrefixNoLeadingSlash = errors.New("namer: WithKeyPrefix must start with '/'") // ErrKeyPrefixTrailingSlash is returned when WithKeyPrefix is given a value // that ends with '/'. Layered keys begin with '/', so a trailing slash here // would produce keys like "/p//objectLocation/name". ErrKeyPrefixTrailingSlash = errors.New("namer: WithKeyPrefix must not end with '/'") // ErrKeyPrefixMissing is returned by ParseKey when the raw key does not // start with the configured WithKeyPrefix. ErrKeyPrefixMissing = errors.New("namer: key does not start with configured WithKeyPrefix") )
Sentinel errors for New validation.
Functions ¶
This section is empty.
Types ¶
type HashLocation ¶
type HashLocation struct {
HasherName string // matches hasher.Hasher.Name().
Location string // the location segment (e.g. "sha256").
}
HashLocation associates a hasher name with its key location segment.
type InvalidKeyError ¶
InvalidKeyError represents an error for invalid key format.
func (InvalidKeyError) Error ¶
func (e InvalidKeyError) Error() string
type InvalidNameError ¶
InvalidNameError represents an error for invalid name format.
func (InvalidNameError) Error ¶
func (e InvalidNameError) Error() string
type Key ¶
type Key struct {
// contains filtered or unexported fields
}
Key is a single key emitted or parsed by a Namer.
type Namer ¶
type Namer interface {
GenerateNames(name string) ([]Key, error)
ParseKey(name string) (Key, error)
ParseKeys(names []string, ignoreError bool) (Results, error)
Prefix(val string, isPrefix bool) string
// Prefixes returns one prefix per key category (value, hash, sig) so a
// range walk can fetch every key the namer owns. For namers whose
// categories share a single root the returned slice may have a single
// element. Use this instead of Prefix when validating integrity-protected
// data — Prefix only covers the value layer, which makes the validator
// report missing hash/sig keys.
Prefixes(val string, isPrefix bool) []string
}
Namer defines the interface for generating and parsing storage key names.
func New ¶
func New( objectLocation string, hashLocations []HashLocation, sigLocations []SigLocation, opts ...Option, ) (Namer, error)
New constructs a Namer that emits keys with per-category location segments. All segment strings are validated at construction time.
Layout (default):
/<objectLocation>/<name> (value) /hashes/<hashLocation>/<objectLocation>/<name> (hash, one per hasher) /sig/<sigLocation>/<objectLocation>/<name> (sig, one per signer)
objectLocation may itself be a multi-segment path (e.g. "settings/ldap"). hashLocation and sigLocation are still single tokens since they index per-hasher / per-signer maps.
With CompactSingleHash, the <hashLocation> segment is omitted; with CompactSingleSig, the <sigLocation> segment is omitted.
Pass ObjectLocationMissing as objectLocation to drop the per-codec segment entirely; see ObjectLocationMissing for the resulting layout.
With LegacyHashSigLayout, the per-codec <objectLocation> segment is dropped from hash and sig keys (but kept for value keys) to match the legacy product layout.
With WithKeyPrefix, every emitted/parsed key is prefixed with the given path segment, allowing multiple namers to share a single storage handle in disjoint key spaces.
Validation rules:
- hashLocation / sigLocation must be a single non-empty segment with no '/'.
- objectLocation must not start or end with '/' and must not contain empty inner segments ("//"). Its first segment must not be "hashes" or "sig". An empty objectLocation (ObjectLocationMissing) selects unnamed mode and skips these checks.
- hashLocations must be unique within hashes; sigLocations must be unique within sigs.
- Compact flags require exactly one entry in their respective category.
Example ¶
ExampleNew demonstrates the default layered key layout:
/<obj>/<name> (value) /hashes/<hashLoc>/<obj>/<name> (one per hasher) /sig/<sigLoc>/<obj>/<name> (one per signer)
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/v2/namer"
)
func main() {
layered, err := namer.New(
"objects",
[]namer.HashLocation{{HasherName: "sha256", Location: "sha256"}},
[]namer.SigLocation{{SignerName: "rsa", Location: "rsa"}},
)
if err != nil {
log.Fatalf("new namer: %v", err)
}
keys, err := layered.GenerateNames("alice")
if err != nil {
log.Fatalf("generate: %v", err)
}
for _, k := range keys {
fmt.Printf("%s -> %s\n", k.Type(), k.Build())
}
}
Output: value -> /objects/alice hash -> /hashes/sha256/objects/alice signature -> /sig/rsa/objects/alice
Example (Parse) ¶
ExampleNew_parse shows how ParseKey resolves a raw key path back into its category and object name.
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/v2/namer"
)
func main() {
layered, err := namer.New(
"objects",
[]namer.HashLocation{{HasherName: "sha256", Location: "sha256"}},
nil,
)
if err != nil {
log.Fatalf("new namer: %v", err)
}
for _, raw := range []string{
"/objects/alice",
"/hashes/sha256/objects/alice",
} {
k, err := layered.ParseKey(raw)
if err != nil {
log.Fatalf("parse %q: %v", raw, err)
}
fmt.Printf("%s name=%s property=%q\n", k.Type(), k.Name(), k.Property())
}
}
Output: value name=alice property="" hash name=alice property="sha256"
Example (Prefix) ¶
ExampleNew_prefix shows the prefix used to walk all values under the value layer (hashes/sigs are separate sub-trees).
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/v2/namer"
)
func main() {
layered, err := namer.New("objects", nil, nil)
if err != nil {
log.Fatalf("new namer: %v", err)
}
fmt.Println(layered.Prefix("", false))
fmt.Println(layered.Prefix("alice", false))
fmt.Println(layered.Prefix("users", true))
}
Output: /objects/ /objects/alice /objects/users/
type Option ¶
type Option func(*namerOpts)
Option configures New.
func CompactSingleHash ¶
func CompactSingleHash() Option
CompactSingleHash drops the per-hasher location segment in generated and parsed hash keys. Layout becomes /hashes/<objectLocation>/<name>. New returns ErrCompactSingleHashCardinality if the hash list does not have exactly one entry.
Example ¶
ExampleCompactSingleHash shows the compact hash layout, which drops the per-hasher segment when exactly one hasher is configured.
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/v2/namer"
)
func main() {
layered, err := namer.New(
"objects",
[]namer.HashLocation{{HasherName: "sha256", Location: "sha256"}},
nil,
namer.CompactSingleHash(),
)
if err != nil {
log.Fatalf("new namer: %v", err)
}
keys, err := layered.GenerateNames("alice")
if err != nil {
log.Fatalf("generate: %v", err)
}
for _, k := range keys {
fmt.Printf("%s -> %s\n", k.Type(), k.Build())
}
}
Output: value -> /objects/alice hash -> /hashes/objects/alice
func CompactSingleSig ¶
func CompactSingleSig() Option
CompactSingleSig drops the per-signer location segment in generated and parsed sig keys. Layout becomes /sig/<objectLocation>/<name>. New returns ErrCompactSingleSigCardinality if the sig list does not have exactly one entry.
Example ¶
ExampleCompactSingleSig shows the compact sig layout, which drops the per-signer segment when exactly one signer is configured.
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/v2/namer"
)
func main() {
layered, err := namer.New(
"objects",
nil,
[]namer.SigLocation{{SignerName: "rsapss", Location: "rsapss"}},
namer.CompactSingleSig(),
)
if err != nil {
log.Fatalf("new namer: %v", err)
}
keys, err := layered.GenerateNames("alice")
if err != nil {
log.Fatalf("generate: %v", err)
}
for _, k := range keys {
fmt.Printf("%s -> %s\n", k.Type(), k.Build())
}
}
Output: value -> /objects/alice signature -> /sig/objects/alice
func LegacyHashSigLayout ¶
func LegacyHashSigLayout() Option
LegacyHashSigLayout drops the per-codec objectLocation segment from hash and signature keys (but keeps it for value keys), matching the layout emitted by the legacy product. The resulting layout is:
/<objectLocation>/<name> (value) /hashes/<hashLocation>/<name> (hash) /sig/<sigLocation>/<name> (sig)
Composes with CompactSingleHash / CompactSingleSig as expected — the <hashLocation> / <sigLocation> segments are dropped independently of this option. Has no effect in unnamed mode (objectLocation == ObjectLocationMissing) since the unnamed layout already omits the objectLocation segment.
Example ¶
ExampleLegacyHashSigLayout shows the legacy product layout: hash and sig keys drop the objectLocation segment while value keys keep it.
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/v2/namer"
)
func main() {
layered, err := namer.New(
"config",
[]namer.HashLocation{{HasherName: "sha256", Location: "sha256"}},
[]namer.SigLocation{{SignerName: "ed25519", Location: "ed25519"}},
namer.LegacyHashSigLayout(),
)
if err != nil {
log.Fatalf("new namer: %v", err)
}
keys, err := layered.GenerateNames("all")
if err != nil {
log.Fatalf("generate: %v", err)
}
for _, k := range keys {
fmt.Printf("%s -> %s\n", k.Type(), k.Build())
}
}
Output: value -> /config/all hash -> /hashes/sha256/all signature -> /sig/ed25519/all
func WithKeyPrefix ¶
WithKeyPrefix prepends a fixed path prefix to every key the namer emits and expects on parse. The resulting layout is:
<keyPrefix>/<objectLocation>/<name> (value) <keyPrefix>/hashes/<hashLocation>/<objectLocation>/<name> (hash) <keyPrefix>/sig/<sigLocation>/<objectLocation>/<name> (sig)
The prefix must start with '/' and must not end with '/' (it is prepended verbatim to keys that themselves start with '/', so a trailing slash would produce empty segments). Interior '/' separators are allowed (e.g. "/a/b"). An empty prefix is a no-op.
Compared with storage.Prefixed, WithKeyPrefix lives in the namer, so two codecs with different prefixes can share a single storage.Storage and be committed atomically via a single transaction. Don't stack WithKeyPrefix on top of storage.Prefixed for the same logical prefix — the two would double it.
New returns ErrKeyPrefixNoLeadingSlash or ErrKeyPrefixTrailingSlash if the prefix is malformed.
type Results ¶
type Results struct {
// contains filtered or unexported fields
}
Results represents Namer working result.
func NewResults ¶
NewResults creates a new Results instance from the provided initial data.
func (Results) SelectSingle ¶
SelectSingle gets keys for single-name case (if applicable).
type SigLocation ¶
type SigLocation struct {
SignerName string // matches crypto.Signer.Name() / Verifier.Name().
Location string // the location segment (e.g. "ed25519").
}
SigLocation associates a signer name with its key location segment.