richcty

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Apr 14, 2026 License: BSD-2-Clause Imports: 7 Imported by: 0

README

rich-cty-types

Generic capability interfaces, dispatcher functions, and _ctx / _capsule rich-object helpers for go-cty.

CI

Overview

When building HCL2- or cty-based DSLs, it's common to wrap Go values in cty capsules so expressions can operate on them. This package provides three reusable layers on top of go-cty:

  1. Capability interfaces — a small set of Go interfaces (Stringable, Gettable, Settable, Watchable, …) that a wrapped value may implement to opt into generic operations.
  2. "Rich object" convention — cty objects carrying a _capsule attribute (the encapsulated value) and optionally a _ctx attribute (a context.Context capsule). Helpers transparently unwrap either form.
  3. Dispatcher functions — cty function.Functions (get, set, call, tostring, length, watch, …) that inspect the argument, pull out the capsule, type-assert to the relevant interface, and invoke it.

Extracted from vinculum, where it powers the HCL expression language for variables, metrics, HTTP clients, conditions, and triggers.

Capability interfaces

Interface Method Used by
Stringable ToString(ctx) (string, error) tostring()
Lengthable Length(ctx) (int64, error) length()
Callable Call(ctx, args) (cty.Value, error) call()
Gettable Get(ctx, args) (cty.Value, error) get()
Settable Set(ctx, args) (cty.Value, error) set()
Incrementable Increment(ctx, args) (cty.Value, error) increment(), decrement()
Observable Observe(ctx, args) (cty.Value, error) observe()
Countable Count(ctx) (int64, error) count()
Resettable Reset(ctx) error reset()
Stateful State(ctx) (string, error) state()
Clearable Clear(ctx) error clear()
Watchable Watch(Watcher) / Unwatch(Watcher) change-notification
Watcher OnChange(ctx, old, new cty.Value) receiver side
WatchableMixin

Embed WatchableMixin to get thread-safe Watch / Unwatch plus a NotifyAll(ctx, old, new) helper to fan out change events to all registered watchers. Call NotifyAll after releasing your own value mutex.

Context (_ctx) helpers

import richcty "github.com/tsarna/rich-cty-types"

ctxVal := richcty.NewContextCapsule(ctx)              // cty.Value wrapping a context.Context
ctx2, err := richcty.GetContextFromValue(ctxVal)      // round-trip

// Or build a "ctx" variable as a cty object with extra attributes:
obj, err := richcty.NewContextObject(ctx).
    WithStringAttribute("user", "alice").
    WithInt64Attribute("request_id", 42).
    Build()
// obj is a cty.Object with attributes {user, request_id, _ctx} —
// pass it as evalCtx.Variables["ctx"].

GetContextFromValue accepts either a raw context capsule or an object with a _ctx attribute, so HCL expressions can pass ctx directly.

Capsule helpers

enc, err := richcty.GetCapsuleFromValue(val)  // unwraps capsule or object with _capsule
if s, ok := enc.(richcty.Stringable); ok { ... }

name, ok := richcty.GetStringAttr(obj, "name")
n, ok    := richcty.GetIntAttr(obj, "count")
f, ok    := richcty.GetFloat32Attr(obj, "ratio")

Generic dispatcher functions

GetGenericFunctions() returns a ready-to-register map:

import richcty "github.com/tsarna/rich-cty-types"

evalCtx.Functions = map[string]function.Function{}
for name, fn := range richcty.GetGenericFunctions() {
    evalCtx.Functions[name] = fn
}

Provides: call, get, set, increment, decrement, observe, tostring, length, state, clear, reset, count.

Most accept an optional leading ctx argument — e.g. get(ctx, thing, default) or just get(thing, default) (falls back to context.Background()).

tostring and length fall through to stdlib.MakeToFunc(cty.String) / stdlib.LengthFunc for values that aren't Stringable / Lengthable, so they're safe drop-in replacements.

License

