ignition_core/output.rs
1//! Success envelope rendering — the LOCKED Phase-1 output shape.
2//!
3//! Success: `{"ok":true,"profile":<name|null>,"data":{...}}` with exactly
4//! those top-level fields (the failure twin lives in [`crate::error`]).
5//! Field order is declaration order and part of the golden-file contract.
6//!
7//! Core NEVER prints — these functions return `String`s; the binary owns
8//! stdout/stderr (ARCHITECTURE.md layering invariant).
9
10use serde::Serialize;
11
12use crate::error::ErrorEnvelope;
13
14/// LOCKED success envelope: exactly the top-level fields `ok`, `profile`,
15/// `data` — changing the set is a breaking change for agents.
16#[derive(Debug, Serialize)]
17pub struct JsonEnvelope<'a, T: Serialize + ?Sized> {
18 /// Always `true` in this envelope.
19 pub ok: bool,
20 /// Active profile echoed in every output (CORE-01); `None` until config
21 /// resolution lands.
22 pub profile: Option<&'a str>,
23 /// The command's payload.
24 pub data: &'a T,
25}
26
27/// Render a success envelope: pretty (default `--json`) or one-line compact
28/// (`--compact`). Field order: `ok`, `profile`, `data`.
29///
30/// # Panics
31/// Panics only if serialization of a well-formed model fails — impossible
32/// for the crate's output models (no map keys, no IO); a violation is a bug
33/// worth surfacing loudly rather than an empty-but-successful render.
34pub fn render_success<T>(profile: Option<&str>, data: &T, compact: bool) -> String
35where
36 T: Serialize + ?Sized,
37{
38 let envelope = JsonEnvelope {
39 ok: true,
40 profile,
41 data,
42 };
43 serialize(&envelope, compact)
44}
45
46/// Render a failure envelope: pretty or compact. The caller routes the
47/// result to stderr in both modes.
48///
49/// # Panics
50/// See [`render_success`].
51pub fn render_failure(envelope: &ErrorEnvelope<'_>, compact: bool) -> String {
52 serialize(envelope, compact)
53}
54
55fn serialize<T>(value: &T, compact: bool) -> String
56where
57 T: Serialize + ?Sized,
58{
59 if compact {
60 serde_json::to_string(value)
61 } else {
62 serde_json::to_string_pretty(value)
63 }
64 .expect("envelope serialization cannot fail for well-formed models")
65}