Skip to main content

objectiveai_sdk/cli/output/
mod.rs

1//! Structured JSON Lines output for `objectiveai-cli`.
2//!
3//! Every line `objectiveai-cli` writes to stdout is one [`Output`] JSON
4//! object. There are two top-level shapes, discriminated by `"type"`:
5//!
6//! - `error` — a failure or advisory ([`Error`]).
7//! - `notification` — a typed payload `T` chosen by the consumer (the
8//!   CLI defines its own notification enum and parameterizes
9//!   `Output<T>` over it).
10//!
11//! `T` is flattened into the same JSON object as the `"type"` tag via
12//! serde's internal tagging, so `T` should be a struct or an
13//! internally-tagged enum.
14
15mod error;
16mod handle;
17pub mod notification;
18
19pub use error::*;
20pub use handle::*;
21pub use notification::*;
22
23use schemars::JsonSchema;
24use serde::{Deserialize, Serialize};
25
26/// A single line of CLI output.
27#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
28#[serde(tag = "type", rename_all = "snake_case")]
29#[schemars(rename = "cli.output.Output.{T}")]
30pub enum Output<T> {
31    Error(Error),
32    /// Wraps `T` in [`Notification`] so its fields end up under a
33    /// nested `value` key — protects against `T` carrying its own
34    /// `"type"` field colliding with this enum's discriminator.
35    Notification(Notification<T>),
36    /// Stream-start marker. Wire: `{"type":"begin"}`.
37    Begin,
38    /// Stream-end marker. Wire: `{"type":"end"}`.
39    End,
40}
41
42impl<T: Serialize> Output<T> {
43    /// Emit this output via `handle`. Equivalent to
44    /// `handle.emit(self).await`; see [`Handle::emit`] for the routing
45    /// details.
46    pub async fn emit(&self, handle: &Handle) {
47        handle.emit(self).await
48    }
49}
50
51#[cfg(test)]
52mod tests;