Skip to content

Commit

Permalink
Merge pull request #296 from jwodder/startup-script
Browse files Browse the repository at this point in the history
Add `--startup-script` option
  • Loading branch information
jwodder authored Nov 27, 2023
2 parents c82f77a + 5da157b commit 7021945
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 138 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
v0.3.0 (in development)
-----------------------
- Increased MSRV to 1.70
- Added `--startup-script` and `--startup-interval-ms` options

v0.2.0 (2023-06-03)
-------------------
Expand Down
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ exclude = ["/.*"]

[dependencies]
anyhow = "1.0.75"
async-stream = "0.3.5"
bytes = "1.5.0"
cfg-if = "1.0.0"
clap = { version = "4.4.8", default-features = false, features = ["derive", "error-context", "help", "std", "suggestions", "usage", "wrap_help"] }
crossterm = "0.27.0"
futures = "0.3.29"
futures = { version = "0.3.29", default-features = false }
itertools = "0.12.0"
native-tls = { version = "0.2.11", optional = true }
openssl = { version = "0.10.60", optional = true }
pin-project-lite = "0.2.13"
rustls-native-certs = { version = "0.6.3", optional = true }
rustyline-async = "0.4.0"
thiserror = "1.0.50"
time = { version = "0.3.30", default-features = false, features = ["std", "local-offset", "macros", "formatting"] }
tokio = { version = "1.34.0", features = ["macros", "net", "rt", "rt-multi-thread"] }
tokio = { version = "1.34.0", features = ["fs", "io-util", "macros", "net", "rt", "rt-multi-thread", "time"] }
tokio-native-tls = { version = "0.3.1", optional = true }
tokio-rustls = { version = "0.24.1", optional = true }
tokio-util = { version = "0.7.10", features = ["codec"] }
Expand All @@ -41,7 +44,7 @@ serde-jsonlines = "0.5.0"
serde_json = "1.0.108"
tempfile = "3.8.1"
time = { version = "0.3.30", default-features = false, features = ["serde", "parsing"] }
tokio = { version = "1.34.0", features = ["io-util", "sync", "time"] }
tokio = { version = "1.34.0", features = ["sync"] }
tokio-stream = { version = "0.1.14", features = ["time"] }

[build-dependencies]
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,18 @@ Options
- `--servername <DOMAIN>` — (with `--tls`) Use the given domain name for SNI
and certificate hostname validation; defaults to the remote host name

- `--startup-interval-ms <INT>` — Specify the time to wait in milliseconds
between sending lines of the startup script

- `-S <FILE>`, `--startup-script <FILE>` — On startup, send lines read from the
given file to the server before requesting user input

- `-t`, `--show-times` — Prepend a timestamp of the form `[HH:MM:SS]` to each
line printed to the terminal

- `--tls` — Connect using SSL/TLS

- `-T <file>`, `--transcript <file>` — Append a transcript of events to the
- `-T <FILE>`, `--transcript <FILE>` — Append a transcript of events to the
given file. See [Transcript Format](#transcript-format) below for more
information.

Expand Down
122 changes: 122 additions & 0 deletions THIRDPARTY.toml
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,128 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
END OF TERMS AND CONDITIONS
"""

[[third_party_libraries]]
package_name = "async-stream"
package_version = "0.3.5"
license = "MIT"

[[third_party_libraries.licenses]]
license = "MIT"
text = """
Copyright (c) 2019 Carl Lerche

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the \"Software\"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Copyright (c) 2018 David Tolnay

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the \"Software\"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

[[third_party_libraries]]
package_name = "async-stream-impl"
package_version = "0.3.5"
license = "MIT"

[[third_party_libraries.licenses]]
license = "MIT"
text = """
Copyright (c) 2019 Carl Lerche

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the \"Software\"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Copyright (c) 2018 David Tolnay

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the \"Software\"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

[[third_party_libraries]]
package_name = "backtrace"
package_version = "0.3.69"
Expand Down
4 changes: 4 additions & 0 deletions src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ impl ConfabCodec {
pub(crate) fn encoding(self, encoding: CharEncoding) -> ConfabCodec {
ConfabCodec { encoding, ..self }
}

pub(crate) fn get_encoding(&self) -> CharEncoding {
self.encoding
}
}

impl Decoder for ConfabCodec {
Expand Down
10 changes: 1 addition & 9 deletions src/events.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
use crate::util::{chomp, display_vis, JsonStrMap};
use crate::util::{chomp, display_vis, now, JsonStrMap, HMS_FMT};
use crossterm::style::{StyledContent, Stylize};
use std::fmt;
use std::net::SocketAddr;
use time::format_description::well_known::Rfc3339;
use time::format_description::FormatItem;
use time::macros::format_description;
use time::OffsetDateTime;

pub(crate) static HMS_FMT: &[FormatItem<'_>] = format_description!("[hour]:[minute]:[second]");

pub(crate) enum Event {
ConnectStart {
timestamp: OffsetDateTime,
Expand Down Expand Up @@ -184,7 +180,3 @@ impl fmt::Display for EventDisplay<'_> {
Ok(())
}
}

pub(crate) fn now() -> OffsetDateTime {
OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc())
}
79 changes: 79 additions & 0 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::util::InterfaceError;
use async_stream::stream;
use futures::stream::Stream;
use pin_project_lite::pin_project;
use rustyline_async::{Readline, ReadlineError, ReadlineEvent};
use std::future::Future;
use std::pin::Pin;
use std::task::{ready, Context, Poll};
use std::time::Duration;
use tokio::fs::File as TokioFile;
use tokio::io::{AsyncBufReadExt, BufReader, Lines};
use tokio::time::{sleep, Sleep};

#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum Input {
Line(String),
CtrlC,
}

pin_project! {
#[derive(Debug)]
pub(crate) struct StartupScript {
#[pin]
lines: Lines<BufReader<TokioFile>>,
#[pin]
nap: Option<Sleep>,
delay: Duration,
}
}

impl StartupScript {
pub(crate) fn new(reader: BufReader<TokioFile>, delay: Duration) -> StartupScript {
StartupScript {
lines: reader.lines(),
nap: None,
delay,
}
}
}

impl Stream for StartupScript {
type Item = Result<Input, InterfaceError>;

fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
if let Some(nap) = this.nap.as_mut().as_pin_mut() {
ready!(nap.poll(cx));
this.nap.set(None);
}
let r = match ready!(this.lines.as_mut().poll_next_line(cx)) {
Ok(Some(line)) => Some(Ok(Input::Line(line))),
Ok(None) => None,
Err(e) => Some(Err(InterfaceError::ReadScript(e))),
};
// TODO: Should the stream be forcibly fused when we're about to return
// None?
this.nap.set(Some(sleep(*this.delay)));
r.into()
}
}

#[allow(clippy::needless_pass_by_ref_mut)] // False positive
pub(crate) async fn readline_stream(
rl: &mut Readline,
) -> impl Stream<Item = Result<Input, InterfaceError>> + Send + '_ {
stream! {
loop {
match rl.readline().await {
Ok(ReadlineEvent::Line(line)) => {
rl.add_history_entry(line.clone());
yield Ok(Input::Line(line));
}
Ok(ReadlineEvent::Eof) | Err(ReadlineError::Closed) => break,
Ok(ReadlineEvent::Interrupted) => yield Ok(Input::CtrlC),
Err(ReadlineError::IO(e)) => yield Err(InterfaceError::ReadLine(e)),
}
}
}
}
Loading

0 comments on commit 7021945

Please sign in to comment.