@@ -52,16 +52,23 @@ macro_rules! delegate {
52
52
}
53
53
54
54
/// If a command is `OverSsh` then it can be executed over an SSH session.
55
+ ///
55
56
/// Primarily a way to allow `std::process::Command` to be turned directly into an `openssh::Command`.
56
57
pub 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.
58
59
///
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.
65
72
///
66
73
/// ```no_run
67
74
/// # #[tokio::main(flavor = "current_thread")]
@@ -75,7 +82,7 @@ pub trait OverSsh {
75
82
/// .arg("-l")
76
83
/// .arg("-a")
77
84
/// .arg("-h")
78
- /// .over_session (&session)
85
+ /// .over_ssh (&session)?
79
86
/// .output()
80
87
/// .await?;
81
88
///
@@ -84,61 +91,75 @@ pub trait OverSsh {
84
91
/// }
85
92
///
86
93
/// ```
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("[email protected] ", 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 > ;
88
115
}
89
116
90
117
impl 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
+
92
130
let program_escaped: Cow < ' _ , OsStr > = escape ( self . get_program ( ) ) ;
93
131
let mut command = session. raw_command ( program_escaped) ;
94
132
95
133
let args = self . get_args ( ) . map ( escape) ;
96
134
command. raw_args ( args) ;
97
- command
135
+ Ok ( command)
98
136
}
99
137
}
100
138
101
139
impl 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)
104
142
}
105
143
}
106
144
107
145
impl < S > OverSsh for & S
108
146
where
109
147
S : OverSsh ,
110
148
{
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)
122
151
}
123
152
}
124
153
125
- impl < S > OverSsh for std :: rc :: Rc < S >
154
+ impl < S > OverSsh for & mut S
126
155
where
127
156
S : OverSsh ,
128
157
{
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)
131
160
}
132
161
}
133
162
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
- }
142
163
143
164
/// A remote process builder, providing fine-grained control over how a new remote process should
144
165
/// be spawned.
0 commit comments