diff --git a/README.md b/README.md index 0061052..155c900 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,21 @@ Implementations own engine configuration; consumers share `Runtime`, reusable `Runtime.Compile` and `CompileDebug` finish compilation before returning a plan. Syntax and compiler errors are immediate. Plans support repeated and concurrent sessions with independent parameters and filesystem configuration. -`Params() ([]string, error)` returns a caller-owned snapshot or a metadata -retrieval error. It does not take a context. +`Plan.Params(ctx) ([]string, error)` returns a caller-owned snapshot of parameter +names or a metadata retrieval error. An empty list with a nil error means the plan +has no parameters; it is distinct from a retrieval failure. + +`Runtime.Version(ctx) (Version, error)` reports the version of the runtime +implementation represented by that `Runtime`. A remote adapter reports the remote +runtime's version. This is separate from the embedding application's, CLI's, +daemon/server's, or transport protocol's version. `Version` is an opaque string-backed +value whose `String()` method preserves the implementation-provided value unchanged. +Values such as `v2.0.0-alpha.55`, `2.0.0`, `dev`, and `unknown` are valid; UAPI does +not parse, normalize, or validate them as semantic versions. + +Both metadata operations may fail and may require remote I/O. Local implementations +may return immediately. UAPI imposes no transport-specific behavior or caching +requirements. Each object releases the resources it owns. Owning runtimes reject subsequent work according to their closed-state semantics. Borrowing adapters may document @@ -58,7 +71,8 @@ The output fields and their serialized representation are unchanged. Transports preserve output presence through their own representations. Non-nil caller contexts control cancellation of `Run`, `Compile`, -`CompileDebug`, `NewSession`, and `NewDebugSession`. Cancellation errors +`CompileDebug`, `NewSession`, `NewDebugSession`, `Plan.Params`, and `Runtime.Version`. +All of these operations require a non-nil context. Cancellation errors preserve `context.Canceled` and `context.DeadlineExceeded` through `errors.Is`. Implementations need not derive operation contexts to coordinate parent Close. They may use internal contexts for their own resources and may translate portable diff --git a/doc.go b/doc.go index 382e4f3..d25d668 100644 --- a/doc.go +++ b/doc.go @@ -1,3 +1,13 @@ // Package api defines implementation-independent contracts for compiling, // executing, and debugging Ferret queries. +// +// Plan.Params and Runtime.Version retrieve metadata and may involve remote I/O. +// Both require non-nil caller contexts, and cancellation errors must preserve +// context.Canceled and context.DeadlineExceeded through errors.Is. The API imposes +// no transport-specific behavior or caching requirements. +// +// Plan.Params returns a caller-owned snapshot; an empty parameter list is distinct +// from a metadata retrieval error. Runtime.Version returns an opaque Version for +// the represented runtime implementation, including the remote runtime for a +// remote adapter, rather than the host application, CLI, server, or transport. package api diff --git a/output_contract_fixture_test.go b/output_contract_fixture_test.go index e09b68e..af41639 100644 --- a/output_contract_fixture_test.go +++ b/output_contract_fixture_test.go @@ -23,6 +23,10 @@ var ( _ api.Session = (*outputSession)(nil) ) +func (r *outputRuntime) Version(context.Context) (api.Version, error) { + return api.Version(""), nil +} + func (r *outputRuntime) Run(context.Context, api.Source, ...api.SessionOption) (*api.Output, error) { return r.output, r.err } diff --git a/plan.go b/plan.go index c308f83..59598a9 100644 --- a/plan.go +++ b/plan.go @@ -10,7 +10,6 @@ import ( type ( // Plan represents a compiled program. Compilation finishes before a plan is // returned. Plans support independent sessions and are not consumed by execution. - // Params returns a caller-owned snapshot or an error if metadata cannot be retrieved. // // Close releases plan-owned resources and prevents subsequent session and // debug-session creation. It is idempotent and retains its cleanup result, @@ -18,12 +17,21 @@ type ( // constructors already started. It does not implicitly close or cancel returned // sessions or debug sessions; callers remain responsible for their lifecycle. // - // NewSession and NewDebugSession use non-nil caller contexts for cancellation. + // Params, NewSession, and NewDebugSession use non-nil caller contexts for cancellation. // Close does not cancel those contexts. Callers coordinate work and cleanup // when sessions use plan-owned resources. Plan interface { io.Closer - Params() ([]string, error) + + // Params returns a caller-owned snapshot of the plan's parameter names. + // An empty list with a nil error means the plan has no parameters; failure + // to retrieve metadata returns an error. + // + // ctx must be non-nil. Cancellation errors must preserve context.Canceled + // and context.DeadlineExceeded through errors.Is. Retrieval may involve + // remote I/O; local implementations may return immediately. UAPI imposes + // no transport-specific behavior or caching requirements. + Params(ctx context.Context) ([]string, error) NewSession(ctx context.Context, opts ...SessionOption) (Session, error) NewDebugSession(ctx context.Context, opts ...SessionOption) (debugger.Session, error) } diff --git a/plan_contract_test.go b/plan_contract_test.go index f908de1..3e111d8 100644 --- a/plan_contract_test.go +++ b/plan_contract_test.go @@ -1,6 +1,10 @@ package api_test -import "github.com/MontFerret/api" +import ( + "context" -// Metadata retrieval can fail without adding a context to Plan.Params. -var _ func(api.Plan) ([]string, error) = api.Plan.Params + "github.com/MontFerret/api" +) + +// Parameter metadata retrieval is fallible and context-aware. +var _ func(api.Plan, context.Context) ([]string, error) = api.Plan.Params diff --git a/runtime.go b/runtime.go index d2caf60..707d5c0 100644 --- a/runtime.go +++ b/runtime.go @@ -15,12 +15,23 @@ import ( // adapter and underlying runtime usable. Close does not implicitly cancel // caller-owned work and need not wait for operations already started. // -// Run, Compile, and CompileDebug use non-nil caller contexts for cancellation. +// Run, Compile, CompileDebug, and Version use non-nil caller contexts for cancellation. // Callers coordinate work and cleanup when descendants use parent-owned // resources. Run closes its temporary session and plan, preserving execution // and cleanup errors together with any available encoded output. type Runtime interface { io.Closer + + // Version reports the version of the runtime implementation represented by + // this Runtime. A remote adapter reports its remote runtime's version, not + // the version of the host application, CLI, daemon/server, or transport protocol. + // + // ctx must be non-nil. Cancellation errors must preserve context.Canceled + // and context.DeadlineExceeded through errors.Is. Retrieval may involve + // remote I/O; local implementations may return immediately. UAPI imposes + // no transport-specific behavior or caching requirements. + Version(ctx context.Context) (Version, error) + // Run returns nil output with an error when no output was produced. // A non-nil output with a nil error indicates success, including empty output. // A non-nil output may accompany an error from cleanup or other processing; diff --git a/runtime_contract_test.go b/runtime_contract_test.go new file mode 100644 index 0000000..23e9f37 --- /dev/null +++ b/runtime_contract_test.go @@ -0,0 +1,10 @@ +package api_test + +import ( + "context" + + "github.com/MontFerret/api" +) + +// Runtime version retrieval is fallible and context-aware. +var _ func(api.Runtime, context.Context) (api.Version, error) = api.Runtime.Version diff --git a/version.go b/version.go new file mode 100644 index 0000000..2251b81 --- /dev/null +++ b/version.go @@ -0,0 +1,11 @@ +package api + +// Version is an opaque, implementation-provided runtime version value. +// It preserves the original string without normalization or validation and +// does not require any particular versioning scheme. +type Version string + +// String returns the implementation-provided version string unchanged. +func (v Version) String() string { + return string(v) +} diff --git a/version_test.go b/version_test.go new file mode 100644 index 0000000..eadfaff --- /dev/null +++ b/version_test.go @@ -0,0 +1,27 @@ +package api_test + +import ( + "testing" + + "github.com/MontFerret/api" +) + +func TestVersionStringPreservesValue(t *testing.T) { + for _, tc := range []struct { + name string + value string + }{ + {name: "empty", value: ""}, + {name: "prerelease", value: "v2.0.0-alpha.55"}, + {name: "release", value: "2.0.0"}, + {name: "development", value: "dev"}, + {name: "unknown", value: "unknown"}, + {name: "opaque with whitespace", value: " \tbuild:abc123+custom\n "}, + } { + t.Run(tc.name, func(t *testing.T) { + if got := api.Version(tc.value).String(); got != tc.value { + t.Fatalf("Version.String() = %q, want %q", got, tc.value) + } + }) + } +}