1use crate::cli::Command;
3use crate::data::{InputMode, WebSharing};
4use clap::{ArgEnum, Args};
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7use std::str::FromStr;
8
9use std::net::IpAddr;
10
11#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ArgEnum)]
12pub enum OnForceClose {
13 #[serde(alias = "quit")]
14 Quit,
15 #[serde(alias = "detach")]
16 Detach,
17}
18
19impl Default for OnForceClose {
20 fn default() -> Self {
21 Self::Detach
22 }
23}
24
25impl FromStr for OnForceClose {
26 type Err = Box<dyn std::error::Error>;
27
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 match s {
30 "quit" => Ok(Self::Quit),
31 "detach" => Ok(Self::Detach),
32 e => Err(e.to_string().into()),
33 }
34 }
35}
36
37#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize, Args)]
38pub struct Options {
43 #[clap(long, value_parser)]
46 #[serde(default)]
47 pub simplified_ui: Option<bool>,
48 #[clap(long, value_parser)]
50 pub theme: Option<String>,
51 #[clap(long, arg_enum, hide_possible_values = true, value_parser)]
53 pub default_mode: Option<InputMode>,
54 #[clap(long, value_parser)]
56 pub default_shell: Option<PathBuf>,
57 #[clap(long, value_parser)]
59 pub default_cwd: Option<PathBuf>,
60 #[clap(long, value_parser)]
62 pub default_layout: Option<PathBuf>,
63 #[clap(long, value_parser)]
66 pub layout_dir: Option<PathBuf>,
67 #[clap(long, value_parser)]
70 pub theme_dir: Option<PathBuf>,
71 #[clap(long, value_parser)]
72 #[serde(default)]
73 pub mouse_mode: Option<bool>,
76 #[clap(long, value_parser)]
77 #[serde(default)]
78 pub pane_frames: Option<bool>,
80 #[clap(long, value_parser)]
81 #[serde(default)]
82 pub mirror_session: Option<bool>,
84 #[clap(long, arg_enum, hide_possible_values = true, value_parser)]
86 pub on_force_close: Option<OnForceClose>,
87 #[clap(long, value_parser)]
88 pub scroll_buffer_size: Option<usize>,
89
90 #[clap(long, value_parser)]
92 #[serde(default)]
93 pub copy_command: Option<String>,
94
95 #[clap(
97 long,
98 arg_enum,
99 ignore_case = true,
100 conflicts_with = "copy-command",
101 value_parser
102 )]
103 #[serde(default)]
104 pub copy_clipboard: Option<Clipboard>,
105
106 #[clap(long, value_parser)]
108 #[serde(default)]
109 pub copy_on_select: Option<bool>,
110
111 #[clap(long, value_parser)]
113 #[serde(default)]
114 pub osc8_hyperlinks: Option<bool>,
115
116 #[clap(long, value_parser)]
118 pub scrollback_editor: Option<PathBuf>,
119
120 #[clap(long, value_parser)]
122 #[serde(default)]
123 pub session_name: Option<String>,
124
125 #[clap(long, value_parser)]
127 #[serde(default)]
128 pub attach_to_session: Option<bool>,
129
130 #[clap(long, value_parser)]
132 #[serde(default)]
133 pub auto_layout: Option<bool>,
134
135 #[clap(long, value_parser)]
138 #[serde(default)]
139 pub session_serialization: Option<bool>,
140
141 #[clap(long, value_parser)]
143 #[serde(default)]
144 pub serialize_pane_viewport: Option<bool>,
145
146 #[clap(long, value_parser)]
150 #[serde(default)]
151 pub scrollback_lines_to_serialize: Option<usize>,
152
153 #[clap(long, value_parser)]
155 #[serde(default)]
156 pub styled_underlines: Option<bool>,
157
158 #[clap(long, value_parser)]
160 pub serialization_interval: Option<u64>,
161
162 #[clap(long, value_parser)]
164 pub disable_session_metadata: Option<bool>,
165
166 #[clap(long, value_parser)]
169 #[serde(default)]
170 pub support_kitty_keyboard_protocol: Option<bool>,
171
172 #[clap(long, value_parser)]
185 #[serde(default)]
186 pub web_server: Option<bool>,
187
188 #[clap(long, value_parser)]
201 #[serde(default)]
202 pub web_sharing: Option<WebSharing>,
203
204 #[clap(long, value_parser)]
207 #[serde(default)]
208 pub stacked_resize: Option<bool>,
209
210 #[clap(long, value_parser)]
213 #[serde(default)]
214 pub show_startup_tips: Option<bool>,
215
216 #[clap(long, value_parser)]
219 #[serde(default)]
220 pub show_release_notes: Option<bool>,
221
222 #[clap(long, value_parser)]
225 #[serde(default)]
226 pub advanced_mouse_actions: Option<bool>,
227
228 #[clap(long, value_parser)]
231 #[serde(default)]
232 pub mouse_hover_effects: Option<bool>,
233
234 #[clap(long, value_parser)]
237 #[serde(default)]
238 pub visual_bell: Option<bool>,
239
240 #[clap(long, value_parser)]
243 #[serde(default)]
244 pub focus_follows_mouse: Option<bool>,
245
246 #[clap(long, value_parser)]
249 #[serde(default)]
250 pub mouse_click_through: Option<bool>,
251
252 pub web_server_ip: Option<IpAddr>,
255 pub web_server_port: Option<u16>,
256 pub web_server_cert: Option<PathBuf>,
257 pub web_server_key: Option<PathBuf>,
258 pub enforce_https_for_localhost: Option<bool>,
259 #[clap(long, value_parser)]
262 pub post_command_discovery_hook: Option<String>,
263
264 #[clap(long)]
270 pub client_async_worker_tasks: Option<usize>,
271}
272
273#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
274pub enum Clipboard {
275 #[serde(alias = "system")]
276 System,
277 #[serde(alias = "primary")]
278 Primary,
279}
280
281impl Default for Clipboard {
282 fn default() -> Self {
283 Self::System
284 }
285}
286
287impl FromStr for Clipboard {
288 type Err = String;
289 fn from_str(s: &str) -> Result<Self, Self::Err> {
290 match s {
291 "System" | "system" => Ok(Self::System),
292 "Primary" | "primary" => Ok(Self::Primary),
293 _ => Err(format!("No such clipboard: {}", s)),
294 }
295 }
296}
297
298impl Options {
299 pub fn from_yaml(from_yaml: Option<Options>) -> Options {
300 if let Some(opts) = from_yaml {
301 opts
302 } else {
303 Options::default()
304 }
305 }
306 pub fn merge(&self, other: Options) -> Options {
310 let mouse_mode = other.mouse_mode.or(self.mouse_mode);
311 let pane_frames = other.pane_frames.or(self.pane_frames);
312 let auto_layout = other.auto_layout.or(self.auto_layout);
313 let mirror_session = other.mirror_session.or(self.mirror_session);
314 let simplified_ui = other.simplified_ui.or(self.simplified_ui);
315 let default_mode = other.default_mode.or(self.default_mode);
316 let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
317 let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
318 let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
319 let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
320 let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
321 let theme = other.theme.or_else(|| self.theme.clone());
322 let on_force_close = other.on_force_close.or(self.on_force_close);
323 let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
324 let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
325 let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
326 let copy_on_select = other.copy_on_select.or(self.copy_on_select);
327 let osc8_hyperlinks = other.osc8_hyperlinks.or(self.osc8_hyperlinks);
328 let scrollback_editor = other
329 .scrollback_editor
330 .or_else(|| self.scrollback_editor.clone());
331 let session_name = other.session_name.or_else(|| self.session_name.clone());
332 let attach_to_session = other
333 .attach_to_session
334 .or_else(|| self.attach_to_session.clone());
335 let session_serialization = other.session_serialization.or(self.session_serialization);
336 let serialize_pane_viewport = other
337 .serialize_pane_viewport
338 .or(self.serialize_pane_viewport);
339 let scrollback_lines_to_serialize = other
340 .scrollback_lines_to_serialize
341 .or(self.scrollback_lines_to_serialize);
342 let styled_underlines = other.styled_underlines.or(self.styled_underlines);
343 let serialization_interval = other.serialization_interval.or(self.serialization_interval);
344 let disable_session_metadata = other
345 .disable_session_metadata
346 .or(self.disable_session_metadata);
347 let support_kitty_keyboard_protocol = other
348 .support_kitty_keyboard_protocol
349 .or(self.support_kitty_keyboard_protocol);
350 let web_server = other.web_server.or(self.web_server);
351 let web_sharing = other.web_sharing.or(self.web_sharing);
352 let stacked_resize = other.stacked_resize.or(self.stacked_resize);
353 let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
354 let show_release_notes = other.show_release_notes.or(self.show_release_notes);
355 let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
356 let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects);
357 let visual_bell = other.visual_bell.or(self.visual_bell);
358 let focus_follows_mouse = other.focus_follows_mouse.or(self.focus_follows_mouse);
359 let mouse_click_through = other.mouse_click_through.or(self.mouse_click_through);
360 let web_server_ip = other.web_server_ip.or(self.web_server_ip);
361 let web_server_port = other.web_server_port.or(self.web_server_port);
362 let web_server_cert = other
363 .web_server_cert
364 .or_else(|| self.web_server_cert.clone());
365 let web_server_key = other.web_server_key.or_else(|| self.web_server_key.clone());
366 let enforce_https_for_localhost = other
367 .enforce_https_for_localhost
368 .or(self.enforce_https_for_localhost);
369 let post_command_discovery_hook = other
370 .post_command_discovery_hook
371 .or(self.post_command_discovery_hook.clone());
372 let client_async_worker_tasks = other
373 .client_async_worker_tasks
374 .or(self.client_async_worker_tasks);
375
376 Options {
377 simplified_ui,
378 theme,
379 default_mode,
380 default_shell,
381 default_cwd,
382 default_layout,
383 layout_dir,
384 theme_dir,
385 mouse_mode,
386 pane_frames,
387 mirror_session,
388 on_force_close,
389 scroll_buffer_size,
390 copy_command,
391 copy_clipboard,
392 copy_on_select,
393 osc8_hyperlinks,
394 scrollback_editor,
395 session_name,
396 attach_to_session,
397 auto_layout,
398 session_serialization,
399 serialize_pane_viewport,
400 scrollback_lines_to_serialize,
401 styled_underlines,
402 serialization_interval,
403 disable_session_metadata,
404 support_kitty_keyboard_protocol,
405 web_server,
406 web_sharing,
407 stacked_resize,
408 show_startup_tips,
409 show_release_notes,
410 advanced_mouse_actions,
411 mouse_hover_effects,
412 visual_bell,
413 focus_follows_mouse,
414 mouse_click_through,
415 web_server_ip,
416 web_server_port,
417 web_server_cert,
418 web_server_key,
419 enforce_https_for_localhost,
420 post_command_discovery_hook,
421 client_async_worker_tasks,
422 }
423 }
424
425 pub fn merge_from_cli(&self, other: Options) -> Options {
430 let merge_bool = |opt_other: Option<bool>, opt_self: Option<bool>| {
431 if opt_other.is_some() ^ opt_self.is_some() {
432 opt_other.or(opt_self)
433 } else if opt_other.is_some() && opt_self.is_some() {
434 Some(opt_other.unwrap() ^ opt_self.unwrap())
435 } else {
436 None
437 }
438 };
439
440 let simplified_ui = merge_bool(other.simplified_ui, self.simplified_ui);
441 let mouse_mode = merge_bool(other.mouse_mode, self.mouse_mode);
442 let pane_frames = merge_bool(other.pane_frames, self.pane_frames);
443 let auto_layout = merge_bool(other.auto_layout, self.auto_layout);
444 let mirror_session = merge_bool(other.mirror_session, self.mirror_session);
445 let session_serialization =
446 merge_bool(other.session_serialization, self.session_serialization);
447 let serialize_pane_viewport =
448 merge_bool(other.serialize_pane_viewport, self.serialize_pane_viewport);
449
450 let default_mode = other.default_mode.or(self.default_mode);
451 let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
452 let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
453 let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
454 let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
455 let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
456 let theme = other.theme.or_else(|| self.theme.clone());
457 let on_force_close = other.on_force_close.or(self.on_force_close);
458 let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
459 let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
460 let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
461 let copy_on_select = other.copy_on_select.or(self.copy_on_select);
462 let osc8_hyperlinks = other.osc8_hyperlinks.or(self.osc8_hyperlinks);
463 let scrollback_editor = other
464 .scrollback_editor
465 .or_else(|| self.scrollback_editor.clone());
466 let session_name = other.session_name.or_else(|| self.session_name.clone());
467 let attach_to_session = other
468 .attach_to_session
469 .or_else(|| self.attach_to_session.clone());
470 let scrollback_lines_to_serialize = other
471 .scrollback_lines_to_serialize
472 .or_else(|| self.scrollback_lines_to_serialize.clone());
473 let styled_underlines = other.styled_underlines.or(self.styled_underlines);
474 let serialization_interval = other.serialization_interval.or(self.serialization_interval);
475 let disable_session_metadata = other
476 .disable_session_metadata
477 .or(self.disable_session_metadata);
478 let support_kitty_keyboard_protocol = other
479 .support_kitty_keyboard_protocol
480 .or(self.support_kitty_keyboard_protocol);
481 let web_server = other.web_server.or(self.web_server);
482 let web_sharing = other.web_sharing.or(self.web_sharing);
483 let stacked_resize = other.stacked_resize.or(self.stacked_resize);
484 let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
485 let show_release_notes = other.show_release_notes.or(self.show_release_notes);
486 let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
487 let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects);
488 let visual_bell = other.visual_bell.or(self.visual_bell);
489 let focus_follows_mouse = merge_bool(other.focus_follows_mouse, self.focus_follows_mouse);
490 let mouse_click_through = merge_bool(other.mouse_click_through, self.mouse_click_through);
491 let web_server_ip = other.web_server_ip.or(self.web_server_ip);
492 let web_server_port = other.web_server_port.or(self.web_server_port);
493 let web_server_cert = other
494 .web_server_cert
495 .or_else(|| self.web_server_cert.clone());
496 let web_server_key = other.web_server_key.or_else(|| self.web_server_key.clone());
497 let enforce_https_for_localhost = other
498 .enforce_https_for_localhost
499 .or(self.enforce_https_for_localhost);
500 let post_command_discovery_hook = other
501 .post_command_discovery_hook
502 .or_else(|| self.post_command_discovery_hook.clone());
503 let client_async_worker_tasks = other
504 .client_async_worker_tasks
505 .or(self.client_async_worker_tasks);
506
507 Options {
508 simplified_ui,
509 theme,
510 default_mode,
511 default_shell,
512 default_cwd,
513 default_layout,
514 layout_dir,
515 theme_dir,
516 mouse_mode,
517 pane_frames,
518 mirror_session,
519 on_force_close,
520 scroll_buffer_size,
521 copy_command,
522 copy_clipboard,
523 copy_on_select,
524 osc8_hyperlinks,
525 scrollback_editor,
526 session_name,
527 attach_to_session,
528 auto_layout,
529 session_serialization,
530 serialize_pane_viewport,
531 scrollback_lines_to_serialize,
532 styled_underlines,
533 serialization_interval,
534 disable_session_metadata,
535 support_kitty_keyboard_protocol,
536 web_server,
537 web_sharing,
538 stacked_resize,
539 show_startup_tips,
540 show_release_notes,
541 advanced_mouse_actions,
542 mouse_hover_effects,
543 visual_bell,
544 focus_follows_mouse,
545 mouse_click_through,
546 web_server_ip,
547 web_server_port,
548 web_server_cert,
549 web_server_key,
550 enforce_https_for_localhost,
551 post_command_discovery_hook,
552 client_async_worker_tasks,
553 }
554 }
555
556 pub fn from_cli(&self, other: Option<Command>) -> Options {
557 if let Some(Command::Options(options)) = other {
558 Options::merge_from_cli(self, options.into())
559 } else {
560 self.to_owned()
561 }
562 }
563}