Documentation
¶
Overview ¶
Package core supplies foundational, capability-oriented abstractions for building composable CLI and service applications on top of strongly typed, generic configuration values. The design emphasizes:
- Progressive enhancement: Optional features (I/O, logging, metrics) surface through small extension interfaces rather than a monolithic context.
- Type safety via generics: Actions receive their full concrete configuration type (T) without casting, reflection, or map indirection.
- Explicit capability discovery: Helper probes return (value, ok) allowing graceful degradation when a feature is absent.
- Testability: Slim interfaces plus an unimplemented configuration reduce boilerplate in fixtures and mock setups.
Concurrency Guidance:
- Prefer immutable configuration structs after construction.
- Treat optional capabilities as independent; absence is not an error.
- Helper probes (Stdin, Stdout, Logger, Observability) never panic.
To introduce a new capability (e.g., TracingAppContext):
- Define an interface embedding AppContext[T] plus accessor(s).
- Provide a generic helper: func Tracing[T ActionConfig](ctx AppContext[T]) (..., bool).
- Implement the interface only in contexts needing that feature.
Error Handling Philosophy: Capability helpers favor presence checks over sentinel errors, promoting resilient, feature-adaptive code paths without tightly coupling execution logic to wiring.
Overall, this package encourages clear ownership of configuration, lean interfaces, and explicit optional feature discovery—yielding maintainable, testable application composition in Go. Package core defines the foundational abstractions for building CLI / service style applications around a strongly-typed, generic configuration, optional pipeline I/O, logging, and observability features.
What is a "configuration" ¶
In this model a "configuration" is any concrete type (struct) implementing the ActionConfig interface. It encapsulates all resources required by an application: log level, certificate material, secret source DSNs, tracing endpoint, metrics address, etc. By coding against the ActionConfig interface instead of a concrete struct, application logic can remain decoupled from the mechanism of loading / assembling configuration (flags, env, files, remote stores). Each application (or sub-command) is free to introduce its own richer configuration type while still satisfying the minimal contract.
Why use a generic (T ActionConfig) ¶
The generic parameter T on AppContext and related helpers provides:
- Callers that obtain [AppContext.Config] receive the full concrete type (T), not just the interface, eliminating repetitive casts.
- Additional fields unique to a given application configuration are immediately available wherever that specific T is in scope, with no need for map access or reflection-based extraction.
- Each executable unit (command/action) declares exactly which configuration type it expects, improving readability and maintainability.
- Go inlines interface method calls when feasible; generics avoid dynamic type assertions at use sites for the concrete config.
Interfaces and Layering ¶
Rather than bloating AppContext with optional concerns, capabilities are modeled as additional interfaces that can be checked at runtime:
- Stdin()/Stdout(): Declarative access to input/output streams enabling pipeline-friendly commands without imposing streams on all contexts.
LoggerAppContext[T]:
- Log(): Provides a slog.Handler for structured logging emission without forcing every context to carry a logger.
- Observability(): Grants metrics instrumentation (Metrics interface) when available; absent contexts remain lightweight.
This list is not exhaustive, since runtime implementation technically MAY introduce some custom interfaces, however, it's highly recommended to keep the number of such interfaces minimal to reduce complexity.
Design Principles ¶
- Separation of Concerns: Core logic depends only on interfaces; wiring layers populate concrete implementations.
- Progressive Enhancement: Features (I/O, logging, metrics) are additive.
- Testability: UnimplementedActionConfig + small interfaces ease mock creation.
- Explicit Capability Discovery: Boolean return pattern communicates optionality.
- Type Safety via Generics: Reduces accidental mismatches and casts.
Extension Guidance ¶
To introduce new contextual capabilities (e.g., TracingAppContext, or whatever you want), define a new interface embedding AppContext[T] plus accessor methods, and supply a helper similar to Logger() or Observability().
By consolidating these patterns, the package offers a flexible, strongly typed, capability-driven foundation for building composable Go applications.
Index ¶
- Constants
- func BuildContext(name AppName, version AppVersion, pipeline Pipeline) (ctx context.Context, cancel context.CancelFunc)
- func Config[T ActionConfig](ctx AppContext[T]) T
- func GetAllParseFunc() map[reflect.Type]func(context.Context, string) (any, error)deprecated
- func GetParseFunc(typ reflect.Type) (f func(context.Context, string) (any, error), ptrDepth int, ok bool)
- func Logger[T ActionConfig](ctx AppContext[T]) (slog.Handler, bool)
- func RegisterEnvParser[T any](f EnvParamParserFunc[T])
- func RunJobs(ctx context.Context, jobs ...func(context.Context) error) error
- func Stdin[T ActionConfig](ctx AppContext[T]) (io.Reader, bool)
- func Stdout[T ActionConfig](ctx AppContext[T]) (io.Writer, bool)
- func WithAppName(ctx context.Context, v AppName) context.Context
- func WithPipelines(ctx context.Context, p Pipeline) context.Context
- func WithVersion(ctx context.Context, v AppVersion) context.Context
- type AcquireData
- type ActionConfig
- type ActionFunc
- type AppContext
- type AppName
- type AppVersion
- func (v AppVersion) CommitHash() ([sha1.Size]byte, bool)
- func (v AppVersion) Date() (time.Time, bool)
- func (v AppVersion) ShortHash() (short [7]byte, valid bool)
- func (v AppVersion) String() (res string)
- func (v AppVersion) Valid() bool
- func (v AppVersion) Version() (string, bool)
- func (v AppVersion) VersionCommit() (res string, valid bool)
- type ConfigureData
- type EnvParam
- type EnvParamParserFunc
- type ExitCode
- type LoggerAppContext
- type Metrics
- type ObservabilityAppContext
- type Pipeline
- type PipelineAppContext
- type ShutdownData
- type UnimplementedActionConfig
- func (UnimplementedActionConfig) ClientCertPaths() (cert, key string)
- func (UnimplementedActionConfig) GetCertPaths() []string
- func (UnimplementedActionConfig) GetLogLevel() slog.Level
- func (UnimplementedActionConfig) GetMetricsAddr() *url.URL
- func (UnimplementedActionConfig) GetSecretDSNs() map[string]*url.URL
- func (UnimplementedActionConfig) GetTraceEndpoint() *url.URL
- type UnsafeActionConfig
Constants ¶
const ( // DefaultAppName is the fallback stable identifier used when no explicit // name is provided. DefaultAppName = "unknown" // DefaultAppTitle is the human-friendly title used when no explicit // application title is supplied. DefaultAppTitle = "Unknown Application" )
const ( DefaultVersion = "v0.0.0-dev" DefaultCommit = "none" DefaultDate = "unknown" DefaultDateFormat = time.RFC3339 )
Variables ¶
This section is empty.
Functions ¶
func BuildContext ¶
func BuildContext( name AppName, version AppVersion, pipeline Pipeline, ) ( ctx context.Context, cancel context.CancelFunc, )
BuildContext constructs a root application context annotated with identity (AppName), version (AppVersion) and pipeline I/O (Pipeline), and automatically wired to OS interrupt signals. The returned cancel function MUST be invoked by the caller to release signal resources.
Cancellation Sources:
- Incoming SIGINT / SIGKILL (os.Interrupt, os.Kill) trigger context cancellation for graceful shutdown.
- Manual invocation of the returned cancel function.
The supplied Pipeline is stored for later retrieval via PipelinesFromContext. Prefer passing explicit version / name values; fallback defaults remain available through helper extraction funcs.
func Config ¶
func Config[T ActionConfig](ctx AppContext[T]) T
Config returns the concrete configuration value (type T) carried by the supplied AppContext. It is a thin, inline-able alias for ctx.Config() provided for symmetry with the Name and Version helpers and to improve readability at call sites.
Characteristics:
- Zero overhead: No copying beyond what ctx.Config() itself performs; the generic accessor typically inlines.
- Strongly typed: Callers receive the full concrete T, enabling direct field / method access without casts or interface assertions.
- Concurrency: Safety of the returned value depends on the AppContext implementation. Prefer immutable configuration structs after initialization.
Usage Example:
cfg := Config(appCtx) // obtains T lvl := cfg.GetLogLevel() // invoke concrete methods directly
Mutability Guidance: Modifying cfg is discouraged unless the specific configuration type documents that such mutation is safe. Treat configuration as read-only in most application code.
Equivalent Call:
Config(appCtx) == appCtx.Config()
func GetAllParseFunc
deprecated
func GetParseFunc ¶
func Logger ¶
func Logger[T ActionConfig](ctx AppContext[T]) (slog.Handler, bool)
Logger attempts to extract a slog.Handler logging capability from the provided AppContext. It performs a single type assertion against LoggerAppContext. Returns (handler, true) when the capability is present, or (nil, false) if the context does not supply structured logging.
Semantics:
- Absence is not an error; callers should branch on the boolean and degrade gracefully (e.g., use a no-op handler or skip logging).
- The returned slog.Handler SHOULD be safe for concurrent use; this is an implementation concern of the concrete AppContext.
Example:
if h, ok := Logger(appCtx); ok {
h.Handle(ctx, slog.Record{ /* ... */ })
}
func RegisterEnvParser ¶
func RegisterEnvParser[T any](f EnvParamParserFunc[T])
RegisterEnvParser registers a parser function for the concrete type T, enabling custom environment value decoding for that type.
Usage:
- Call only from init() to ensure deterministic, one-time registration.
- The registration is global; duplicate registrations for the same T panic.
- The lookup key is exactly reflect.Type. Pointer forms must be registered separately if they require distinct parsing.
Panics:
- If a parser for T is already present.
Concurrency:
- Expected to run during startup before other goroutines; no synchronization.
Parser Contract (parseFunc):
- Must be pure (no hidden global mutations).
- Should not panic on malformed input; return an error instead.
- May use context for cancellation or ancillary lookups.
Example:
func init() {
RegisterEnvParser[MyType](func(ctx context.Context, raw string) (MyType, error) {
var v MyType
if err := v.Unmarshal(raw); err != nil {
return MyType{}, err
}
return v, nil
})
}
Hint: extract parser function to separated named function for clarity. It's recommended to keep it private.
func RunJobs ¶
RunJobs concurrently executes the provided job functions, cancelling all remaining work as soon as any job returns a non-context error. Each job receives a shared derived context that is cancelled on the first failure (excluding correct context cancellations).
Execution Model:
- All jobs start immediately in separate goroutines.
- A derived context is cancelled after the first non-context error.
- Subsequent jobs should observe cancellation and return promptly.
Error Handling:
- Errors equal to context.Canceled or context.DeadlineExceeded are suppressed.
- All other errors are collected and returned via errors.Join.
- If no non-context errors occur, the function returns nil.
Cancellation Semantics:
- Caller cancellation (ctx) propagates to all jobs.
- Internal cancellation (first failure) does not mask earlier successful results.
Ordering & Aggregation:
- Error order is not guaranteed.
- Multiple failures are joined; callers should inspect errors.Is/errors.As.
Usage Example:
err := RunJobs(ctx,
func(c context.Context) error { return workA(c) },
func(c context.Context) error { return workB(c) },
)
if err != nil {
// handle joined errors
}
func Stdin ¶
func Stdin[T ActionConfig](ctx AppContext[T]) (io.Reader, bool)
func Stdout ¶
func Stdout[T ActionConfig](ctx AppContext[T]) (io.Writer, bool)
func WithAppName ¶
WithAppName returns a derived context carrying the provided AppName. It is a lightweight convenience used during application startup to annotate the root context with identity metadata that downstream code can retrieve via AppNameFromContext.
The stored value is immutable.
func WithPipelines ¶
WithPipelines stores a Pipeline in a derived context for later retrieval. Use PipelinesFromContext to extract it; if absent, a cached default is provided.
func WithVersion ¶
func WithVersion(ctx context.Context, v AppVersion) context.Context
WithVersion attaches an AppVersion to a derived context for later retrieval via VersionFromContext. The stored value is immutable.
Types ¶
type AcquireData ¶
type AcquireData struct {
}
AcquireData inherits configuration values and allows acquisition logic (e.g., binding network listeners). Additional runtime derived fields can be layered in future without breaking implementers.
type ActionConfig ¶
type ActionConfig interface {
// log level
GetLogLevel() slog.Level
// paths to CA certificates. May return zero slice.
GetCertPaths() []string
// path to client certificate
ClientCertPaths() (cert, key string)
// secret DSNs
//
// TODO: two engines with one protocol? like vault-1:// and vault-2://?
GetSecretDSNs() map[string]*url.URL
// OTEL trace endpoint
GetTraceEndpoint() *url.URL
// Prometheus metrics address to listen. If nil, metrics export is disabled
GetMetricsAddr() *url.URL
// contains filtered or unexported methods
}
ActionConfig is the minimal contract every concrete configuration must satisfy. The private _ActionConfig method acts as a marker to avoid accidental interface satisfaction by unrelated types. Implementations typically hold immutable, application-wide tunables (log level, certificate paths, metrics endpoint, etc.).
type ActionFunc ¶
type ActionFunc[T ActionConfig] func(ctx context.Context, appCtx AppContext[T]) ExitCode
ActionFunc is the canonical executable signature for an application action or subcommand. It receives:
- context.Context: For cancellation, deadlines, and cross-cutting values.
- AppContext[T]: A strongly typed application context exposing identity and configuration.
type AppContext ¶
type AppContext[T ActionConfig] interface { // Stable application identifier (not necessarily user-facing). Name() AppName // Semantic or custom version descriptor Version() AppVersion // Concrete configuration value of type T. // // Implementations SHOULD document whether Config() is safe for concurrent // use. Immutable configs are preferred to simplify synchronization. Config() T }
type AppName ¶
type AppName struct {
// contains filtered or unexported fields
}
AppName represents both a machine-oriented stable identifier and a human-friendly display title for an application. Empty fields are normalized to defaults by NewAppName guaranteeing that calls to AppName.Name/AppName.Title always succeed with a non-empty fallback.
func AppNameFromContext ¶
AppNameFromContext extracts an AppName previously attached with WithAppName. When no value is present a stable default is returned and the boolean is false, allowing callers to distinguish between implicit and explicit identity.
func Name ¶
func Name[T ActionConfig](ctx AppContext[T]) AppName
func NewAppName ¶
NewAppName constructs a new AppName, normalizing empty inputs to DefaultAppName/DefaultAppTitle. Prefer passing explicit values when available; defaults keep logs and telemetry consistent for prototypes.
type AppVersion ¶
type AppVersion struct {
// contains filtered or unexported fields
}
AppVersion represents build-time version metadata including semantic version string, commit hash and build date. Raw values preserve original inputs; normalized fields store validated / parsed representations. AppVersion.Valid reports aggregate correctness allowing callers to warn on malformed embeddings without failing hard.
func NewVersion ¶
func NewVersion(versionRaw, commitRaw, dateRaw string) AppVersion
NewVersion creates a version abstraction, based on raw compiled static variables.
IMPORTANT: this function WON'T panic or return an error on bad input. Since it's expected to provide correct values on compile-time, returning error or panicking might broke whole build.
Instead, Version has AppVersion.Valid method, that returns whether the object is correct or not. Caller may use this info to warn user that version info is invalid.
func Version ¶
func Version[T ActionConfig](ctx AppContext[T]) AppVersion
func VersionFromContext ¶
func VersionFromContext(ctx context.Context) (AppVersion, bool)
VersionFromContext extracts an AppVersion previously attached with WithVersion. When absent it returns a lazily constructed default and false. Callers can use the boolean to differentiate explicit vs. fallback version data.
func (AppVersion) CommitHash ¶
func (v AppVersion) CommitHash() ([sha1.Size]byte, bool)
CommitHash returns the parsed full commit hash bytes and validity.
func (AppVersion) Date ¶
func (v AppVersion) Date() (time.Time, bool)
Date returns the parsed build timestamp and validity.
func (AppVersion) ShortHash ¶
func (v AppVersion) ShortHash() (short [7]byte, valid bool)
ShortHash extracts the first 7 bytes of the commit hash along with validity.
func (AppVersion) String ¶
func (v AppVersion) String() (res string)
String returns a human-friendly composite string including raw version, short commit hash (or raw commit when invalid) and build date (parsed or raw). This is suitable for logging.
func (AppVersion) Valid ¶
func (v AppVersion) Valid() bool
Valid reports whether version, commit hash and build date were all parsed successfully.
func (AppVersion) Version ¶
func (v AppVersion) Version() (string, bool)
Version returns the normalized semantic version string and a boolean denoting validity.
func (AppVersion) VersionCommit ¶
func (v AppVersion) VersionCommit() (res string, valid bool)
VersionCommit combines semantic version and short commit hash as version#<short-hash>.
More info: https://github.com/semver/semver/issues/614
type ConfigureData ¶
type ConfigureData struct {
AppCert tls.Certificate
Logger slog.Handler
Secrets secrets.Engine
Metric metric.MeterProvider
Trace trace.TracerProvider
Pool *x509.CertPool
Version AppVersion
}
ConfigureData provides foundational wiring inputs for [EnvParam.Configure]. Fields may be nil when a capability is absent (e.g., Secrets, Metric). Implementations should not mutate shared values.
type EnvParam ¶
type EnvParam interface {
Configure(ctx context.Context, data *ConfigureData) error
Acquire(ctx context.Context, data *AcquireData) error
Shutdown(ctx context.Context, data *ShutdownData) error
}
EnvParam models a lifecycle-aware configurable entity exposed through environment values (e.g., servers, listeners, credentials). The methods are invoked in order: [EnvParam.Configure] -> [EnvParam.Acquire] -> [EnvParam.Shutdown]. Implementations should be idempotent where feasible and release resources in Shutdown.
type EnvParamParserFunc ¶
type ExitCode ¶
type ExitCode uint8
ExitCode represents the process exit status produced by an ActionFunc. The uint8 size mirrors conventional POSIX exit ranges (0–255) and communicates intent more clearly than a bare int.
type LoggerAppContext ¶
type LoggerAppContext[T ActionConfig] interface { AppContext[T] Log() slog.Handler }
type Metrics ¶
type Metrics interface {
slog.Handler
trace.TracerProvider
metric.MeterProvider
}
Metrics bundles logging, tracing and metrics emission capabilities into a single optional interface exposed by ObservabilityAppContext. It embeds slog.Handler for structured logging plus OTel tracer and meter providers. Implementations SHOULD be safe for concurrent use by multiple goroutines.
func NoopMetrics ¶
func NoopMetrics() Metrics
NoopMetrics returns a Metrics implementation that discards all log records and uses no-op tracer / meter providers. This is a lightweight default for tests or commands that do not yet wire observability features.
func Observability ¶
func Observability[T ActionConfig](ctx AppContext[T]) (Metrics, bool)
type ObservabilityAppContext ¶
type ObservabilityAppContext[T ActionConfig] interface { AppContext[T] Observability() Metrics }
type Pipeline ¶
type Pipeline struct {
// contains filtered or unexported fields
}
Pipeline captures standard stream handles plus a flag indicating whether stdin appears to be connected to a non-tty source (e.g., pipe or file) for pipeline-aware behavior. Accessors return the raw handles; callers should not close them directly.
func PipelineFromFiles ¶
PipelineFromFiles builds a Pipeline from explicit *os.File handles (nil values are replaced with process defaults). The IsPipeline bit is derived by inspecting stdin's mode to detect non-interactive usage.
func PipelinesFromContext ¶
PipelinesFromContext retrieves a Pipeline previously attached with WithPipelines. The boolean reports whether an explicit value was set. When false a lazily-created default wrapping the process stdio streams is returned.
func (Pipeline) IsPipeline ¶
IsPipeline reports whether stdin appears to be a non-interactive source (pipe/file). This is useful for adapting behavior (e.g., buffered reads).
type PipelineAppContext ¶
type PipelineAppContext[T ActionConfig] interface { AppContext[T] Stdin() io.Reader Stdout() io.Writer }
type ShutdownData ¶
type ShutdownData struct {
}
ShutdownData inherits acquisition context for graceful teardown. Implementations should attempt best-effort cleanup returning rich errors rather than panicking.
type UnimplementedActionConfig ¶
type UnimplementedActionConfig struct {
UnsafeActionConfig
}
UnimplementedActionConfig is a convenient neutral stub returning zero / nil values. It is useful for tests, prototypes, or commands that do not yet need to surface configuration. All getters return inert defaults (e.g., slog.LevelInfo, nil slices, nil URLs).
func (UnimplementedActionConfig) ClientCertPaths ¶
func (UnimplementedActionConfig) ClientCertPaths() (cert, key string)
func (UnimplementedActionConfig) GetCertPaths ¶
func (UnimplementedActionConfig) GetCertPaths() []string
func (UnimplementedActionConfig) GetLogLevel ¶
func (UnimplementedActionConfig) GetLogLevel() slog.Level
func (UnimplementedActionConfig) GetMetricsAddr ¶
func (UnimplementedActionConfig) GetMetricsAddr() *url.URL
func (UnimplementedActionConfig) GetSecretDSNs ¶
func (UnimplementedActionConfig) GetSecretDSNs() map[string]*url.URL
func (UnimplementedActionConfig) GetTraceEndpoint ¶
func (UnimplementedActionConfig) GetTraceEndpoint() *url.URL
type UnsafeActionConfig ¶
type UnsafeActionConfig struct{}
UnsafeActionConfig is an empty opt-in marker that satisfies ActionConfig via embedding. Use it when quickly scaffolding a config type; replace with explicit methods as requirements grow.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
contrib
|
|
|
params/port
Package port supplies environment-parsed network listener parameters with optional TLS wrapping.
|
Package port supplies environment-parsed network listener parameters with optional TLS wrapping. |
|
params/grpc
module
|
|
|
params/http
module
|
|
|
runtime
module
|
|
|
secrets
module
|
|
|
Package secrets defines interfaces and helpers for retrieving secret values (credentials, configuration blobs) from pluggable engines.
|
Package secrets defines interfaces and helpers for retrieving secret values (credentials, configuration blobs) from pluggable engines. |