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*/