tmux_interface/commands/tmux_command.rs
1use crate::{Tmux, TmuxCommands};
2use std::borrow::Cow;
3use std::fmt;
4use std::process::Command;
5
6// XXX: cmd, command, tmux command all proper names in methods, fields
7// XXX: mb enum for command?
8// XXX: rename (.cmd to .name)?
9// XXX: environment
10/// Standard tmux command line arguments syntax:
11///
12/// ```text
13/// tmux [-2CluvV] [-c shell-command] [-f file] [-L socket-name] [-S socket-path] [command [flags]]
14/// ```
15///
16#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
17pub struct TmuxCommand<'a> {
18 /// environment variables
19 pub envs: Option<Vec<(Cow<'a, str>, Cow<'a, str>)>>,
20
21 /// command name
22 pub name: Option<Cow<'a, str>>,
23
24 /// command alias
25 pub alias: Option<Cow<'a, str>>,
26
27 // XXX: remove
28 /// flags (`[-a] [-b] [-c]`)
29 pub flags: Option<Vec<Cow<'a, str>>>,
30
31 /// short flags (`[-a] [-b] [-c]`)
32 pub flags_short: Option<String>,
33
34 /// arguments: long flags, options, parameters (`[--longflag] [-o opt] [param]`)
35 pub args: Option<Vec<Cow<'a, str>>>,
36
37 /// subcommands list
38 pub subcommands: Option<TmuxCommands<'a>>,
39
40 // XXX: Cow<'a, str> or &'a str?
41 /// separator between command and it's flags, args, subcommand (" ")
42 pub separator: Option<&'a str>,
43
44 // XXX: Cow<'a, str> or &'a str?
45 /// flags, args separator (usually double dash `--`)
46 pub flags_args_separator: Option<&'a str>,
47
48 /// combine multiple single flags into flags line (`-f -a` = `-fa`)
49 pub combine_short_flags: bool,
50
51 /// use command alias instead of name (`new-session` = `new`)
52 pub use_alias: bool,
53}
54
55// XXX: reason?
56//macro_rules! tmux_command!("env", "cmd", "-a", "-b", "-arg 0", "param")
57
58pub const EMPTY_CMD: &str = "";
59pub const TMUX_COMMAND_ARG_SEPARATOR: &str = " ";
60
61impl<'a> Default for TmuxCommand<'a> {
62 fn default() -> Self {
63 Self {
64 envs: None,
65 name: None,
66 alias: None,
67 flags: None,
68 flags_short: None,
69 args: None,
70 subcommands: None,
71 separator: None,
72 flags_args_separator: None,
73 combine_short_flags: true,
74 use_alias: true,
75 }
76 }
77}
78
79// XXX: reason?
80// s. clap
81//macro_rules! tmux_command!("env", "cmd", "-a", "-b", "-arg 0", "param")
82
83impl<'a> fmt::Display for TmuxCommand<'a> {
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 let output = self
86 .to_vec()
87 .join(self.separator.unwrap_or(TMUX_COMMAND_ARG_SEPARATOR));
88 write!(f, "{}", output)
89 }
90}
91
92impl<'a> TmuxCommand<'a> {
93 /// Create new `Cmd` structure (using `default()` method)
94 pub fn new() -> Self {
95 Default::default()
96 }
97
98 /// Create new `Cmd` structure, initialize both `not_combine_short_flags` and `not_use_alias`
99 /// fields with `true`. Command will not combine flags (separate flags will be used instead),
100 /// and not use alias (command name will be used instead)
101 pub fn new_full<S: Into<Cow<'a, str>>>(name: S) -> Self {
102 Self {
103 name: Some(name.into()),
104 combine_short_flags: false,
105 use_alias: false,
106 ..Default::default()
107 }
108 }
109
110 /// Create and set `name` field
111 pub fn with_name<S: Into<Cow<'a, str>>>(name: S) -> Self {
112 Self {
113 name: Some(name.into()),
114 ..Default::default()
115 }
116 }
117
118 /// Create and set `alias` field
119 pub fn with_alias<S: Into<Cow<'a, str>>>(alias: S) -> Self {
120 Self {
121 alias: Some(alias.into()),
122 ..Default::default()
123 }
124 }
125
126 pub fn with_cmds(cmd_list: TmuxCommands<'a>) -> Self {
127 Self {
128 subcommands: Some(cmd_list),
129 ..Default::default()
130 }
131 }
132
133 /// Set `Cmd.name` field
134 pub fn name<S: Into<Cow<'a, str>>>(&mut self, cmd: S) -> &mut Self {
135 self.name = Some(cmd.into());
136 self
137 }
138
139 /// Set `Cmd.alias` field
140 pub fn alias<S: Into<Cow<'a, str>>>(&mut self, alias: S) -> &mut Self {
141 self.alias = Some(alias.into());
142 self
143 }
144
145 /// Add an environment variable to `Cmd.env`
146 pub fn env<T, U>(&mut self, key: T, value: U) -> &mut Self
147 where
148 T: Into<Cow<'a, str>>,
149 U: Into<Cow<'a, str>>,
150 {
151 self.envs
152 .get_or_insert(Vec::new())
153 .push((key.into(), value.into()));
154 self
155 }
156
157 // XXX: hard bound to cmd_args
158 // if vec doesn't exist, creates it and appends with given arguments
159 /// push a single flag (`-x`)
160 pub fn push_flag<S: Into<Cow<'a, str>>>(&mut self, flag: S) -> &mut Self {
161 self.args.get_or_insert(Vec::new()).push(flag.into());
162 self
163 }
164
165 pub fn push_flag_short(&mut self, flag: char) -> &mut Self {
166 self.flags_short.get_or_insert(String::new()).push(flag);
167 self
168 }
169
170 // if vec doesn't exist, creates it and appends with given arguments
171 /// push an option, flag and value (`-x <VALUE>`)
172 pub fn push_option<U, V>(&mut self, key: U, option: V) -> &mut Self
173 where
174 U: Into<Cow<'a, str>>,
175 V: Into<Cow<'a, str>>,
176 {
177 self.args
178 .get_or_insert(Vec::new())
179 .extend_from_slice(&[key.into(), option.into()]);
180 self
181 }
182
183 // if vec doesn't exist, creates it and appends with given arguments
184 /// push a single parameter (`<VALUE>`)
185 pub fn push_param<S: Into<Cow<'a, str>>>(&mut self, param: S) -> &mut Self {
186 self.args.get_or_insert(Vec::new()).push(param.into());
187 self
188 }
189
190 // XXX: rename subcmd?
191 pub fn push_cmd(&mut self, cmd: TmuxCommand<'a>) -> &mut Self {
192 self.subcommands
193 .get_or_insert(TmuxCommands::new())
194 .push(cmd);
195 self
196 }
197
198 // XXX: rename subcmd?
199 pub fn push_cmds(&mut self, cmd_list: TmuxCommands<'a>) -> &mut Self {
200 self.subcommands = Some(cmd_list);
201 self
202 }
203
204 pub fn arg<T, U>(&mut self, flag: T, opt: U) -> &mut Self
205 where
206 T: Into<Cow<'a, str>>,
207 U: Into<Cow<'a, str>>,
208 {
209 let v = self.args.get_or_insert(Vec::new());
210 v.push(flag.into());
211 v.push(opt.into());
212 self
213 }
214
215 // XXX: -> &mut Self, or Self
216 pub fn opt<T, U>(&mut self, short: T, opt: U) -> &mut Self
217 where
218 T: Into<Cow<'a, str>>,
219 U: Into<Cow<'a, str>>,
220 {
221 let v = self.args.get_or_insert(Vec::new());
222 v.push(short.into());
223 v.push(opt.into());
224 self
225 }
226
227 pub fn param<T: Into<Cow<'a, str>>>(&mut self, param: T) -> &mut Self {
228 self.args.get_or_insert(Vec::new()).push(param.into());
229 self
230 }
231
232 /// Set `Cmd.combine_short_flags` to `true`
233 pub fn combine_short_flags(&mut self) -> &mut Self {
234 self.combine_short_flags = true;
235 self
236 }
237
238 pub fn not_combine_short_flags(&mut self) -> &mut Self {
239 self.combine_short_flags = false;
240 self
241 }
242
243 pub fn combine_short_flags_ext(&mut self, state: bool) -> &mut Self {
244 self.combine_short_flags = state;
245 self
246 }
247
248 /// Set `Cmd.use_alias` to `true`
249 // XXX: Remove, replace?
250 pub fn use_alias(&mut self) -> &mut Self {
251 self.use_alias = true;
252 self
253 }
254
255 pub fn not_use_alias(&mut self) -> &mut Self {
256 self.use_alias = false;
257 self
258 }
259
260 pub fn use_alias_ext(&mut self, state: bool) -> &mut Self {
261 self.use_alias = state;
262 self
263 }
264
265 //pub fn arg(&mut self, arg: &'a str) -> &mut Self {
266 //self.args.get_or_insert(Vec::new()).push(arg);
267 //self
268 //}
269
270 // TODO: custom command
271 //pub fn custom<S: Into<Cow<'a, str>>>(&self, ) -> &mut Self {
272 //}
273
274 //// create `std::process::Command` from `Self` (consuming `Self`)
275 //pub fn to_command(&self) -> Command {
276 //Command::from(self)
277 //}
278
279 // NOTE: can't be consuming `to_vec(self)`, borrowing used in `fmt(&self)`
280 /// Transform `Cmd` to `Vec<Cow<'a, str>>`
281 pub fn to_vec(&self) -> Vec<Cow<'a, str>> {
282 let mut v: Vec<Cow<'a, str>> = Vec::new();
283
284 if let Some(envs) = &self.envs {
285 for (key, value) in envs {
286 v.push(Cow::Owned(format!("{}={}", key, value)));
287 }
288 }
289
290 // XXX: ugly
291 if self.use_alias {
292 if let Some(alias) = &self.alias {
293 v.push(alias.clone());
294 } else if let Some(name) = &self.name {
295 v.push(name.clone());
296 }
297 } else if let Some(name) = &self.name {
298 v.push(name.clone());
299 }
300
301 if let Some(flags_short) = &self.flags_short {
302 if self.combine_short_flags {
303 v.push(Cow::Owned(format!("-{}", flags_short)));
304 } else {
305 for c in flags_short.chars() {
306 v.push(Cow::Owned(format!("-{}", c)));
307 }
308 }
309 }
310
311 if let Some(args) = &self.args {
312 v.extend(args.to_vec());
313 }
314
315 if let Some(cmds) = &self.subcommands {
316 v.extend(cmds.to_vec());
317 }
318
319 v
320 }
321
322 /// Transform `Cmd` into [`std::process::Command`]
323 pub fn to_command(self) -> Command {
324 let name = self.name.as_ref().unwrap_or(&Cow::Borrowed(""));
325 let mut command = Command::new(name.as_ref());
326
327 if let Some(envs) = self.envs {
328 command.envs(
329 envs.iter()
330 .map(|(key, value)| (key.as_ref(), value.as_ref())),
331 );
332 }
333
334 if let Some(args) = self.args {
335 command.args(args.iter().map(|arg| arg.as_ref()));
336 }
337
338 // additional subcommands
339 if let Some(cmds) = self.subcommands {
340 command.args(cmds.to_vec().iter().map(|arg| arg.as_ref()));
341 }
342
343 //if let Some(stdin) = self.stdin {
344 //command.stdin(stdin);
345 //}
346
347 //if let Some(stdout) = self.stdout {
348 //command.stdout(stdout);
349 //}
350
351 //if let Some(stderr) = self.stderr {
352 //command.stderr(stderr);
353 //}
354
355 command
356 }
357
358 //pub fn into_tmux_command(self) -> TmuxCommand<'a> {
359 //TmuxCommand::default()
360 //}
361
362 //pub fn into_tmux_bin_command_ext(self, tmux: TmuxBin<'a>) -> TmuxBinCommand<'a> {
363 //TmuxBinCommand {
364 //tmux: tmux,
365 //command: self,
366 //}
367 //}
368
369 //pub fn append_to(self, cmds: &mut TmuxCommands<'a>) {
370 //cmds.push(self);
371 //}
372
373 //pub fn writeln(self, stdin: &mut ChildStdin) -> Result<(), std::io::Error> {
374 //writeln!(stdin, "{}", self.to_string())
375 //}
376 //pub fn into_vec(self) -> Vec<&'a str> {
377 //let mut v = Vec::new();
378
379 //v.push(self.name);
380
381 //for cmd in self.cmds.cmds {
382 //v.append(&mut cmd.into_vec());
383 //}
384
385 //for arg in self.args.args {
386 ////v.append(&mut args.into_vec());
387 //match arg {
388 //Arg::Flag(flag) => {
389 //if let Some(short) = flag.get_short() {
390 //v.push(short);
391 //}
392 //}
393 //Arg::Opt(opt) => {}
394 //Arg::Param(param) => {}
395 //_ => {}
396 //};
397 //}
398
399 //v
400 //}
401}
402
403// create ready to exec [`std::process::Command`]
404// * create [`std::process::Command`]
405// * push environment variables
406// * push binary arguments
407// * push subcommand
408impl<'a> From<&TmuxCommand<'a>> for Command {
409 fn from(cmd: &TmuxCommand) -> Self {
410 // user given command or blank command
411 let name = cmd.name.as_ref().unwrap_or(&Cow::Borrowed(EMPTY_CMD));
412 let mut command = Command::new(name.as_ref());
413
414 // environment variables
415 if let Some(envs) = &cmd.envs {
416 command.envs(
417 envs.iter()
418 .map(|(key, value)| (key.as_ref(), value.as_ref())),
419 );
420 }
421
422 // arguments
423 if let Some(args) = &cmd.args {
424 command.args(args.iter().map(|arg| arg.as_ref()));
425 }
426
427 // subcommands
428 if let Some(cmds) = &cmd.subcommands {
429 command.args(cmds.to_vec().iter().map(|arg| arg.as_ref()));
430 }
431
432 command
433 }
434}
435
436// create ready to exec [`std::process::Command`]
437// * create [`std::process::Command`]
438// * push environment variables
439// * push binary arguments
440// * push subcommand
441impl<'a> From<TmuxCommand<'a>> for Command {
442 fn from(cmd: TmuxCommand) -> Self {
443 Command::from(&cmd)
444 }
445}
446
447/* NOTE: from bin
448 /// Returns mutable reference to tmux executable name `Cow<'a, str>`
449 ///
450 /// # Examples
451 /// ```
452 /// use std::borrow::Cow;
453 /// use tmux_interface::commands::tmux_bin::TmuxBin;
454 ///
455 /// let mut tmux = TmuxBin::default();
456 /// let bin = tmux.bin();
457 /// assert_eq!(bin, &Cow::Borrowed("tmux"));
458 /// ```
459 pub fn bin(&self) -> &Cow<'a, str> {
460 &self.bin
461 }
462
463 /// Returns mutable reference to tmux executable name `Cow<'a, str>`
464 ///
465 /// # Examples
466 /// ```
467 /// use std::borrow::Cow;
468 /// use tmux_interface::commands::tmux_bin::TmuxBin;
469 ///
470 /// let mut tmux = TmuxBin::default();
471 /// *tmux.bin_mut() = Cow::Borrowed("/usr/bin/tmux");
472 /// assert_eq!(tmux.bin, Cow::Borrowed("/usr/bin/tmux"));
473 /// ```
474 /// or
475 /// ```
476 /// use std::borrow::Cow;
477 /// use tmux_interface::commands::tmux_bin::TmuxBin;
478 ///
479 /// let mut tmux = TmuxBin::default();
480 /// *tmux.bin_mut() = "/usr/bin/tmux".into();
481 /// assert_eq!(tmux.bin, Cow::Borrowed("/usr/bin/tmux"));
482 /// ```
483 pub fn bin_mut(&mut self) -> &mut Cow<'a, str> {
484 &mut self.bin
485 }
486
487*/
488
489impl<'a> TmuxCommand<'a> {
490 pub fn add_command(mut self, command: TmuxCommand<'a>) -> Self {
491 self.push_cmd(command);
492 self
493 }
494
495 // TODO: custom command
496 //pub fn custom<S: Into<Cow<'a, str>>>(&self, ) -> &mut Self {
497 //}
498
499 //// create `std::process::Command` from `Self` (consuming `Self`)
500 //pub fn to_command(&self) -> Command {
501 //Command::from(self)
502 //}
503
504 /// create [`Tmux`] from [`TmuxCommand`]
505 pub fn into_tmux(self) -> Tmux<'a> {
506 Tmux::new().command(self)
507 }
508
509 //pub fn into_tmux_command(self) -> TmuxCommand<'a> {
510 //TmuxCommand::default()
511 //}
512
513 //pub fn into_tmux_bin_command_ext(self, tmux: TmuxBin<'a>) -> TmuxBinCommand<'a> {
514 //TmuxBinCommand {
515 //tmux: tmux,
516 //command: self,
517 //}
518 //}
519
520 //pub fn append_to(self, cmds: &mut TmuxCommands<'a>) {
521 //cmds.push(self);
522 //}
523
524 //pub fn writeln(self, stdin: &mut ChildStdin) -> Result<(), std::io::Error> {
525 //writeln!(stdin, "{}", self.to_string())
526 //}
527}
528
529//pub trait BuildCommand<'a> {
530//fn build(self) -> TmuxCommand<'a>;
531//}
532
533// trait used for bin_args and cmd_args
534//pub trait Args<'a> {
535//fn push_param<S: AsRef<str>>(&mut self, param: S);
536//fn push_option<S, U>(&mut self, key: S, option: U)
537//where
538//S: AsRef<str>,
539//U: AsRef<str>;
540//fn push_flag<S: AsRef<str>>(&mut self, flag: S);
541//}
542
543// trait used for bin_args and cmd_args
544//impl<'a> Args<'a> for Option<Vec<Cow<'a, str>>> {
545//fn push_param<S: Into<Cow<'a, str>>>(&mut self, param: S) {
546//self.get_or_insert(Vec::new()).push(param.into());
547//}
548
549//fn push_option<S, U>(&mut self, key: S, option: U)
550//where
551//S: AsRef<Cow<'a, str>>,
552//U: Into<Cow<'a, str>>,
553//{
554//self.get_or_insert(Vec::new())
555//.extend_from_slice(&[key.into(), option.into()]);
556//}
557
558//fn push_flag<S: Into<Cow<'a, str>>>(&mut self, flag: S) {
559//self.push_param(flag.into());
560//}
561//}
562// /// Returns mutable reference to tmux executable name `Cow<'a, str>`
563/* NOTE: from bin
564 ///
565 /// # Examples
566 /// ```
567 /// use std::borrow::Cow;
568 /// use tmux_interface::commands::tmux_bin::TmuxBin;
569 ///
570 /// let mut tmux = TmuxBin::default();
571 /// let bin = tmux.bin();
572 /// assert_eq!(bin, &Cow::Borrowed("tmux"));
573 /// ```
574 pub fn bin(&self) -> &Cow<'a, str> {
575 &self.bin
576 }
577
578 /// Returns mutable reference to tmux executable name `Cow<'a, str>`
579 ///
580 /// # Examples
581 /// ```
582 /// use std::borrow::Cow;
583 /// use tmux_interface::commands::tmux_bin::TmuxBin;
584 ///
585 /// let mut tmux = TmuxBin::default();
586 /// *tmux.bin_mut() = Cow::Borrowed("/usr/bin/tmux");
587 /// assert_eq!(tmux.bin, Cow::Borrowed("/usr/bin/tmux"));
588 /// ```
589 /// or
590 /// ```
591 /// use std::borrow::Cow;
592 /// use tmux_interface::commands::tmux_bin::TmuxBin;
593 ///
594 /// let mut tmux = TmuxBin::default();
595 /// *tmux.bin_mut() = "/usr/bin/tmux".into();
596 /// assert_eq!(tmux.bin, Cow::Borrowed("/usr/bin/tmux"));
597 /// ```
598 pub fn bin_mut(&mut self) -> &mut Cow<'a, str> {
599 &mut self.bin
600 }
601
602*/