BSD 2-Clause — see LICENSE.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ContextCapsuleType = cty.CapsuleWithOps("_context", reflect.TypeOf((*any)(nil)).Elem(), &cty.CapsuleOps{
	GoString: func(val interface{}) string {
		return fmt.Sprintf("_ctx(%p)", val)
	},
	TypeGoString: func(_ reflect.Type) string {
		return "_ctx"
	},
})

ContextCapsuleType is a cty capsule type for wrapping Context instances.

Functions

func GetCapsuleFromValue

func GetCapsuleFromValue(val cty.Value) (interface{}, error)

GetCapsuleFromValue extracts the encapsulated Go value from a cty capsule, or from an object's _capsule attribute if present. Returns the raw interface{} so callers can type-assert to the interface they need.

Accepts:

  • a capsule value directly
  • an object with a _capsule attribute that is a capsule

func GetContextFromValue

func GetContextFromValue(val cty.Value) (context.Context, error)

Gets a context from a value, which must either be a context capsule or an object with a _ctx attribute that is a context capsule.

func GetFloat32Attr

func GetFloat32Attr(val cty.Value, attr string) (float32, bool)

GetFloat32Attr returns an attribute value as a float32 if it exists and is a number.

func GetGenericFunctions

func GetGenericFunctions() map[string]function.Function

func GetIntAttr

func GetIntAttr(val cty.Value, attr string) (int, bool)

GetIntAttr returns an attribute value as an int if it exists and is a number.

func GetStringAttr

func GetStringAttr(val cty.Value, attr string) (string, bool)

GetStringAttr returns an attribute value as a string if it exists and is a string.

func NewContextCapsule

func NewContextCapsule(ctx context.Context) cty.Value

NewContextCapsule creates a new cty capsule value wrapping a Context.

Types

type Callable

type Callable interface {
	Call(ctx context.Context, args []cty.Value) (cty.Value, error)
}

Callable is a pure request/response capability. It is not specific to LLM clients and is designed to cover future types (HTTP, JSON-RPC, MCP, etc.). args contains all arguments after the "thing": the implementor fully controls what they mean.

type Clearable

type Clearable interface {
	Clear(ctx context.Context) error
}

Clearable is implemented by types that support the clear() function. On timer and threshold conditions this cancels any pending state, releases any latch, and (for retentive timers) discards accumulated time. Semantically distinct from Resettable.

type ContextObjectBuilder

type ContextObjectBuilder struct {
	Ctx        context.Context
	Attributes map[string]cty.Value
}

ContextObjectBuilder builds a cty object value for use as the "ctx" variable.

func NewContextObject

func NewContextObject(ctx context.Context) *ContextObjectBuilder

NewContextObject creates a new ContextObjectBuilder wrapping the given context.

func (*ContextObjectBuilder) Build

func (b *ContextObjectBuilder) Build() (cty.Value, error)

Build creates the cty object value for the "ctx" variable.

func (*ContextObjectBuilder) WithAttribute

func (b *ContextObjectBuilder) WithAttribute(name string, value cty.Value) *ContextObjectBuilder

func (*ContextObjectBuilder) WithInt64Attribute

func (b *ContextObjectBuilder) WithInt64Attribute(name string, value int64) *ContextObjectBuilder

func (*ContextObjectBuilder) WithStringAttribute

func (b *ContextObjectBuilder) WithStringAttribute(name string, value string) *ContextObjectBuilder

func (*ContextObjectBuilder) WithUInt64Attribute

func (b *ContextObjectBuilder) WithUInt64Attribute(name string, value uint64) *ContextObjectBuilder

type Countable

type Countable interface {
	Count(ctx context.Context) (int64, error)
}

Countable is implemented by types that maintain a running count accessible via count(). Distinct from Lengthable: Lengthable answers "how many elements does this collection have?"; Countable answers "how many times has this happened?" — it backs counters and run-count accumulators.

type Gettable

