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 term::Waker;
57pub use ui::{Node, View};
58pub use wire::{Event, Font, Mode, Op};
59
60/// What can go wrong talking TSP.
61#[derive(Debug, thiserror::Error)]
62pub enum Error {
63 /// Terminal or file I/O failed.
64 #[error("i/o: {0}")]
65 Io(#[from] std::io::Error),
66 /// JSON could not be encoded or decoded.
67 #[error("json: {0}")]
68 Json(#[from] serde_json::Error),
69 /// Two siblings of a view share a key; nothing was sent.
70 #[error("{path}: duplicate child key {key:?}")]
71 DuplicateId {
72 /// The parent node's derived id.
73 path: String,
74 /// The duplicate key (explicit or positional).
75 key: String,
76 },
77 /// A view or node in wire form is malformed.
78 #[error("{0}")]
79 InvalidView(String),
80 /// A blob over 16 MiB.
81 #[error("a blob of {0} bytes is over the 16 MiB limit")]
82 BlobTooLarge(usize),
83 /// The surface is closed (by the program, or `gone`).
84 #[error("surface {0:?} is closed")]
85 SurfaceClosed(String),
86 /// No answer came in time.
87 #[error("timed out waiting for {0}")]
88 Timeout(&'static str),
89 /// The terminal closed its input.
90 #[error("the terminal closed its input")]
91 InputClosed,
92}