mod input;
mod progress;
use input::{DefaultPrompt, PasswordPrompt, SelectionPrompt, TextPrompt};
use progress::{ProgressBarTrait, SpinnerTrait};
use std::sync::OnceLock;
pub const DOWNLOAD_MESSAGE: &str = "Downloading changes";
pub const APPLY_MESSAGE: &str = "Applying changes";
pub const UPLOAD_MESSAGE: &str = "Uploading changes";
pub const COMPLETE_MESSAGE: &str = "Completing changes";
pub const OUTPUT_MESSAGE: &str = "Outputting repository";
static INTERACTIVE_CONTEXT: OnceLock<InteractiveContext> = OnceLock::new();
pub fn get_context() -> Result<InteractiveContext, InteractionError> {
if let Some(context) = INTERACTIVE_CONTEXT.get() {
Ok(*context)
} else {
Err(InteractionError::NoContext)
}
}
pub fn set_context(value: InteractiveContext) {
INTERACTIVE_CONTEXT
.set(value)
.expect("Interactive context is already set!");
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum PromptType {
Confirm,
Input,
Select,
Password,
}
impl core::fmt::Display for PromptType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let name = match *self {
Self::Confirm => "confirm",
Self::Input => "input",
Self::Select => "fuzzy selection",
Self::Password => "password",
};
write!(f, "{name}")
}
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum InteractionError {
#[error("mode of interactivity not set")]
NoContext,
#[error("unable to provide interactivity in this context, and no valid default value for {0} prompt `{1}`")]
NotInteractive(PromptType, String),
#[error("I/O error while interacting with terminal")]
IO(#[from] std::io::Error),
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum InteractiveContext {
Terminal,
NotInteractive,
}
pub struct Confirm(Box<dyn DefaultPrompt<bool>>);
pub struct Select(Box<dyn SelectionPrompt<usize>>);
pub struct Input(Box<dyn TextPrompt<String>>);
pub struct Password(Box<dyn PasswordPrompt<String>>);
pub struct ProgressBar(Box<dyn ProgressBarTrait>);
pub struct Spinner(Box<dyn SpinnerTrait>);