type Gettable interface {
	Get(ctx context.Context, args []cty.Value) (cty.Value, error)
}

Gettable is implemented by types whose current value can be read via get(). args contains all arguments after the "thing": typically args[0] is the default value, but implementors may interpret additional args freely.

type Incrementable

type Incrementable interface {
	Increment(ctx context.Context, args []cty.Value) (cty.Value, error)
}

Incrementable is implemented by types that support numeric increment via increment(). args contains all arguments after the "thing": args[0] is the delta; implementors may interpret additional args freely.

type Lengthable

type Lengthable interface {
	Length(ctx context.Context) (int64, error)
}

Lengthable is implemented by types that have a meaningful length, allowing them to be used with length().

type Observable

type Observable interface {
	Observe(ctx context.Context, args []cty.Value) (cty.Value, error)
}

Observable is implemented by types that support observe() (histograms). args contains all arguments after the "thing": args[0] is the observed value; implementors may interpret additional args (e.g. labels) freely.

type Resettable

type Resettable interface {
	Reset(ctx context.Context) error
}

Resettable is implemented by types that can be reset to an initial state via reset(). Used by counter conditions and watchdog triggers. Distinct from the condition-specific clear() semantics on timer/threshold conditions.

type Settable

type Settable interface {
	Set(ctx context.Context, args []cty.Value) (cty.Value, error)
}

Settable is implemented by types whose value can be updated via set(). args contains all arguments after the "thing": args[0] is the value to set; implementors may interpret additional args (e.g. labels) freely.

type Stateful

type Stateful interface {
	State(ctx context.Context) (string, error)
}

Stateful is implemented by types that expose a named internal state via state(). Condition blocks implement this with the four-state vocabulary ("inactive", "pending_activation", "active", "pending_deactivation").

type Stringable

type Stringable interface {
	ToString(ctx context.Context) (string, error)
}

Stringable is implemented by types that have a natural string representation, allowing them to be used with tostring().

type Watchable

type Watchable interface {
	// Watch registers w to receive OnChange notifications when the value changes.
	// Registering the same Watcher twice is a no-op.
	Watch(w Watcher)

	// Unwatch removes a previously registered Watcher.
	// Removing a Watcher that is not registered is a no-op.
	Unwatch(w Watcher)
}

Watchable is implemented by types whose value can be observed for changes. Implementations must be safe to call from multiple goroutines concurrently.

func WatchableFromCtyValue

func WatchableFromCtyValue(val cty.Value) (Watchable, error)

WatchableFromCtyValue extracts a Watchable from a cty capsule value, returning an error if the value is not a capsule or its encapsulated type does not implement Watchable.

type WatchableMixin

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

WatchableMixin encapsulates watcher-list management for Watchable types. Embedding types must use a separate mutex for their own value; watchMu is used only for the watcher slice.

func (*WatchableMixin) NotifyAll

func (m *WatchableMixin) NotifyAll(ctx context.Context, old, new cty.Value)

NotifyAll snapshots the watcher list and calls OnChange on each entry. Must be called after the embedding type's value mutex has been released.

func (*WatchableMixin) Unwatch

func (m *WatchableMixin) Unwatch(w Watcher)

Unwatch removes a previously registered Watcher. Removing an unregistered Watcher is a no-op.

func (*WatchableMixin) Watch

func (m *WatchableMixin) Watch(w Watcher)

Watch registers w to receive OnChange notifications. Registering the same Watcher twice is a no-op.

type Watcher

type Watcher interface {
	// OnChange is called after the Watchable's value has been updated.
	// oldValue is the value immediately before the change.
	// newValue is the value immediately after the change.
	// ctx is the context.Context that was passed to the Set (or Increment) call
	// that triggered the change.
	OnChange(ctx context.Context, oldValue, newValue cty.Value)
}

Watcher receives notifications from a Watchable when its value changes. OnChange is called synchronously, outside the Watchable's internal mutex.

Jump to

Keyboard shortcuts

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