tag

package
v0.801.0 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package tag expands JaWS dependency tags into keys used to associate Elements with application state and logical signals.

Tags identify dependencies; they do not observe state or schedule work. Register them with github.com/linkdata/jaws.Request.Tag or github.com/linkdata/jaws.Element.Tag, mutate the authoritative state, then pass the corresponding keys to github.com/linkdata/jaws.Request.Dirty or use them as github.com/linkdata/jaws.Jaws.Broadcast destinations.

Choosing tags

Prefer stable identities derived from the data being rendered. Use a pointer when output depends on an object as a whole, a distinct comparable wrapper type for an independently changing part, or a named empty struct for a shared signal. Name a tag for the dependency, not the event that changes it. Plain strings are not tag keys; use Tag for a string-named logical signal.

Register an item with its own key and a separate group key when both narrow and broad updates are needed. Do not include the group key in the item's TagGetter.JawsGetTag result: dirtying the item would then expand to the group and refresh every group listener.

Registration and targeting

Registration is additive and lasts until the Element is removed or its Request ends. Adding a tag neither schedules an update nor changes targets already selected. Register known dependencies during initial rendering; add a later-discovered dependency only when it remains valid for the Element's remaining lifetime.

Ordinary keys passed through github.com/linkdata/jaws.Request.Dirty match Elements across all live Requests. A non-nil pointer to github.com/linkdata/jaws.Element is the exact-target exception after expansion. TagExpand defines accepted keys and validation, TagGetter defines stable identity and concurrency requirements, and github.com/linkdata/jaws.Request.TagsOf reports the keys actually registered on an Element.

Index

Examples

Constants

View Source
const DebugRender = false

DebugRender reports whether TagString and TagsString render tags in full.

It is false in the default (production) build, where they forward to the bounded, crash-safe TagStringRelease/TagsStringRelease. Build with -tags debug or -race to make it true.

Variables

View Source
var ErrIllegalTagType errIllegalTagType

ErrIllegalTagType is returned when a UI tag type is disallowed.

View Source
var ErrNotComparable errNotComparable

ErrNotComparable is returned when a UI object or tag is not comparable.

View Source
var ErrNotUsableAsTag errNotUsableAsTag

ErrNotUsableAsTag is returned when a value cannot be used as a tag.

A tag key must be comparable at runtime and equal to itself so later dirtying, broadcasts and event routing can match it reliably. This error also matches ErrNotComparable via errors.Is.

View Source
var ErrTooManyTags errTooManyTags

ErrTooManyTags is returned when tag expansion exceeds the recursion depth (maxTagDepth) or result count (maxTagCount) limits.

Functions

func FindTagGetter

func FindTagGetter(x any) (path string, tgType reflect.Type, found bool)

FindTagGetter searches x recursively for a nested TagGetter.

The search is bounded: it follows at most maxTagDepth levels of nesting and scans only the first maxHintScan elements of any array or slice. It is used only to enrich the ErrNotUsableAsTag diagnostic, so these bounds trade completeness for a cheap, terminating search.

It is a best-effort diagnostic aid: the maxHintScan and maxTagDepth bounds may change, and a negative result is not authoritative, so callers should not rely on it for non-diagnostic purposes.

func NewErrNotComparable

func NewErrNotComparable(x any) error

NewErrNotComparable returns ErrNotComparable if x is not comparable.

func NewErrNotUsableAsTag

func NewErrNotUsableAsTag(x any) error

NewErrNotUsableAsTag returns ErrNotUsableAsTag for an unusable tag key.

It returns nil for nil and for values that are comparable at runtime and equal to themselves. It only validates key usability; it does not apply TagExpand's tag-type policy, so a value may pass this check and still be rejected with ErrIllegalTagType.

func TagExpand

func TagExpand(tagValue any) (result []any, err error)

TagExpand expands tagValue into a flat list of unique, usable tag keys.

tagValue may be nil, a Tag, []Tag, []any, a TagGetter, or another value that is comparable at runtime and equals itself. A nil interface contributes no keys. A typed nil is a non-nil interface and follows the normal rules for its dynamic type, including dispatch to TagGetter.JawsGetTag when it implements TagGetter.

The predeclared string, bool, signed integer, unsigned integer other than uintptr, and floating-point types are rejected with ErrIllegalTagType, as are template.HTML, template.HTMLAttr, jid.Jid and key.Key. An unusable expanded key is rejected with ErrNotUsableAsTag, which also matches ErrNotComparable under errors.Is. Expansion that exceeds the nesting-depth or total-count limits is rejected with ErrTooManyTags.

On error, result contains the tags expanded before the failure. If an expanded value is not usable as a tag key, result is nil and err matches ErrNotUsableAsTag.

A single call may invoke a TagGetter more than once, and later calls may expand the same value again. When expansion encounters a cyclic TagGetter graph, it uses the participating TagGetter values themselves as keys; they must therefore be usable as tags. Implementations must satisfy TagGetter's stable-identity contract.

TagExpand does not copy input slices or slices returned by TagGetter.JawsGetTag before traversing them. They must not be mutated concurrently with expansion.

