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, value_parser)]
55 pub theme_dark: Option<String>,
56 #[clap(long, value_parser)]
60 pub theme_light: Option<String>,
61 #[clap(long, arg_enum, hide_possible_values = true, value_parser)]
63 pub default_mode: Option<InputMode>,
64 #[clap(long, value_parser)]
66 pub default_shell: Option<PathBuf>,
67 #[clap(long, value_parser)]
69 pub default_cwd: Option<PathBuf>,
70 #[clap(long, value_parser)]
72 pub default_layout: Option<PathBuf>,
73 #[clap(long, value_parser)]
76 pub layout_dir: Option<PathBuf>,
77 #[clap(long, value_parser)]
80 pub theme_dir: Option<PathBuf>,
81 #[clap(long, value_parser)]
82 #[serde(default)]
83 pub mouse_mode: Option<bool>,
86 #[clap(long, value_parser)]
87 #[serde(default)]
88 pub pane_frames: Option<bool>,
90 #[clap(long, value_parser)]
91 #[serde(default)]
92 pub mirror_session: Option<bool>,
94 #[clap(long, arg_enum, hide_possible_values = true, value_parser)]
96 pub on_force_close: Option<OnForceClose>,
97 #[clap(long, value_parser)]
98 pub scroll_buffer_size: Option<usize>,
99
100 #[clap(long, value_parser)]
102 #[serde(default)]
103 pub copy_command: Option<String>,
104
105 #[clap(
107 long,
108 arg_enum,
109 ignore_case = true,
110 conflicts_with = "copy-command",
111 value_parser
112 )]
113 #[serde(default)]
114 pub copy_clipboard: Option<Clipboard>,
115
116 #[clap(long, value_parser)]
118 #[serde(default)]
119 pub copy_on_select: Option<bool>,
120
121 #[clap(long, value_parser)]
123 #[serde(default)]
124 pub osc8_hyperlinks: Option<bool>,
125
126 #[clap(long, value_parser)]
128 pub scrollback_editor: Option<PathBuf>,
129
130 #[clap(long, value_parser)]
132 #[serde(default)]
133 pub session_name: Option<String>,
134
135 #[clap(long, value_parser)]
137 #[serde(default)]
138 pub attach_to_session: Option<bool>,
139
140 #[clap(long, value_parser)]
142 #[serde(default)]
143 pub auto_layout: Option<bool>,
144
145 #[clap(long, value_parser)]
148 #[serde(default)]
149 pub session_serialization: Option<bool>,
150
151 #[clap(long, value_parser)]
153 #[serde(default)]
154 pub serialize_pane_viewport: Option<bool>,
155
156 #[clap(long, value_parser)]
160 #[serde(default)]
161 pub scrollback_lines_to_serialize: Option<usize>,
162
163 #[clap(long, value_parser)]
165 #[serde(default)]
166 pub styled_underlines: Option<bool>,
167
168 #[clap(long, value_parser)]
170 pub serialization_interval: Option<u64>,
171
172 #[clap(long, value_parser)]
174 pub disable_session_metadata: Option<bool>,
175
176 #[clap(long, value_parser)]
179 #[serde(default)]
180 pub support_kitty_keyboard_protocol: Option<bool>,
181
182 #[clap(long, value_parser)]
195 #[serde(default)]
196 pub web_server: Option<bool>,
197
198 #[clap(long, value_parser)]
211 #[serde(default)]
212 pub web_sharing: Option<WebSharing>,
213
214 #[clap(long, value_parser)]
217 #[serde(default)]
218 pub stacked_resize: Option<bool>,
219
220 #[clap(long, value_parser)]
223 #[serde(default)]
224 pub show_startup_tips: Option<bool>,
225
226 #[clap(long, value_parser)]
229 #[serde(default)]
230 pub show_release_notes: Option<bool>,
231
232 #[clap(long, value_parser)]
235 #[serde(default)]
236 pub advanced_mouse_actions: Option<bool>,
237
238 #[clap(long, value_parser)]
241 #[serde(default)]
242 pub mouse_hover_effects: Option<bool>,
243
244 #[clap(long, value_parser)]
247 #[serde(default)]
248 pub visual_bell: Option<bool>,
249
250 #[clap(long, value_parser)]
253 #[serde(default)]
254 pub focus_follows_mouse: Option<bool>,
255
256 #[clap(long, value_parser)]
259 #[serde(default)]
260 pub mouse_click_through: Option<bool>,
261
262 pub web_server_ip: Option<IpAddr>,
265 pub web_server_port: Option<u16>,
266 pub web_server_cert: Option<PathBuf>,
267 pub web_server_key: Option<PathBuf>,
268 pub enforce_https_for_localhost: Option<bool>,
269 #[clap(long, value_parser)]
272 pub post_command_discovery_hook: Option<String>,
273
274 #[clap(long)]
280 pub client_async_worker_tasks: Option<usize>,
281}
282
283#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
284pub enum Clipboard {
285 #[serde(alias = "system")]
286 System,
287 #[serde(alias = "primary")]
288 Primary,
289}
290
291impl Default for Clipboard {
292 fn default() -> Self {
293 Self::System
294 }
295}
296
297impl FromStr for Clipboard {
298 type Err = String;
299 fn from_str(s: &str) -> Result<Self, Self::Err> {
300 match s {
301 "System" | "system" => Ok(Self::System),
302 "Primary" | "primary" => Ok(Self::Primary),
303 _ => Err(format!("No such clipboard: {}", s)),
304 }
305 }
306}
307
308impl Options {
309 pub fn from_yaml(from_yaml: Option<Options>) -> Options {
310 if let Some(opts) = from_yaml {
311 opts
312 } else {
313 Options::default()
314 }
315 }
316 pub fn merge(&self, other: Options) -> Options {
320 let mouse_mode = other.mouse_mode.or(self.mouse_mode);
321 let pane_frames = other.pane_frames.or(self.pane_frames);
322 let auto_layout = other.auto_layout.or(self.auto_layout);
323 let mirror_session = other.mirror_session.or(self.mirror_session);
324 let simplified_ui = other.simplified_ui.or(self.simplified_ui);
325 let default_mode = other.default_mode.or(self.default_mode);
326 let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
327 let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
328 let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
329 let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
330 let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
331 let theme = other.theme.or_else(|| self.theme.clone());
332 let theme_dark = other.theme_dark.or_else(|| self.theme_dark.clone());
333 let theme_light = other.theme_light.or_else(|| self.theme_light.clone());
334 let on_force_close = other.on_force_close.or(self.on_force_close);
335 let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
336 let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
337 let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
338 let copy_on_select = other.copy_on_select.or(self.copy_on_select);
339 let osc8_hyperlinks = other.osc8_hyperlinks.or(self.osc8_hyperlinks);
340 let scrollback_editor = other
341 .scrollback_editor
342 .or_else(|| self.scrollback_editor.clone());
343 let session_name = other.session_name.or_else(|| self.session_name.clone());
344 let attach_to_session = other
345 .attach_to_session
346 .or_else(|| self.attach_to_session.clone());
347 let session_serialization = other.session_serialization.or(self.session_serialization);
348 let serialize_pane_viewport = other
349 .serialize_pane_viewport
350 .or(self.serialize_pane_viewport);
351 let scrollback_lines_to_serialize = other
352 .scrollback_lines_to_serialize
353 .or(self.scrollback_lines_to_serialize);
354 let styled_underlines = other.styled_underlines.or(self.styled_underlines);
355 let serialization_interval = other.serialization_interval.or(self.serialization_interval);
356 let disable_session_metadata = other
357 .disable_session_metadata
358 .or(self.disable_session_metadata);
359 let support_kitty_keyboard_protocol = other
360 .support_kitty_keyboard_protocol
361 .or(self.support_kitty_keyboard_protocol);
362 let web_server = other.web_server.or(self.web_server);
363 let web_sharing = other.web_sharing.or(self.web_sharing);
364 let stacked_resize = other.stacked_resize.or(self.stacked_resize);
365 let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
366 let show_release_notes = other.show_release_notes.or(self.show_release_notes);
367 let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
368 let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects);
369 let visual_bell = other.visual_bell.or(self.visual_bell);
370 let focus_follows_mouse = other.focus_follows_mouse.or(self.focus_follows_mouse);
371 let mouse_click_through = other.mouse_click_through.or(self.mouse_click_through);
372 let web_server_ip = other.web_server_ip.or(self.web_server_ip);
373 let web_server_port = other.web_server_port.or(self.web_server_port);
374 let web_server_cert = other
375 .web_server_cert
376 .or_else(|| self.web_server_cert.clone());
377 let web_server_key = other.web_server_key.or_else(|| self.web_server_key.clone());
378 let enforce_https_for_localhost = other
379 .enforce_https_for_localhost
380 .or(self.enforce_https_for_localhost);
381 let post_command_discovery_hook = other
382 .post_command_discovery_hook
383 .or(self.post_command_discovery_hook.clone());
384 let client_async_worker_tasks = other
385 .client_async_worker_tasks
386 .or(self.client_async_worker_tasks);
387
388 Options {
389 simplified_ui,
390 theme,
391 theme_dark,
392 theme_light,
393 default_mode,
394 default_shell,
395 default_cwd,
396 default_layout,
397 layout_dir,
398 theme_dir,
399 mouse_mode,
400 pane_frames,
401 mirror_session,
402 on_force_close,
403 scroll_buffer_size,
404 copy_command,
405 copy_clipboard,
406 copy_on_select,
407 osc8_hyperlinks,
408 scrollback_editor,
409 session_name,
410 attach_to_session,
411 auto_layout,
412 session_serialization,
413 serialize_pane_viewport,
414 scrollback_lines_to_serialize,
415 styled_underlines,
416 serialization_interval,
417 disable_session_metadata,
418 support_kitty_keyboard_protocol,
419 web_server,
420 web_sharing,
421 stacked_resize,
422 show_startup_tips,
423 show_release_notes,
424 advanced_mouse_actions,
425 mouse_hover_effects,
426 visual_bell,
427 focus_follows_mouse,
428 mouse_click_through,
429 web_server_ip,
430 web_server_port,
431 web_server_cert,
432 web_server_key,
433 enforce_https_for_localhost,
434 post_command_discovery_hook,
435 client_async_worker_tasks,
436 }
437 }
438
439 pub fn merge_from_cli(&self, other: Options) -> Options {
444 let merge_bool = |opt_other: Option<bool>, opt_self: Option<bool>| {
445 if opt_other.is_some() ^ opt_self.is_some() {
446 opt_other.or(opt_self)
447 } else if opt_other.is_some() && opt_self.is_some() {
448 Some(opt_other.unwrap() ^ opt_self.unwrap())
449 } else {
450 None
451 }
452 };
453
454 let simplified_ui = merge_bool(other.simplified_ui, self.simplified_ui);
455 let mouse_mode = merge_bool(other.mouse_mode, self.mouse_mode);
456 let pane_frames = merge_bool(other.pane_frames, self.pane_frames);
457 let auto_layout = merge_bool(other.auto_layout, self.auto_layout);
458 let mirror_session = merge_bool(other.mirror_session, self.mirror_session);
459 let session_serialization =
460 merge_bool(other.session_serialization, self.session_serialization);
461 let serialize_pane_viewport =
462 merge_bool(other.serialize_pane_viewport, self.serialize_pane_viewport);
463
464 let default_mode = other.default_mode.or(self.default_mode);
465 let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
466 let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
467 let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
468 let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
469 let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
470 let theme = other.theme.or_else(|| self.theme.clone());
471 let theme_dark = other.theme_dark.or_else(|| self.theme_dark.clone());
472 let theme_light = other.theme_light.or_else(|| self.theme_light.clone());
473 let on_force_close = other.on_force_close.or(self.on_force_close);
474 let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
475 let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
476 let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
477 let copy_on_select = other.copy_on_select.or(self.copy_on_select);
478 let osc8_hyperlinks = other.osc8_hyperlinks.or(self.osc8_hyperlinks);
479 let scrollback_editor = other
480 .scrollback_editor
481 .or_else(|| self.scrollback_editor.clone());
482 let session_name = other.session_name.or_else(|| self.session_name.clone());
483 let attach_to_session = other
484 .attach_to_session
485 .or_else(|| self.attach_to_session.clone());
486 let scrollback_lines_to_serialize = other
487 .scrollback_lines_to_serialize
488 .or_else(|| self.scrollback_lines_to_serialize.clone());
489 let styled_underlines = other.styled_underlines.or(self.styled_underlines);
490 let serialization_interval = other.serialization_interval.or(self.serialization_interval);
491 let disable_session_metadata = other
492 .disable_session_metadata
493 .or(self.disable_session_metadata);
494 let support_kitty_keyboard_protocol = other
495 .support_kitty_keyboard_protocol
496 .or(self.support_kitty_keyboard_protocol);
497 let web_server = other.web_server.or(self.web_server);
498 let web_sharing = other.web_sharing.or(self.web_sharing);
499 let stacked_resize = other.stacked_resize.or(self.stacked_resize);
500 let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
501 let show_release_notes = other.show_release_notes.or(self.show_release_notes);
502 let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
503 let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects);
504 let visual_bell = other.visual_bell.or(self.visual_bell);
505 let focus_follows_mouse = merge_bool(other.focus_follows_mouse, self.focus_follows_mouse);
506 let mouse_click_through = merge_bool(other.mouse_click_through, self.mouse_click_through);
507 let web_server_ip = other.web_server_ip.or(self.web_server_ip);
508 let web_server_port = other.web_server_port.or(self.web_server_port);
509 let web_server_cert = other
510 .web_server_cert
511 .or_else(|| self.web_server_cert.clone());
512 let web_server_key = other.web_server_key.or_else(|| self.web_server_key.clone());
513 let enforce_https_for_localhost = other
514 .enforce_https_for_localhost
515 .or(self.enforce_https_for_localhost);
516 let post_command_discovery_hook = other
517 .post_command_discovery_hook
518 .or_else(|| self.post_command_discovery_hook.clone());
519 let client_async_worker_tasks = other
520 .client_async_worker_tasks
521 .or(self.client_async_worker_tasks);
522
523 Options {
524 simplified_ui,
525 theme,
526 theme_dark,
527 theme_light,
528 default_mode,
529 default_shell,
530 default_cwd,
531 default_layout,
532 layout_dir,
533 theme_dir,
534 mouse_mode,
535 pane_frames,
536 mirror_session,
537 on_force_close,
538 scroll_buffer_size,
539 copy_command,
540 copy_clipboard,
541 copy_on_select,
542 osc8_hyperlinks,
543 scrollback_editor,
544 session_name,
545 attach_to_session,
546 auto_layout,
547 session_serialization,
548 serialize_pane_viewport,
549 scrollback_lines_to_serialize,
550 styled_underlines,
551 serialization_interval,
552 disable_session_metadata,
553 support_kitty_keyboard_protocol,
554 web_server,
555 web_sharing,
556 stacked_resize,
557 show_startup_tips,
558 show_release_notes,
559 advanced_mouse_actions,
560 mouse_hover_effects,
561 visual_bell,
562 focus_follows_mouse,
563 mouse_click_through,
564 web_server_ip,
565 web_server_port,
566 web_server_cert,
567 web_server_key,
568 enforce_https_for_localhost,
569 post_command_discovery_hook,
570 client_async_worker_tasks,
571 }
572 }
573
574 pub fn from_cli(&self, other: Option<Command>) -> Options {
575 if let Some(Command::Options(options)) = other {
576 Options::merge_from_cli(self, options.into())
577 } else {
578 self.to_owned()
579 }
580 }
581}