use std::fmt;
use std::time::Duration;
use tastty::Position;
pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_millis(50);
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WaitCondition {
pub(super) kind: WaitConditionKind<String>,
pub(super) poll: Duration,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) enum WaitConditionKind<P> {
Text(String),
Regex {
pattern: P,
include_scrollback: bool,
},
RowRegex {
row: u16,
pattern: P,
},
CellText {
position: Position,
text: String,
},
Cursor(Position),
Exit,
Stable {
settle: Duration,
ignore_cursor: bool,
ignore_style: bool,
},
AnyOf(Vec<WaitConditionKind<P>>),
}
impl WaitCondition {
#[must_use]
pub fn text(text: impl Into<String>) -> Self {
Self::with_kind(WaitConditionKind::Text(text.into()))
}
#[must_use]
pub fn regex(pattern: impl Into<String>) -> RegexCondition {
RegexCondition {
pattern: pattern.into(),
include_scrollback: false,
poll: DEFAULT_POLL_INTERVAL,
}
}
#[must_use]
pub fn row_regex(row: u16, pattern: impl Into<String>) -> Self {
Self::with_kind(WaitConditionKind::RowRegex {
row,
pattern: pattern.into(),
})
}
#[must_use]
pub fn cell_text(position: Position, text: impl Into<String>) -> Self {
Self::with_kind(WaitConditionKind::CellText {
position,
text: text.into(),
})
}
#[must_use]
pub fn cursor(position: Position) -> Self {
Self::with_kind(WaitConditionKind::Cursor(position))
}
#[must_use]
pub fn exit() -> Self {
Self::with_kind(WaitConditionKind::Exit)
}
#[must_use]
pub fn any_of<I, C>(conditions: I) -> Self
where
I: IntoIterator<Item = C>,
C: Into<WaitCondition>,
{
Self::with_kind(WaitConditionKind::AnyOf(
conditions
.into_iter()
.map(|condition| condition.into().kind)
.collect(),
))
}
#[must_use]
pub fn stable(settle: Duration) -> StableCondition {
StableCondition {
settle,
ignore_cursor: false,
ignore_style: false,
poll: DEFAULT_POLL_INTERVAL,
}
}
#[must_use]
pub fn poll(mut self, poll: Duration) -> Self {
self.poll = poll;
self
}
fn with_kind(kind: WaitConditionKind<String>) -> Self {
Self {
kind,
poll: DEFAULT_POLL_INTERVAL,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RegexCondition {
pattern: String,
include_scrollback: bool,
poll: Duration,
}
impl RegexCondition {
#[must_use]
pub fn poll(mut self, poll: Duration) -> Self {
self.poll = poll;
self
}
#[must_use]
pub fn include_scrollback(mut self) -> Self {
self.include_scrollback = true;
self
}
}
impl From<RegexCondition> for WaitCondition {
fn from(condition: RegexCondition) -> Self {
WaitCondition {
kind: WaitConditionKind::Regex {
pattern: condition.pattern,
include_scrollback: condition.include_scrollback,
},
poll: condition.poll,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StableCondition {
settle: Duration,
ignore_cursor: bool,
ignore_style: bool,
poll: Duration,
}
impl StableCondition {
#[must_use]
pub fn poll(mut self, poll: Duration) -> Self {
self.poll = poll;
self
}
#[must_use]
pub fn ignore_cursor(mut self) -> Self {
self.ignore_cursor = true;
self
}
#[must_use]
pub fn ignore_style(mut self) -> Self {
self.ignore_style = true;
self
}
}
impl From<StableCondition> for WaitCondition {
fn from(condition: StableCondition) -> Self {
WaitCondition {
kind: WaitConditionKind::Stable {
settle: condition.settle,
ignore_cursor: condition.ignore_cursor,
ignore_style: condition.ignore_style,
},
poll: condition.poll,
}
}
}
impl fmt::Display for WaitCondition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.kind, f)
}
}
impl fmt::Display for WaitConditionKind<String> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WaitConditionKind::Text(text) => write!(f, "text containing {text:?}"),
WaitConditionKind::Regex {
pattern,
include_scrollback,
} => {
write!(f, "regex /{pattern}/")?;
if *include_scrollback {
f.write_str(" (with scrollback)")?;
}
Ok(())
}
WaitConditionKind::RowRegex { row, pattern } => {
write!(f, "row {row} matching /{pattern}/")
}
WaitConditionKind::CellText { position, text } => {
write!(
f,
"cell ({}, {}) containing {text:?}",
position.row, position.col
)
}
WaitConditionKind::Cursor(position) => {
write!(f, "cursor at ({}, {})", position.row, position.col)
}
WaitConditionKind::Exit => f.write_str("process exit"),
WaitConditionKind::Stable {
settle,
ignore_cursor,
ignore_style,
} => {
write!(f, "stable for {}ms", settle.as_millis())?;
if *ignore_cursor || *ignore_style {
f.write_str(" (")?;
let mut first = true;
if *ignore_cursor {
f.write_str("ignore cursor")?;
first = false;
}
if *ignore_style {
if !first {
f.write_str(", ")?;
}
f.write_str("ignore style")?;
}
f.write_str(")")?;
}
Ok(())
}
WaitConditionKind::AnyOf(kinds) => {
f.write_str("any of [")?;
for (i, kind) in kinds.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
fmt::Display::fmt(kind, f)?;
}
f.write_str("]")
}
}
}
}