Errors are returned rather than logged. Use github.com/linkdata/jaws.Jaws.MustTagExpand to report them through a configured logger instead.

Example (ErrorsIs)
package main

import (
	"errors"
	"fmt"

	"github.com/linkdata/jaws/lib/tag"
)

func main() {
	_, err := tag.TagExpand([]int{1})
	fmt.Println(errors.Is(err, tag.ErrNotUsableAsTag))
	fmt.Println(errors.Is(err, tag.ErrNotComparable))

}
Output:
true
true
Example (TagGetter)
package main

import (
	"fmt"

	"github.com/linkdata/jaws/lib/tag"
)

type exampleItem struct {
	Name string
}

func (item *exampleItem) JawsGetTag() any {
	return item
}

func main() {
	item := &exampleItem{Name: "row"}
	tags, err := tag.TagExpand([]any{item, tag.Tag("list")})
	if err != nil {
		panic(err)
	}
	fmt.Println(len(tags), tags[0] == item, tags[1] == tag.Tag("list"))

}
Output:
2 true true

func TagString

func TagString(tag any) string

TagString returns a debug string for tag.

In the default build it forwards to the crash-safe TagStringRelease; see DebugRender.

func TagStringDebug added in v0.700.0

func TagStringDebug(tag any) string

TagStringDebug renders tag in full: a pointer as its type and address, a fmt.Stringer as its type and String(), and every other value through "%#v".

It is the most informative form, but because it descends into the value it can overflow the stack or exhaust memory on a self-referential or oversized tag, so it is unsuitable for untrusted input.

TagString forwards here in the debug and -race builds.

func TagStringRelease added in v0.700.0

func TagStringRelease(tag any) string

TagStringRelease renders tag showing only its type, plus its address when tag is a pointer whose address can be read without panicking.

It reads the type via reflection, never hands tag itself to a formatting verb, and never invokes a String/GoString/Format/Error method or touches the pointed-to memory; the address is read under a recover (see pointerAddr) because reflect.Value.Pointer can panic on some cgo / not-in-heap pointers, and an over-long type name is truncated. It therefore cannot recurse, overflow the stack, exhaust memory, or panic no matter what tag contains — at the cost of not showing the tag's contents.

TagString forwards here in the default build.

func TagsString added in v0.700.0

func TagsString(tags []any) string

TagsString returns a debug string for a slice of tags.

In the default build it forwards to the crash-safe TagsStringRelease; see DebugRender.

func TagsStringDebug added in v0.700.0

func TagsStringDebug(tags []any) string

TagsStringDebug renders a slice of tags in full, each with TagStringDebug.

It applies no aggregate size limit, matching the informative (unsafe) debug contract. TagsString forwards here in the debug and -race builds.

func TagsStringRelease added in v0.700.0

func TagsStringRelease(tags []any) string

TagsStringRelease renders a slice of tags, each with TagStringRelease.

It stops once the output reaches maxTagString, so it inherits the per-element crash-safety and stays bounded however long the slice is. TagsString forwards here in the default build.

Types

type Tag

type Tag string

Tag is a simple comparable tag value.

type TagGetter

type TagGetter interface {
	// JawsGetTag returns the value that [TagExpand] interprets as the object's tags.
	JawsGetTag() any
}

TagGetter exposes an object's tags to TagExpand.

github.com/linkdata/jaws.Element.ApplyGetter, TagExpand, and APIs that use TagExpand for registration, dirtying, or broadcast destinations may call JawsGetTag before or after rendering and more than once. Application code may also call it directly. The method receives no Request or rendering context. Callers needing flattened, validated keys should use TagExpand rather than interpret the raw return value.

An explicitly documented initialization phase may return a nil interface, which expands to no keys. A typed nil is a non-nil interface and follows TagExpand's normal rules for its dynamic type. Initialization is not retroactive: it does not affect earlier registration, dirtying, or broadcasts. After its first non-nil result, every call must return a value that TagExpand expands to the same set of keys. Previously returned containers must continue expanding to the key set they produced when returned and must be treated as read-only. Fresh containers and equivalent representations are allowed.

JaWS does not serialize JawsGetTag calls. A getter used concurrently must synchronize its state and safely publish any returned containers.

Example

ExampleTagGetter shows the two supported ways to read an object's tags: JawsGetTag directly, which returns the raw value, and TagExpand, which flattens and validates it into keys. Both are stable for as long as the getter is idempotent.

package main

import (
	"fmt"

	"github.com/linkdata/jaws/lib/tag"
)

type exampleItem struct {
	Name string
}

func (item *exampleItem) JawsGetTag() any {
	return item
}

func main() {
	group := &exampleItem{Name: "group"}
	item := &exampleItem{Name: "row"}

	// JawsGetTag is the canonical public accessor and may be called directly.
	fmt.Println(item.JawsGetTag() == item)

	// TagExpand is how to obtain flattened, validated keys.
	keys, err := tag.TagExpand([]any{item, group})
	if err != nil {
		panic(err)
	}
	fmt.Println(len(keys), keys[0] == item, keys[1] == group)

}
Output:
true
2 true true

Jump to

Keyboard shortcuts

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