namer

package
v1.6.1 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2026 License: BSD-2-Clause Imports: 4 Imported by: 1

Documentation

Overview

Package namer represent interface to templates creation.

Index

Examples

Constants

View Source
const ObjectLocationMissing = ""

ObjectLocationMissing is the sentinel value passed as the objectLocation argument to NewLayeredNamer 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

View Source
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 NewLayeredNamer validation.

Functions

This section is empty.

Types

type DefaultKey

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

DefaultKey implements default realization.

func NewDefaultKey

func NewDefaultKey(name string, keytype KeyType, property string, raw string) DefaultKey

NewDefaultKey returns new Key object.

func (DefaultKey) Build

func (k DefaultKey) Build() string

Build reconstructs key string.

func (DefaultKey) Name

func (k DefaultKey) Name() string

Name returns name of the key.

func (DefaultKey) Property

func (k DefaultKey) Property() string

Property returns property of the key.

func (DefaultKey) Type

func (k DefaultKey) Type() KeyType

Type returns type of the key.

type DefaultNamer

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

DefaultNamer represents default namer.

func (*DefaultNamer) GenerateNames

func (n *DefaultNamer) GenerateNames(name string) ([]Key, error)

GenerateNames all keys for an object name.

func (*DefaultNamer) ParseKey

func (n *DefaultNamer) ParseKey(name string) (DefaultKey, error)

ParseKey parses a raw key name into a structured DefaultKey.

func (*DefaultNamer) ParseKeys

func (n *DefaultNamer) ParseKeys(names []string, ignoreError bool) (Results, error)

ParseKeys combine multiple raw keys into grouped results.

func (*DefaultNamer) Prefix

func (n *DefaultNamer) Prefix(val string, isPrefix bool) string

Prefix returns the prefix used by this namer.

func (*DefaultNamer) Prefixes added in v1.4.0

func (n *DefaultNamer) Prefixes(val string, isPrefix bool) []string

Prefixes returns the range prefixes for every key category. DefaultNamer stores all categories under a single /<prefix>/ root, so the empty-name case collapses to a single prefix. For non-empty names the per-category fan-out matters because each category interleaves an extra path segment.

type InvalidKeyError

type InvalidKeyError struct {
	Key     string
	Problem string
}

InvalidKeyError represents an error for invalid key format.

func (InvalidKeyError) Error

func (e InvalidKeyError) Error() string

type InvalidNameError

type InvalidNameError struct {
	Name    string
	Problem string
}

InvalidNameError represents an error for invalid name format.

func (InvalidNameError) Error

func (e InvalidNameError) Error() string

type Key

type Key interface {
	Name() string     // Get object name.
	Type() KeyType    // Get key type.
	Property() string // Get metadata (e.g., algorithm version).
	Build() string    // Reconstruct raw key string.
}

Key defines the minimal interface required by keys.

type KeyType

type KeyType int

KeyType represents key types.

const (
	// KeyTypeValue represents data type.
	KeyTypeValue KeyType = iota + 1
	// KeyTypeHash represents hash of the data type.
	KeyTypeHash
	// KeyTypeSignature represents signature of the data type.
	KeyTypeSignature
)

func (KeyType) String

func (t KeyType) String() string

type LayeredHashLocation added in v1.3.0

type LayeredHashLocation struct {
	HasherName string // matches hasher.Hasher.Name().
	Location   string // the location segment (e.g. "sha256").
}

LayeredHashLocation associates a hasher name with its key location segment.

type LayeredOption added in v1.3.0

type LayeredOption func(*layeredOpts)

LayeredOption configures NewLayeredNamer.

func CompactSingleHash added in v1.3.0

func CompactSingleHash() LayeredOption

CompactSingleHash drops the per-hasher location segment in generated and parsed hash keys. Layout becomes /hashes/<objectLocation>/<name>. NewLayeredNamer 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/namer"
)

