@@ -52,16 +52,23 @@ macro_rules! delegate {
5252}
5353
5454/// If a command is `OverSsh` then it can be executed over an SSH session.
55+ ///
5556/// Primarily a way to allow `std::process::Command` to be turned directly into an `openssh::Command`.
5657pub trait OverSsh {
57- /// Given a session, return a command that can be executed over that session.
58+ /// Given an ssh session, return a command that can be executed over that ssh session.
5859 ///
59- /// **Note**: The command to be executed on the remote machine does not include
60- /// any environment variables or the current directory. They must be
61- /// set explicitly, if desired.
62- ///
63- /// # Example
64- /// Consider the implementation of `OverSsh` for `std::process::Command`:
60+ /// ### Notes
61+ ///
62+ /// The command to be executed on the remote machine should not explicitly
63+ /// set environment variables or the current working directory. It errors if the source command
64+ /// has environment variables or a current working directory set, since `openssh` doesn't (yet) have
65+ /// a method to set environment variables and `ssh` doesn't support setting a current working directory
66+ /// outside of `bash/dash/zsh` (which is not always available).
67+ ///
68+ /// ### Examples
69+ ///
70+ /// 1. Consider the implementation of `OverSsh` for `std::process::Command`. Let's build a
71+ /// `ls -l -a -h` command and execute it over an SSH session.
6572 ///
6673 /// ```no_run
6774 /// # #[tokio::main(flavor = "current_thread")]
@@ -75,7 +82,7 @@ pub trait OverSsh {
7582 /// .arg("-l")
7683 /// .arg("-a")
7784 /// .arg("-h")
78- /// .over_session (&session)
85+ /// .over_ssh (&session)?
7986 /// .output()
8087 /// .await?;
8188 ///
@@ -84,61 +91,75 @@ pub trait OverSsh {
8491 /// }
8592 ///
8693 /// ```
87- fn over_session < ' session > ( & self , session : & ' session Session ) -> crate :: Command < ' session > ;
94+ /// 2. Building a command with environment variables or a current working directory set will
95+ /// results in an error.
96+ ///
97+ /// ```no_run
98+ /// # #[tokio::main(flavor = "current_thread")]
99+ /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
100+ /// use std::process::Command;
101+ /// use openssh::{Session, KnownHosts, OverSsh};
102+ ///
103+ /// let session = Session::connect_mux("me@ssh.example.com", KnownHosts::Strict).await?;
104+ /// let echo =
105+ /// Command::new("echo")
106+ /// .arg("$MY_ENV_VAR")
107+ /// .over_ssh(&session);
108+ /// assert_matches!(echo, Err(openssh::Error::CommandHasEnv));
109+ ///
110+ /// # Ok(())
111+ /// }
112+ ///
113+ /// ```
114+ fn over_ssh < ' session > ( & self , session : & ' session Session ) -> Result < crate :: Command < ' session > , crate :: Error > ;
88115}
89116
90117impl OverSsh for std:: process:: Command {
91- fn over_session < ' session > ( & self , session : & ' session Session ) -> Command < ' session > {
118+ fn over_ssh < ' session > ( & self , session : & ' session Session ) -> Result < Command < ' session > , crate :: Error > {
119+
120+ // I'd really like `!self.get_envs().is_empty()` here, but that's
121+ // behind a `exact_size_is_empty` feature flag.
122+ if self . get_envs ( ) . len ( ) > 0 {
123+ return Err ( crate :: Error :: CommandHasEnv ) ;
124+ }
125+
126+ if self . get_current_dir ( ) . is_some ( ) {
127+ return Err ( crate :: Error :: CommandHasCwd ) ;
128+ }
129+
92130 let program_escaped: Cow < ' _ , OsStr > = escape ( self . get_program ( ) ) ;
93131 let mut command = session. raw_command ( program_escaped) ;
94132
95133 let args = self . get_args ( ) . map ( escape) ;
96134 command. raw_args ( args) ;
97- command
135+ Ok ( command)
98136 }
99137}
100138
101139impl OverSsh for tokio:: process:: Command {
102- fn over_session < ' session > ( & self , session : & ' session Session ) -> Command < ' session > {
103- self . as_std ( ) . over_session ( session)
140+ fn over_ssh < ' session > ( & self , session : & ' session Session ) -> Result < Command < ' session > , crate :: Error > {
141+ self . as_std ( ) . over_ssh ( session)
104142 }
105143}
106144
107145impl < S > OverSsh for & S
108146where
109147 S : OverSsh ,
110148{
111- fn over_session < ' session > ( & self , session : & ' session Session ) -> Command < ' session > {
112- <S as OverSsh >:: over_session ( self , session)
113- }
114- }
115-
116- impl < S > OverSsh for Box < S >
117- where
118- S : OverSsh ,
119- {
120- fn over_session < ' session > ( & self , session : & ' session Session ) -> Command < ' session > {
121- <S as OverSsh >:: over_session ( self , session)
149+ fn over_ssh < ' session > ( & self , session : & ' session Session ) -> Result < Command < ' session > , crate :: Error > {
150+ <S as OverSsh >:: over_ssh ( self , session)
122151 }
123152}
124153
125- impl < S > OverSsh for std :: rc :: Rc < S >
154+ impl < S > OverSsh for & mut S
126155where
127156 S : OverSsh ,
128157{
129- fn over_session < ' session > ( & self , session : & ' session Session ) -> Command < ' session > {
130- <S as OverSsh >:: over_session ( self , session)
158+ fn over_ssh < ' session > ( & self , session : & ' session Session ) -> Result < Command < ' session > , crate :: Error > {
159+ <S as OverSsh >:: over_ssh ( self , session)
131160 }
132161}
133162
134- impl < S > OverSsh for std:: sync:: Arc < S >
135- where
136- S : OverSsh ,
137- {
138- fn over_session < ' session > ( & self , session : & ' session Session ) -> Command < ' session > {
139- <S as OverSsh >:: over_session ( self , session)
140- }
141- }
142163
143164/// A remote process builder, providing fine-grained control over how a new remote process should
144165/// be spawned.
0 commit comments