Skip to main content

HostnameResultExt

Trait HostnameResultExt 

Source
pub trait HostnameResultExt<T>: Sized {
    // Required method
    fn hostname_ctx(self, context: &'static str) -> Result<T>;
}
Expand description

Substrate extension trait over Result<T, HostnameError> — the ONE substrate owner of the .map_err(|e| anyhow::anyhow!("<ctx>: {e}")) wrap-shape every reconciler consumer restated by hand at the hostname-formatter → anyhow error boundary. Peer of crate::kube_error::KubeResultExt on the wrap-shape axis; the two traits partition the flatten-wrap space by underlying error type (kube::Error on that peer, HostnameError on this one).

Pre-lift the shape was hand-authored at THREE sites in tatara-reconciler::render::render_routing — each of the three HostnameError-returning hostname primitives (ephemeral_id_from_spec, fmt_fqdn, fmt_fqdn_stable) had ITS consumer restate the SAME closure at the R9 routing-edge render — capture the HostnameError, prepend a static context slug identifying which hostname primitive faulted, delegate the tail to HostnameError’s Display impl via the {e} slot — differing only in the context slug prefix each callsite stamped ("ephemeral_id_from_spec" / "fmt_fqdn (per-instance)" / "fmt_fqdn_stable"). Three hand-authored callsites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold.

Post-lift each callsite reads <hostname-primitive>().hostname_ctx("<slug>")? and the wrap-shape lives at ONE substrate owner here. The composed anyhow::Error’s Display is byte-identical to the pre-lift chain (format!("{ctx}: {e}"), threading the HostnameError’s own Display verbatim into the {e} slot), so operator-facing log output and any error-chain greps still match bytewise. A regression that drifts the separator, swaps the two slots, or wraps the HostnameError with a chain-form source (which would change Display output on the err slot) surfaces at [tests::hostname_ctx_static_str_context_matches_pre_lift_format_bytewise] rather than as silent operator-facing drift across the three pre-lift consumers.

§Naming — hostname_ctx, not anyhow::Context::context

Same discipline as crate::kube_error::KubeResultExt::kube_ctxanyhow::Context::context wraps the source in a chain (so Display emits only the context slug and callers reach the HostnameError via std::error::Error::source traversal), while this trait’s hostname_ctx FLATTENS to a display-prefix shape ("<ctx>: <HostnameError display>") — the pre-lift wire format every consumer’s log output already encoded. Sharing the name would let a caller who has anyhow::Context in scope resolve to the WRONG method (a chain-wrap instead of the display-prefix flatten) and silently change every operator log message.

§Static-slug only (no _with peer yet)

Every current callsite composes its slug at compile time ("ephemeral_id_from_spec", "fmt_fqdn (per-instance)", "fmt_fqdn_stable"); no consumer needs a format!-composed runtime slug. The static-&'static str binding keeps the substrate contract minimal — a future dynamic-slug consumer would add a hostname_ctx_with peer here matching the kube_ctx_with shape, but until then this trait exposes only the static peer.

§#[must_use]

Every consumer threads the ? short-circuit onto its handler’s Result<_, anyhow::Error> return — dropping the wrap swallows the hostname-format failure entirely, which is never the intended semantic (a rejected DNS label at emit time silently produces a resource with a "" FQDN slot that the K8s API server accepts and then no downstream Ingress / DNSEndpoint dispatcher can route to). The attribute surfaces that as a warning at every call site.

Theory anchor: THEORY.md §VI.1 (generation over composition — the HostnameError → anyhow-with-display-prefix wrap-shape recurred at three hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger, and is lifted to ONE substrate owner here). THEORY.md §II.1 invariant 5 (composition preserves proofs — a regression that drifts the display-prefix separator or the byte- shape at ONE site surfaces here at the substrate pin rather than as silent operator-facing skew across every render_routing tick).

Required Methods§

Source

fn hostname_ctx(self, context: &'static str) -> Result<T>

Wrap the HostnameError (if any) with a static context prefix, producing an anyhow::Result whose error Display reads exactly "<context>: <HostnameError display>".

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementations on Foreign Types§

Source§

impl<T> HostnameResultExt<T> for Result<T, HostnameError>

Source§

fn hostname_ctx(self, context: &'static str) -> Result<T>

Implementors§