tern_sdk/lib.rs
1//! Talk to Tern over the Tern Surface Protocol (TSP).
2//!
3//! A program running in a terminal pane describes its UI as a tree of
4//! nodes; Tern lays it out, draws and animates it natively. Outside Tern the
5//! same program falls back to plain text. Six layers, each usable without
6//! the ones above it:
7//!
8//! 1. [`wire`]: constants, typed messages both ways, the encoder (framing,
9//! chunking, blobs) and the decoder.
10//! 2. [`input`] and [`keys`]: the streaming input parser that splits TSP out of
11//! the pty's input, and the key decoder.
12//! 3. [`ui`]: typed builders for every kind and prop, styled spans, and
13//! handlers mapping events to the program's messages.
14//! 4. [`reconcile`]: views diffed into frame ops with derived ids, exactly as
15//! Tern's plugin worker does it.
16//! 5. [`Session`]: the handshake, raw mode, surfaces, flow control, blobs,
17//! event routing, recording and a clean exit.
18//! 6. [`print`], [`ask`] and [`plain`]: one-call helpers and the plain-text
19//! fallback.
20//!
21//! Process-tty APIs (`term::Tty`, `Session::connect`, `print` and `ask`)
22//! require Unix or Windows. The wire, input, node and reconcile layers
23//! also build on `wasm32-unknown-unknown`.
24//!
25//! # Example
26//! ```no_run
27//! use tern_sdk::{PrintOptions, ui, ui::View};
28//!
29//! let view: View = View::new().main([ui::card().head("Deploy").status(ui::Status::Done).child(
30//! ui::kv()
31//! .item("service", "api-gateway")
32//! .item("region", "eu-west-1"),
33//! )]);
34//! // A native card in Tern; plain text anywhere else.
35//! tern_sdk::print(view, PrintOptions::new())?;
36//! # Ok::<(), tern_sdk::Error>(())
37//! ```
38
39#[cfg(any(unix, windows))]
40mod helpers;
41pub mod input;
42pub mod keys;
43mod plain;
44pub mod reconcile;
45mod record;
46mod session;
47pub mod term;
48pub mod ui;
49pub mod wire;
50
51#[cfg(any(unix, windows))]
52pub use helpers::{Answer, AskOptions, PrintOptions, Submission, ask, ask_with, print};
53pub use keys::Key;
54pub use plain::plain;
55pub use session::{Capabilities, Input, Options, Session, Surface, SurfaceOptions};
56pub use ui::{Node, View};
57pub use wire::{Event, Mode, Op};
58
59/// What can go wrong talking TSP.
60#[derive(Debug, thiserror::Error)]
61pub enum Error {
62 /// Terminal or file I/O failed.
63 #[error("i/o: {0}")]
64 Io(#[from] std::io::Error),
65 /// JSON could not be encoded or decoded.
66 #[error("json: {0}")]
67 Json(#[from] serde_json::Error),
68 /// Two siblings of a view share a key; nothing was sent.
69 #[error("{path}: duplicate child key {key:?}")]
70 DuplicateId {
71 /// The parent node's derived id.
72 path: String,
73 /// The duplicate key (explicit or positional).
74 key: String,
75 },
76 /// A view or node in wire form is malformed.
77 #[error("{0}")]
78 InvalidView(String),
79 /// A blob over 16 MiB.
80 #[error("a blob of {0} bytes is over the 16 MiB limit")]
81 BlobTooLarge(usize),
82 /// The surface is closed (by the program, or `gone`).
83 #[error("surface {0:?} is closed")]
84 SurfaceClosed(String),
85 /// No answer came in time.
86 #[error("timed out waiting for {0}")]
87 Timeout(&'static str),
88 /// The terminal closed its input.
89 #[error("the terminal closed its input")]
90 InputClosed,
91}