mod builder;
mod context;
mod writer;
use context::Context;
use writer::Writer;
use crate::actions::{action_for, Action, Direction, Overrider, Range, Scope};
use crate::completion::{Completer, Suggester};
use crate::Buffer;
pub use builder::{Builder, Prompt};
pub enum Outcome {
Accepted(String),
Canceled(Buffer),
}
impl Outcome {
#[must_use]
pub fn was_acceoted(&self) -> bool {
matches!(self, Outcome::Accepted(_))
}
#[must_use]
pub fn unwrap(self) -> String {
if let Outcome::Accepted(string) = self {
string
} else {
panic!("called `Outcome::unwrap()` on a `Canceled` value")
}
}
#[must_use]
pub fn some(self) -> Option<String> {
match self {
Outcome::Accepted(string) => Some(string),
Outcome::Canceled(_) => None,
}
}
pub fn ok(self) -> Result<String, Buffer> {
match self {
Outcome::Accepted(string) => Ok(string),
Outcome::Canceled(buffer) => Err(buffer),
}
}
}
pub fn read_line<O, C, S>(
prompt: Option<&str>,
buffer: Option<Buffer>,
erase_after_read: bool,
overrider: Option<&O>,
completer: Option<&C>,
suggester: Option<&S>,
) -> Result<Outcome, crate::Error>
where
O: Overrider + ?Sized,
C: Completer + ?Sized,
S: Suggester + ?Sized,
{
let mut context = Context::new(
erase_after_read,
prompt.as_deref(),
buffer,
completer,
suggester,
)?;
context.print()?;
loop {
if let crossterm::event::Event::Key(e) = crossterm::event::read()? {
match action_for(overrider, e, &context) {
Action::Write(c) => context.write(c)?,
Action::Delete(scope) => context.delete(scope)?,
Action::Move(range, direction) => context.move_cursor(range, direction)?,
Action::Complete(range) => context.complete(range)?,
Action::Suggest(direction) => context.suggest(direction)?,
Action::Noop => continue,
Action::Cancel => {
if context.is_suggesting() {
context.cancel_suggestion()?;
} else {
return Ok(Outcome::Canceled(context.into()));
}
}
Action::Accept => return Ok(Outcome::Accepted(context.buffer_as_string())),
}
}
}
}