1#![doc = include_str!("../README.md")]
2use core::pin::Pin;
3use core::task::{Context, Poll};
4use russh::{Channel, ChannelId, ChannelMsg};
5use std::io;
6use tokio::io::{AsyncRead, AsyncWrite, ReadBuf, ReadHalf, SimplexStream, WriteHalf};
7use tokio::task::JoinHandle;
8
9mod child;
10pub mod client;
13pub use crate::child::{Child, ChildStderr, ChildStdin, ChildStdout, Output};
14use crate::child::ChildImp;
15
16#[derive(Debug, thiserror::Error)]
17#[error("{0}")]
18pub enum Error {
19 IoError(#[from] io::Error),
20 Russh(#[from] russh::Error),
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct ExitStatus {
25 pub(crate) inner: ExitStatusImp,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub(crate) enum ExitStatusImp {
30 Code(u32),
31 Processing,
32}
33
34impl ExitStatus {
35 pub fn success(&self) -> bool {
37 match &self.inner {
38 ExitStatusImp::Code(code) => *code == 0,
39 ExitStatusImp::Processing => false,
40 }
41 }
42
43 pub fn code(&self) -> Option<u32> {
46 match &self.inner {
47 ExitStatusImp::Code(code) => Some(*code),
48 ExitStatusImp::Processing => None,
49 }
50 }
51}
52
53#[derive(Debug)]
58pub struct Command<S>
59where
60 S: From<(ChannelId, ChannelMsg)> + Send + Sync + 'static,
61{
62 inner: Channel<S>,
63 command: Vec<u8>,
64}
65
66impl<S> Command<S>
67where
68 S: From<(ChannelId, ChannelMsg)> + Send + Sync + 'static,
69{
70 pub fn from_channel<A: Into<Vec<u8>>>(inner: Channel<S>, command: A) -> Self {
71 let command = command.into();
72 Self { inner, command }
73 }
74}
75
76
77impl<S> Command<S>
78where
79 S: From<(ChannelId, ChannelMsg)> + Send + Sync + 'static,
80{
81 pub async fn spawn(self) -> Result<Child, Error> {
82 self.inner.exec(true, self.command).await?;
83 use tokio::io::simplex;
84 let stdin_buf_size = 4096;
85 let stdout_buf_size = 4096;
86 let stderr_buf_size = 4096;
87
88 let (stdin_rx, stdin_tx) = simplex(stdin_buf_size);
89 let (stdout_rx, stdout_tx) = simplex(stdout_buf_size);
90 let (stderr_rx, stderr_tx) = simplex(stderr_buf_size);
91 let stdin = ChildStdin { inner: stdin_tx };
92 let stdout = ChildStdout { inner: stdout_rx };
93 let stderr = ChildStderr { inner: stderr_rx };
94
95 let imp = ChildImp {
96 channel: self.inner,
97 stdin_rx,
98 stdout_tx,
99 stderr_tx,
100 };
101
102 let handle = tokio::spawn(async move { imp.wait().await });
103
104 Ok(Child {
105 stdin: Some(stdin),
106 stdout: Some(stdout),
107 stderr: Some(stderr),
108 handle,
109 })
110 }
111
112 pub async fn output(self) -> Result<Output, Error> {
113 let child = self.spawn().await?;
114 Ok(child.wait_with_output().await?)
115 }
116}