rusty_bubbletea/options.rs
1//! Cleanroom Rust port of upstream Go source file: `options.go`
2//! Upstream Target Tag / Version: `v2.0.8`
3//!
4//! <public-docs>
5//! # Program Options
6//!
7//! Program options (`with_fps`, `without_renderer`, `with_filter`, `with_window_size`,
8//! `with_context`, `with_output`, `with_input`, `with_environment`,
9//! `without_signal_handler`, `without_catch_panics`, `without_signals`, `with_color_profile`).
10//! </public-docs>
11
12use std::io::{Read, Write};
13use std::sync::atomic::{AtomicBool, Ordering};
14use std::sync::Arc;
15
16use crate::model::{Model, Msg};
17use crate::profile::ColorProfile;
18
19/// A cancellation context mirroring `context.Context` for the program lifecycle.
20#[derive(Debug, Clone, Default)]
21pub struct Context {
22 cancelled: Arc<AtomicBool>,
23}
24
25impl Context {
26 /// Returns a new cancellable context.
27 pub fn new() -> Context {
28 Context {
29 cancelled: Arc::new(AtomicBool::new(false)),
30 }
31 }
32
33 /// Done returns whether the context has been cancelled.
34 pub fn done(&self) -> bool {
35 self.cancelled.load(Ordering::SeqCst)
36 }
37
38 /// Cancel cancels the context.
39 pub fn cancel(&self) {
40 self.cancelled.store(true, Ordering::SeqCst);
41 }
42}
43
44/// An event filter invoked before the program processes a message, mirroring
45/// the function type of `WithFilter`.
46pub type EventFilter<M> = Box<dyn Fn(&M, Box<dyn Msg>) -> Option<Box<dyn Msg>> + Send + Sync>;
47
48/// Program configuration options.
49pub struct ProgramOptions<M: Model> {
50 /// Target FPS framerate limit.
51 pub fps: u32,
52 /// Disable renderer (for daemon or headless usage).
53 pub disable_renderer: bool,
54 /// Disable OS signal handling.
55 pub disable_signals: bool,
56 /// Disable the signal handler that Bubble Tea sets up for programs.
57 pub disable_signal_handler: bool,
58 /// Disable the panic catching that Bubble Tea does by default.
59 pub disable_catch_panics: bool,
60 /// Initial terminal width override.
61 pub width: usize,
62 /// Initial terminal height override.
63 pub height: usize,
64 /// Optional event filter.
65 pub filter: Option<EventFilter<M>>,
66 /// Input reader override; None means stdin.
67 pub input: Option<Box<dyn Read + Send + Sync>>,
68 /// Output writer override; None means stdout.
69 pub output: Option<Box<dyn Write + Send + Sync>>,
70 /// Environment variables used by the program.
71 pub environ: Option<Vec<(String, String)>>,
72 /// Forced color profile.
73 pub color_profile: Option<ColorProfile>,
74 /// External cancellation context.
75 pub context: Option<Context>,
76}
77
78impl<M: Model> Default for ProgramOptions<M> {
79 fn default() -> Self {
80 Self {
81 fps: 60,
82 disable_renderer: false,
83 disable_signals: false,
84 disable_signal_handler: false,
85 disable_catch_panics: false,
86 width: 0,
87 height: 0,
88 filter: None,
89 input: None,
90 output: None,
91 environ: None,
92 color_profile: None,
93 context: None,
94 }
95 }
96}
97
98impl<M: Model> ProgramOptions<M> {
99 /// <upstream-comment>WithContext lets you specify a context in which to run the Program. This is
100 /// useful if you want to cancel the execution from outside. When a Program gets
101 /// cancelled it will exit with an error ErrProgramKilled.</upstream-comment>
102 pub fn with_context(mut self, ctx: Context) -> Self {
103 self.context = Some(ctx);
104 self
105 }
106
107 /// <upstream-comment>WithOutput sets the output which, by default, is stdout. In most cases you
108 /// won't need to use this.</upstream-comment>
109 pub fn with_output(mut self, output: Box<dyn Write + Send + Sync>) -> Self {
110 self.output = Some(output);
111 self
112 }
113
114 /// <upstream-comment>WithInput sets the input which, by default, is stdin. In most cases you
115 /// won't need to use this. To disable input entirely pass None.</upstream-comment>
116 pub fn with_input(mut self, input: Option<Box<dyn Read + Send + Sync>>) -> Self {
117 self.input = input;
118 self
119 }
120
121 /// <upstream-comment>WithEnvironment sets the environment variables that the program will use.
122 /// This is useful when the program is running in a remote session (e.g. SSH) and
123 /// you want to pass the environment variables from the remote session to the
124 /// program.</upstream-comment>
125 pub fn with_environment(mut self, env: Vec<(String, String)>) -> Self {
126 self.environ = Some(env);
127 self
128 }
129
130 /// <upstream-comment>WithoutSignalHandler disables the signal handler that Bubble Tea sets up for
131 /// Programs. This is useful if you want to handle signals yourself.</upstream-comment>
132 pub fn without_signal_handler(mut self) -> Self {
133 self.disable_signal_handler = true;
134 self
135 }
136
137 /// <upstream-comment>WithoutCatchPanics disables the panic catching that Bubble Tea does by
138 /// default. If panic catching is disabled the terminal will be in a fairly
139 /// unusable state after a panic because Bubble Tea will not perform its usual
140 /// cleanup on exit.</upstream-comment>
141 pub fn without_catch_panics(mut self) -> Self {
142 self.disable_catch_panics = true;
143 self
144 }
145
146 /// <upstream-comment>WithoutSignals will ignore OS signals.
147 /// This is mainly useful for testing.</upstream-comment>
148 pub fn without_signals(mut self) -> Self {
149 self.disable_signals = true;
150 self
151 }
152
153 /// <upstream-comment>WithoutRenderer disables the renderer. When this is set output and log
154 /// statements will be plainly sent to stdout (or another output if one is set)
155 /// without any rendering and redrawing logic.</upstream-comment>
156 pub fn without_renderer(mut self) -> Self {
157 self.disable_renderer = true;
158 self
159 }
160
161 /// <upstream-comment>WithFilter supplies an event filter that will be invoked before Bubble Tea
162 /// processes a tea.Msg. The event filter can return any tea.Msg which will then
163 /// get handled by Bubble Tea instead of the original event. If the event filter
164 /// returns None, the event will be ignored and Bubble Tea will not process it.</upstream-comment>
165 pub fn with_filter(mut self, filter: EventFilter<M>) -> Self {
166 self.filter = Some(filter);
167 self
168 }
169
170 /// <upstream-comment>WithFPS sets a custom maximum FPS at which the renderer should run. If
171 /// less than 1, the default value of 60 will be used. If over 120, the FPS
172 /// will be capped at 120.</upstream-comment>
173 pub fn with_fps(mut self, fps: u32) -> Self {
174 self.fps = fps;
175 self
176 }
177
178 /// <upstream-comment>WithColorProfile sets the color profile that the program will use.</upstream-comment>
179 pub fn with_color_profile(mut self, profile: ColorProfile) -> Self {
180 self.color_profile = Some(profile);
181 self
182 }
183
184 /// <upstream-comment>WithWindowSize sets the initial size of the terminal window. This is useful
185 /// when you need to set the initial size of the terminal window, for example
186 /// during testing or when you want to run your program in a non-interactive
187 /// environment.</upstream-comment>
188 pub fn with_window_size(mut self, width: usize, height: usize) -> Self {
189 self.width = width;
190 self.height = height;
191 self
192 }
193}