1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
//! This module contains a system independent [Session] representation.
//!
//! But it does set a default [Session<P, S>] processes and stream in order to be able to use Session without generics.
//! It also sets a list of other methods which are available for a platform processes.
//!
//! # Example
//!
//! ```no_run,ignore
//! use std::{process::Command, io::prelude::*};
//! use expectrl::Session;
//!
//! let mut p = Session::spawn(Command::new("cat")).unwrap();
//! writeln!(p, "Hello World").unwrap();
//! let mut line = String::new();
//! p.read_line(&mut line).unwrap();
//! ```
#[cfg(feature = "async")]
mod async_session;
#[cfg(not(feature = "async"))]
mod sync_session;
use std::{io::Write, process::Command};
use crate::{interact::InteractSession, process::Process, stream::log::LogStream, Error};
#[cfg(not(feature = "async"))]
use std::io::Read;
#[cfg(feature = "async")]
use crate::process::IntoAsyncStream;
#[cfg(unix)]
type OsProc = crate::process::unix::UnixProcess;
#[cfg(windows)]
type OsProc = crate::process::windows::WinProcess;
#[cfg(all(unix, not(feature = "async")))]
type OsProcStream = crate::process::unix::PtyStream;
#[cfg(all(unix, feature = "async"))]
type OsProcStream = crate::process::unix::AsyncPtyStream;
#[cfg(all(windows, not(feature = "async")))]
type OsProcStream = crate::process::windows::ProcessStream;
#[cfg(all(windows, feature = "async"))]
type OsProcStream = crate::process::windows::AsyncProcessStream;
/// A type alias for OS process which can run a [`Session`] and a default one.
pub type OsProcess = OsProc;
/// A type alias for OS process stream which is a default one for [`Session`].
pub type OsProcessStream = OsProcStream;
#[cfg(feature = "async")]
pub use async_session::Session;
#[cfg(not(feature = "async"))]
pub use sync_session::Session;
impl Session {
/// Spawns a session on a platform process.
///
/// # Example
///
/// ```no_run
/// use std::process::Command;
/// use expectrl::Session;
///
/// let p = Session::spawn(Command::new("cat"));
/// ```
pub fn spawn(command: Command) -> Result<Self, Error> {
let mut process = OsProcess::spawn_command(command)?;
let stream = process.open_stream()?;
#[cfg(feature = "async")]
let stream = stream.into_async_stream()?;
let session = Self::new(process, stream)?;
Ok(session)
}
/// Spawns a session on a platform process.
/// Using a string commandline.
pub(crate) fn spawn_cmd(cmd: &str) -> Result<Self, Error> {
let mut process = OsProcess::spawn(cmd)?;
let stream = process.open_stream()?;
#[cfg(feature = "async")]
let stream = stream.into_async_stream()?;
let session = Self::new(process, stream)?;
Ok(session)
}
}
impl<P, S> Session<P, S> {
/// Interact gives control of the child process to the interactive user (the
/// human at the keyboard or a [`Read`]er implementator).
///
/// You can set different callbacks to the session, see [`InteractSession`].
///
/// Keystrokes are sent to the child process, and
/// the `stdout` and `stderr` output of the child process is printed.
///
/// When the user types the `escape_character` this method will return control to a running process.
/// The escape_character will not be transmitted.
/// The default for escape_character is entered as `Ctrl-]`, the very same as BSD telnet.
///
/// This simply echos the child `stdout` and `stderr` to the real `stdout` and
/// it echos the real `stdin` to the child `stdin`.
///
/// BEWARE that interact finishes after a process stops.
/// So after the return you may not obtain a correct status of a process.
///
/// In not `async` mode the default version uses a buzy loop.
///
/// - On `linux` you can use a `polling` version using the corresponding feature.
/// - On `windows` the feature is also present but it spawns a thread for pooling which creates a set of obsticales.
/// Specifically if you're planning to call `interact()` multiple times it may not be safe. Because the previous threads may still be running.
///
/// It works via polling in `async` mode on both `unix` and `windows`.
///
/// # Example
///
#[cfg_attr(
all(unix, not(feature = "async"), not(feature = "polling")),
doc = "```no_run"
)]
#[cfg_attr(
not(all(unix, not(feature = "async"), not(feature = "polling"))),
doc = "```ignore"
)]
/// use std::io::{stdout, Cursor};
/// use expectrl::{self, interact::InteractOptions};
///
/// let mut p = expectrl::spawn("cat").unwrap();
///
/// let input = Cursor::new(String::from("Some text right here"));
///
/// p.interact(input, stdout()).spawn(InteractOptions::default()).unwrap();
/// ```
///
/// [`Read`]: std::io::Read
pub fn interact<I, O>(&mut self, input: I, output: O) -> InteractSession<&mut Self, I, O> {
InteractSession::new(self, input, output)
}
}
/// Set a logger which will write each Read/Write operation into the writter.
///
/// # Example
///
/// ```
/// use expectrl::{spawn, session::log};
///
/// let p = spawn("cat").unwrap();
/// let p = log(p, std::io::stdout());
/// ```
#[cfg(not(feature = "async"))]
pub fn log<W, P, S>(session: Session<P, S>, dst: W) -> Result<Session<P, LogStream<S, W>>, Error>
where
W: Write,
S: Read,
{
session.swap_stream(|s| LogStream::new(s, dst))
}
/// Set a logger which will write each Read/Write operation into the writter.
///
/// # Example
///
/// ```
/// use expectrl::{spawn, session::log};
///
/// let p = spawn("cat").unwrap();
/// let p = log(p, std::io::stdout());
/// ```
#[cfg(feature = "async")]
pub fn log<W, P, S>(session: Session<P, S>, dst: W) -> Result<Session<P, LogStream<S, W>>, Error>
where
W: Write,
{
session.swap_stream(|s| LogStream::new(s, dst))
}