Skip to main content

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}