-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce verbosity of getting a session with a logged stream #62
Comments
Hi @dsherret Thank you for opening it up.
Yes.....
You're talking only on behalf of logged session right? According to logged stream. // variant 1
session::log(original_session, output);
// variant 2
LogSession::stdout(original_session);
LogSession::stderr(original_session);;
LogSession::new(original_session, stream); struct LogSession<W, S = Session> {} Take care |
In this case, I'm returning the value from a function, so when I switch it from a regular stream to a logged stream I don't want to have a separate compilation or need to manually change the type. What I've done is created a trait for a session, then put that in a box. Here's what my code looks like at the moment: #[cfg(unix)]
type PtyProcess = expectrl::process::unix::UnixProcess;
#[cfg(windows)]
type PtyProcess = expectrl::process::windows::WinProcess;
#[cfg(unix)]
type PtyProcessStream = expectrl::process::unix::PtyStream;
#[cfg(windows)]
type PtyProcessStream = expectrl::process::windows::ProcessStream;
type PtyLoggedStream =
expectrl::stream::log::LoggedStream<PtyProcessStream, io::Stderr>;
type PtyLoggingSession = expectrl::Session<PtyProcess, PtyLoggedStream>;
trait ExpectrlSession: Read + Write {
fn send_line(&mut self, s: &str) -> std::io::Result<()>;
fn expect(&mut self, s: &str) -> Result<expectrl::Captures, expectrl::Error>;
fn try_read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
}
macro_rules! implement_expectrl_session {
($struct_name:ident) => {
impl ExpectrlSession for $struct_name {
fn send_line(&mut self, s: &str) -> std::io::Result<()> {
self.0.send_line(s)
}
fn expect(
&mut self,
s: &str,
) -> Result<expectrl::Captures, expectrl::Error> {
self.0.expect(s)
}
fn try_read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.0.try_read(buf)
}
}
impl Read for $struct_name {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Write for $struct_name {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
};
}
struct LoggingExpectrlSession(PtyLoggingSession);
implement_expectrl_session!(LoggingExpectrlSession);
struct DefaultExpectrlSession(expectrl::session::Session);
implement_expectrl_session!(DefaultExpectrlSession); Then now I can have a common return value of I almost wonder if it would be useful to have a trait implementation built into the library for a session. Then consumers could box any type that implements that trait. Anyway, this is not a priority for me because I have this workaround that works ok. I'm just raising a pain point I found that other people might run into in the future as well. |
So I was pondering about it too, but decided not to. As I remember it was tried. Plus as I remember I did not found a good name for a trait. Need to think about it again.
Just a note that |
use std::io::{self, Stderr};
use expectrl::{
session::{OsProcess, OsProcessStream},
stream::log::LogStream,
Captures, Session,
};
type LogSession = Session<OsProcess, LogStream<OsProcessStream, Stderr>>;
pub enum PtySession {
Default(Session),
Logged(LogSession),
}
impl PtySession {
pub fn send_line(&mut self, text: &str) -> io::Result<()> {
match self {
PtySession::Default(s) => s.send_line(text),
PtySession::Logged(s) => s.send_line(text),
}
}
pub fn expect(&mut self, text: &str) -> Result<Captures, expectrl::Error> {
match self {
PtySession::Default(s) => s.expect(text),
PtySession::Logged(s) => s.expect(text),
}
}
pub fn try_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
PtySession::Default(s) => s.try_read(buf),
PtySession::Logged(s) => s.try_read(buf),
}
}
} So I've exposed the aliases. |
Hi, So I am running into the same issue here, I want at runtime to either have logging enabled or not. The enum solution works up to a point, it breaks down when we want to pass a session to I really think this should be trait based. Are you willing to take a PR that fixes this? Thanks for the library, it's got a much better API than |
One easier way to do this (avoiding adding a trait) would be to formalize support for the enum version by adding it to this library, currently it looks like this: type LogSession = Session<UnixProcess, LogStream<PtyStream, Stdout>>;
pub enum PtySession {
Default(Session),
Logged(LogSession),
}
impl PtySession {
pub fn send<B: AsRef<[u8]>>(&mut self, buf: B) -> io::Result<()> {
match self {
PtySession::Default(s) => s.send(buf),
PtySession::Logged(s) => s.send(buf),
}
}
pub fn send_line(&mut self, text: &str) -> io::Result<()> {
match self {
PtySession::Default(s) => s.send_line(text),
PtySession::Logged(s) => s.send_line(text),
}
}
pub fn expect<N>(
&mut self,
needle: N,
) -> std::result::Result<Captures, expectrl::Error>
where
N: Needle,
{
match self {
PtySession::Default(s) => s.expect(needle),
PtySession::Logged(s) => s.expect(needle),
}
}
}
impl Write for PtySession {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
PtySession::Default(s) => s.write(buf),
PtySession::Logged(s) => s.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
PtySession::Default(s) => s.flush(),
PtySession::Logged(s) => s.flush(),
}
}
}
impl BufRead for PtySession {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
match self {
PtySession::Default(s) => s.fill_buf(),
PtySession::Logged(s) => s.fill_buf(),
}
}
fn consume(&mut self, amt: usize) {
match self {
PtySession::Default(s) => s.consume(amt),
PtySession::Logged(s) => s.consume(amt),
}
}
}
impl Read for PtySession {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
PtySession::Default(s) => s.read(buf),
PtySession::Logged(s) => s.read(buf),
}
}
} Then we could just update the signature for How does that sound? |
It would be breaking change so would necessitate a bump to |
Originally It was supposed to be a new 'Stream' rather then a new 'Session'. something like the following: let process = UnixProcess::spawn("bash").unwrap();
let stream = process.get_pty_stream().unwrap();
let stream = LogStream::new(stream, std::io::stdout());
let session = Session::new(process, stream).unwrap();
let mut repl = ReplSession::new(session, String::from("$"), None, false);
repl.send_line("echo 1231234").unwrap();
repl.send_line("echo 3242342").unwrap();
repl.send_line("ls").unwrap();
repl.read_line(&mut String::new()).unwrap(); Please share your thoughts |
Just testing out this crate and it works great so far!
What I've found though, is sometimes I want to see the output of what's happening and so I use a session with a logged stream. The types I had to write are extremely verbose though:
I noticed some of these are defined in the crate, but they're not public. Perhaps they should be or perhaps there should be some easier way of doing this? I'm using this crate for testing, so I'm creating my own wrapper API on top of this where I can just set an option to get logging on, but it's quite verbose so far.
The text was updated successfully, but these errors were encountered: