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;