#[non_exhaustive]pub struct ExecResult {
pub code: i64,
pub err: String,
pub data: Option<Value>,
pub did_spill: bool,
pub original_code: Option<i64>,
pub content_type: Option<String>,
pub baggage: BTreeMap<String, String>,
pub latch: Option<Box<LatchRequest>>,
/* private fields */
}Expand description
The result of executing a command or pipeline.
$? in script syntax is the POSIX exit code (an integer). To read the
previous command’s structured .data (or its captured stdout) from
inside a script, use the kaish-last builtin and pipe / capture its
output. Inside Rust callers, read .data, .text_out(), etc. directly.
Notes on the fields:
code— exit code (0 = success)err— error message if failedout— raw stdout as stringdata— structured data; only set by builtins/tools that opt in (e.g.seq,jq,cut,find,glob,split). External commands never populate this — pipe their stdout throughjqto get it.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.code: i64Exit code. 0 means success.
err: StringRaw standard error as a string.
data: Option<Value>Structured data — only populated when a builtin/tool sets it explicitly.
Stdout is never sniffed; this stays None for external commands.
did_spill: boolTrue if output was capped and lost data. Either the output limiter
spilled the overflow to disk (the out message carries the path),
truncated it in memory (Memory spill mode — head+tail only, no
recoverable file), or an external command’s stdout overflowed its
fixed-size capture ring with output limiting off (GH #191) — the
capture buffer evicted its head with no spill file at all. All cases
remap the exit code to 3.
original_code: Option<i64>The command’s original exit code before spill logic overwrote it with 2 or 3.
Present only when did_spill is true and code was changed.
content_type: Option<String>MIME content type hint (e.g., “text/markdown”, “image/svg+xml”). When set, downstream consumers can use this instead of sniffing content.
baggage: BTreeMap<String, String>Opaque key-value context propagated from tools through execution. Intermediaries (kaish) carry but don’t interpret. Consumers read known keys. Follows W3C Baggage semantics — useful for OTel trace propagation, application-level hints, etc.
latch: Option<Box<LatchRequest>>A pending confirmation-latch request — the control-plane signal a
gated destructive op (rm/kaish-trash/an overwrite under set -o latch) raises alongside exit code 2. First-class and typed, distinct
from the data-plane .data: a stdout redirect clears .data but never
this. Read it via Self::latch_request; set it via
ToolCtx::latch_result.
Boxed: ExecResult is returned up every level of deep $()/pipeline
recursion, and LatchRequest is ~150 bytes — inline it would fatten
every stack frame and cost interpreter stack headroom (GH #46/#47). The
box is allocated only when a latch actually fires. Serializes identically
to an unboxed Option (Box is transparent to serde).
Implementations§
Source§impl ExecResult
impl ExecResult
Sourcepub fn success(out: impl Into<String>) -> ExecResult
pub fn success(out: impl Into<String>) -> ExecResult
Create a successful result with output.
Sourcepub fn with_output(output: OutputData) -> ExecResult
pub fn with_output(output: OutputData) -> ExecResult
Create a successful result with structured output data.
The OutputData is the source of truth. Text is materialized lazily
via text_out() when needed (pipes, redirects, command substitution).
Sourcepub fn success_bytes(bytes: Vec<u8>) -> ExecResult
pub fn success_bytes(bytes: Vec<u8>) -> ExecResult
Create a successful result whose stdout is raw bytes (binary payload).
Sourcepub fn success_text_or_bytes(bytes: Vec<u8>) -> ExecResult
pub fn success_text_or_bytes(bytes: Vec<u8>) -> ExecResult
Create a successful result from bytes, applying the coercion rule at the
producer: valid UTF-8 becomes a text result, anything else a binary
Bytes result. This is the single place pass-through/decoder builtins
(cat, head -c, base64 -d, xxd -r, tee, …) decide text-vs-binary,
so text workflows stay text and only real binary flows as bytes.
Sourcepub fn success_data(data: Value) -> ExecResult
pub fn success_data(data: Value) -> ExecResult
Create a successful result with structured data.
Sourcepub fn success_with_data(out: impl Into<String>, data: Value) -> ExecResult
pub fn success_with_data(out: impl Into<String>, data: Value) -> ExecResult
Create a successful result with both text output and structured data.
Use this when a command should have:
- Text output for pipes and traditional shell usage
- Structured data for iteration and programmatic access
The data field takes precedence for command substitution in contexts
like for i in $(cmd) where the structured data can be iterated.
Sourcepub fn failure(code: i64, err: impl Into<String>) -> ExecResult
pub fn failure(code: i64, err: impl Into<String>) -> ExecResult
Create a failed result with an error message.
Sourcepub fn from_output(
code: i64,
stdout: impl Into<String>,
stderr: impl Into<String>,
) -> ExecResult
pub fn from_output( code: i64, stdout: impl Into<String>, stderr: impl Into<String>, ) -> ExecResult
Create a result from raw output streams.
data is left empty — kaish does not sniff stdout for JSON. To get
structured iteration from an external command, pipe through jq:
for i in $(curl ... | jq .); do ....
Sourcepub fn with_output_and_text(
output: OutputData,
text: impl Into<String>,
) -> ExecResult
pub fn with_output_and_text( output: OutputData, text: impl Into<String>, ) -> ExecResult
Create a successful result with structured output and explicit pipe text.
Use this when a builtin needs custom text formatting that differs from
the canonical OutputData::to_canonical_string() representation.
Sourcepub fn from_parts(
code: i64,
out: String,
err: String,
data: Option<Value>,
) -> ExecResult
pub fn from_parts( code: i64, out: String, err: String, data: Option<Value>, ) -> ExecResult
Create a result from parts — for kernel struct literal sites.
Sourcepub fn with_code(self, code: i64) -> ExecResult
pub fn with_code(self, code: i64) -> ExecResult
Builder: set the exit code, returning self for chaining.
Sourcepub fn text_out(&self) -> Cow<'_, str>
pub fn text_out(&self) -> Cow<'_, str>
Get text output, materializing from OutputData on demand.
Returns the text payload if non-empty, otherwise falls back to
OutputData::to_canonical_string(). This is the canonical way to
get text for pipes, command substitution, and file redirects.
Binary payloads decode lossily here (U+FFFD for invalid UTF-8).
Several builtins already produce a Bytes payload (cat/head/tail/
base64 -d/xxd -r/dd/tee/external commands), so this lossy path
IS reachable — callers that need to catch binary rather than silently
mangle it should use Self::try_text_out instead, which loud-errors
with BinaryNotText on invalid UTF-8. See docs/binary-data.md.
Sourcepub fn try_text_out(&self) -> Result<Cow<'_, str>, BinaryNotText>
pub fn try_text_out(&self) -> Result<Cow<'_, str>, BinaryNotText>
Get text output, or a BinaryNotText error if the payload is binary
and not valid UTF-8. This is the boundary guard for text sinks (echo,
interpolation, $() capture) — adopted as those paths grow byte
awareness (Phase 2). Valid-UTF-8 bytes coerce; everything else is loud.
Sourcepub fn out_bytes(&self) -> Option<&[u8]>
pub fn out_bytes(&self) -> Option<&[u8]>
Raw bytes if this result carries a binary payload, else None.
Sourcepub fn output(&self) -> Option<&OutputData>
pub fn output(&self) -> Option<&OutputData>
Get a reference to structured output data.
Sourcepub fn has_output(&self) -> bool
pub fn has_output(&self) -> bool
True if structured output data is present.
Sourcepub fn set_out_bytes(&mut self, b: Vec<u8>)
pub fn set_out_bytes(&mut self, b: Vec<u8>)
Replace .out with raw bytes (binary payload).
Sourcepub fn push_out(&mut self, s: &str)
pub fn push_out(&mut self, s: &str)
Append text to .out. A binary payload is appended to as raw UTF-8 bytes.
Sourcepub fn clear_stdout(&mut self)
pub fn clear_stdout(&mut self)
Drop every representation of stdout: the text .out, the structured
.output, and the data-plane .data sideband. Used when a stdout
redirect (> file, >> file, &> file, 1>&2) has consumed the
command’s output — the bytes went to the file (or stderr), so nothing
flows onward to a pipe, a $(...) capture, or the .data sideband.
Clearing all three together keeps them from drifting: a redirect that
cleared .out/.output but left .data would leak structured data past
its own redirect (x=$(fromjson … > file) capturing the value instead
of "").
The confirmation-latch request is untouched by design: it is a
control-plane signal on the dedicated .latch field, not stdout, so a
redirect can’t drop it — rm precious > log still gates.
Sourcepub fn set_output(&mut self, o: Option<OutputData>)
pub fn set_output(&mut self, o: Option<OutputData>)
Replace .output.
Sourcepub fn take_output(&mut self) -> Option<OutputData>
pub fn take_output(&mut self) -> Option<OutputData>
Take .output, leaving None.
Sourcepub fn materialize(&mut self)
pub fn materialize(&mut self)
Materialize: if .out is empty and .output is present,
populate .out from canonical string and clear .output.
Sourcepub fn take_output_for_stream(&mut self) -> Option<OutputData>
pub fn take_output_for_stream(&mut self) -> Option<OutputData>
Take .output only if .out is empty (no custom text),
so caller can stream directly without materializing.
Sourcepub fn latch_request(&self) -> Option<LatchRequest>
pub fn latch_request(&self) -> Option<LatchRequest>
The pending confirmation-latch request, if this result is a latch gate.
A gated destructive op (rm/kaish-trash/an overwrite under set -o latch) returns exit code 2 with the typed request on the .latch field.
This is the seam an embedder hooks to apply preapproval policy or a model
review before re-running the command with --confirm=<nonce>, instead of
string-matching the error. A plain usage error (also exit 2, but no
latch) returns None. Unlike the data-plane .data, .latch survives
--json formatting, so this is safe to call before or after it.
Sourcepub fn with_content_type(self, ct: impl Into<String>) -> ExecResult
pub fn with_content_type(self, ct: impl Into<String>) -> ExecResult
Set content type hint, returning self for chaining.
Trait Implementations§
Source§impl Clone for ExecResult
impl Clone for ExecResult
Source§fn clone(&self) -> ExecResult
fn clone(&self) -> ExecResult
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ExecResult
impl Debug for ExecResult
Source§impl Default for ExecResult
impl Default for ExecResult
Source§fn default() -> ExecResult
fn default() -> ExecResult
Source§impl<'de> Deserialize<'de> for ExecResult
impl<'de> Deserialize<'de> for ExecResult
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<ExecResult, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<ExecResult, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
Source§impl From<ToolResult> for ExecResult
impl From<ToolResult> for ExecResult
Source§fn from(result: ToolResult) -> ExecResult
fn from(result: ToolResult) -> ExecResult
The symmetric peer of From<ExecResult> for ToolResult above — every
field that direction preserves, this direction must preserve too, or a
backend-registered tool’s structured data/content_type/baggage
silently vanishes crossing back into the kernel (the embedder seam:
x=$(embedder_tool) and for r in $(embedder_tool) need .data to
see typed results, not just stdout text).
data uses json_to_value_no_envelope rather than the internal
round-trip conversion: a backend tool’s JSON is external input, so an
object shaped like the byte envelope must stay a plain record, never
silently auto-decode to Value::Bytes.