osc7501-store
Keep the current status of programs and tasks for a terminal panel or tab indicator. This crate implements record storage for OSC 7501, revision 0.4.
Works without std. Enable alloc to let the store own its storage, or provide
storage yourself to work without an allocator. The default feature set is empty.
Keep a report
[]
= { = "0.1", = ["alloc"] }
The crate exposes its parser, so you do not need a separate parser dependency:
use ;
#
#
#
Store::new(capacity) allocates storage once. Store operations do not allocate
or grow that storage. The store frees it when dropped.
If you already use the producer, enable its store-alloc feature and import
osc7501::store instead. Its store feature exposes caller-provided storage
without enabling alloc.
Provide storage without an allocator
Use the default features and supply slots with Store::from_slots:
use ;
#
This constructor is also available with alloc enabled. It starts empty,
even with reused slots. Choose storage that fits your memory budget,
especially on small stacks.
See the storage measurements
for sizes and allocation checks.
How records change
Each report replaces every explicit field. Omitted fields disappear. The store copies and decodes all fields before changing records, so it can retain reports after the parser's input is gone. Record views borrow the store.
- Clearing a task removes it and its slash-delimited descendants. Clearing root removes everything.
effective_appfinds the nearest app on the record or its ancestors, even when intermediate parents are absent.- At capacity, a new record evicts the least recently updated record. Reads do not change that order; updating an existing record does not evict. Every set refreshes update order, even when its fields are unchanged.
applyand the plain lifecycle methods update the store without collecting notifications. Use their_with_changesvariants to observe changes.
Equal current records do not imply equal future behavior: update order also determines eviction. Preserve report order when replaying input. Removing unchanged updates or restoring records in an arbitrary order can change which record is evicted next.
Both constructors accept capacities from 1 to 256 records, including root.
A conforming terminal host needs at least MIN_HOST_CAPACITY (64); smaller
stores are useful for tests.
Observe record changes
Use apply_with_changes to keep a UI or a history of visible record states in
sync without copying and comparing the whole store. These notifications describe
changes to current records and resolved apps:
- An unchanged report emits no notification, but still refreshes eviction order.
- A child can receive a set notification because its ancestor's app changed, even when the child sent no report.
Keep the incoming reports and lifecycle events when you need every update or must reproduce the store's eviction order. Change notifications alone cannot restore that history.
The callback receives:
Change::Remove(record)before a record is removed.Change::Set { record, effective_app }for each new record, changed explicit field, or changed inherited app. Each surviving record is delivered at most once, after the complete operation.
All removals precede sets. Order within either group is unspecified. Effective apps reflect the final store, including ancestor insertion, replacement, and eviction. An unchanged app value produces no descendant notification, even if the ancestor supplying it changes.
This example maintains a view of states and resolved apps. Copy other fields in the callback if your application needs them:
use BTreeMap;
use ;
#
The child inherits cargo when the root arrives. Prompt cleanup removes the
working root and updates the surviving child to have no effective app.
Use the same callback for every operation that affects an observed view,
including on_prompt_with_changes, on_process_exit_with_changes, and
on_full_reset_with_changes. Calling a plain method changes the store without
updating that view. If observation starts on a populated store, first initialize
the view from iter() and effective_app().
The runnable observer example routes reports and lifecycle events through one handler. It copies selected fields into an application-owned view, which remains usable after the store is dropped:
Use apply(report), on_prompt(), on_process_exit(), and on_full_reset()
when you do not need notifications; they avoid change detection.
Callbacks borrow records only for the call. Store operations remain allocation-free; the map above allocates because it is application-owned. Change detection uses bounded scratch storage for effective apps, and compares decoded field values.
Handle terminal events
Your shell integration and escape-sequence handler supply these events. The store does not infer them from reports or record ids.
| Event | What to do | Records kept |
|---|---|---|
| New shell prompt | Call on_prompt |
Idle, done, error |
| Attached process exits | Call on_process_exit |
Idle, done, error |
| Full reset (RIS) | Call on_full_reset |
None |
| Soft reset (DECSTR) or screen switch | No action | All |
One store represents one terminal. You own history, display, notifications, and transport. Treat decoded text as plain, untrusted text.
Run a terminal host example
From a repository checkout, run the host playground:
It simulates a release, sends reports through a fragmented stream, and shows the records the host keeps. Try this:
- Press
sto start. Select a task with the arrow keys orTab;ffails it andrretries. - Press
ito switch between decoded fields and the store API. - Finish the release, then press
Xto simulate process exit. Completed and failed results remain visible. PressRto clear everything. - Press
qto quit. The on-screen help lists the other controls.
Use a terminal of at least 80 columns and 24 rows. Protocol support is optional.
Unset NO_COLOR for colors. Piped output is plain; add -- --plain to request
a text trace in a terminal.
See the API documentation for the full interface.