func main() {
	layered, err := namer.NewLayeredNamer(
		"objects",
		[]namer.LayeredHashLocation{{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:
KeyTypeValue -> /objects/alice
KeyTypeHash -> /hashes/objects/alice

func CompactSingleSig added in v1.3.0

func CompactSingleSig() LayeredOption

CompactSingleSig drops the per-signer location segment in generated and parsed sig keys. Layout becomes /sig/<objectLocation>/<name>. NewLayeredNamer 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/namer"
)

func main() {
	layered, err := namer.NewLayeredNamer(
		"objects",
		nil,
		[]namer.LayeredSigLocation{{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:
KeyTypeValue -> /objects/alice
KeyTypeSignature -> /sig/objects/alice

func LegacyHashSigLayout added in v1.5.0

func LegacyHashSigLayout() LayeredOption

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/namer"
)

func main() {
	layered, err := namer.NewLayeredNamer(
		"config",
		[]namer.LayeredHashLocation{{HasherName: "sha256", Location: "sha256"}},
		[]namer.LayeredSigLocation{{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:
KeyTypeValue -> /config/all
KeyTypeHash -> /hashes/sha256/all
KeyTypeSignature -> /sig/ed25519/all

func WithKeyPrefix added in v1.6.0

func WithKeyPrefix(prefix string) LayeredOption

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.

NewLayeredNamer returns ErrKeyPrefixNoLeadingSlash or ErrKeyPrefixTrailingSlash if the prefix is malformed.

type LayeredSigLocation added in v1.3.0

type LayeredSigLocation struct {
	SignerName string // matches crypto.Signer.Name() / Verifier.Name().
	Location   string // the location segment (e.g. "ed25519").
}

LayeredSigLocation associates a signer name with its key location segment.

type Namer

type Namer interface {
	GenerateNames(name string) ([]Key, error)
	ParseKey(name string) (DefaultKey, 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 (DefaultNamer) 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 NewDefaultNamer

func NewDefaultNamer(prefix string, hashNames []string, sigNames []string) Namer

NewDefaultNamer returns new DefaultNamer object with hash/signature names configuration.

func NewLayeredNamer added in v1.3.0

func NewLayeredNamer(
	objectLocation string,
	hashLocations []LayeredHashLocation,
	sigLocations []LayeredSigLocation,
	opts ...LayeredOption,
) (Namer, error)

NewLayeredNamer 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

ExampleNewLayeredNamer 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/namer"
)

func main() {
	layered, err := namer.NewLayeredNamer(
		"objects",
		[]namer.LayeredHashLocation{{HasherName: "sha256", Location: "sha256"}},
		[]namer.LayeredSigLocation{{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:
KeyTypeValue -> /objects/alice
KeyTypeHash -> /hashes/sha256/objects/alice
KeyTypeSignature -> /sig/rsa/objects/alice
Example (Parse)

ExampleNewLayeredNamer_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/namer"
)

func main() {
	layered, err := namer.NewLayeredNamer(
		"objects",
		[]namer.LayeredHashLocation{{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:
KeyTypeValue name=alice property=""
KeyTypeHash name=alice property="sha256"
Example (Prefix)

ExampleNewLayeredNamer_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/namer"
)

func main() {
	layered, err := namer.NewLayeredNamer("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 Results

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

Results represents Namer working result.

func NewResults

func NewResults(initial map[string][]Key) Results

NewResults creates a new Results instance from the provided initial data.

func (*Results) Items

func (r *Results) Items() iter.Seq2[string, []Key]

Items return iterator over all name->keys groups.

func (*Results) Len

func (r *Results) Len() int

Len returns the number of unique object names.

func (*Results) Result

func (r *Results) Result() map[string][]Key

Result returns the underlying results map.

func (*Results) Select

func (r *Results) Select(name string) ([]Key, bool)

Select gets keys for a specific object name.

func (*Results) SelectSingle

func (r *Results) SelectSingle() ([]Key, bool)

SelectSingle gets keys for single-name case (if applicable).

Jump to

Keyboard shortcuts

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