use promptuity::prompts::{Input, Select, SelectOption}; use promptuity::style::*; use promptuity::{Error, PromptBody, PromptInput, PromptState, Promptuity, Term, Terminal, Theme}; const S_STEP: Symbol = Symbol("ℹ️", "i"); struct SimpleTheme { prev_lines: u16, } impl SimpleTheme { fn new() -> Self { Self { prev_lines: 0 } } fn fmt_message(&self, message: String) -> String { format!("{} {}:", S_STEP, message) } fn fmt_placeholder(&self, placeholder: Option) -> String { Styled::new(placeholder.unwrap_or_default()) .fg(Color::DarkGrey) .to_string() } fn fmt_hint(&self, hint: Option) -> String { Styled::new(hint.unwrap_or_default()) .fg(Color::DarkGrey) .to_string() } fn fmt_input_active(&self, input: PromptInput, placeholder: Option) -> String { match input { PromptInput::Raw(raw) => { format!( " {}", if raw.is_empty() { self.fmt_placeholder(placeholder) } else { raw } ) } PromptInput::Cursor(cursor) => { format!( " {}", if cursor.is_empty() && placeholder.is_some() { self.fmt_placeholder(placeholder) } else { let (left, cursor, right) = cursor.split(); format!("{left}{}{right}", Styled::new(cursor).rev()) } ) } _ => String::new(), } } fn fmt_input_submit(&self, input: PromptInput) -> String { match input { PromptInput::Raw(raw) => format!(" {}", Styled::new(raw).fg(Color::Green)), PromptInput::Cursor(cursor) => { format!(" {}", Styled::new(cursor.value()).fg(Color::Green)) } _ => String::new(), } } fn fmt_body_active(&self, body: PromptBody) -> String { match body { PromptBody::Raw(raw) => { format!("\n{}", raw) } _ => String::new(), } } fn fmt_body_submit(&self, body: PromptBody) -> String { match body { PromptBody::Raw(raw) => { format!("\n{}", Styled::new(raw).fg(Color::Green)) } _ => String::new(), } } } impl Theme for SimpleTheme { fn log(&mut self, term: &mut dyn Terminal, message: String) -> Result<(), Error> { term.writeln(&message)?; term.flush()?; Ok(()) } fn info(&mut self, term: &mut dyn Terminal, message: String) -> Result<(), Error> { self.log( term, format!("{} {}", Styled::new("[info]").fg(Color::Cyan), message), ) } fn warn(&mut self, term: &mut dyn Terminal, message: String) -> Result<(), Error> { self.log( term, format!("{} {}", Styled::new("[warn]").fg(Color::Yellow), message), ) } fn error(&mut self, term: &mut dyn Terminal, message: String) -> Result<(), Error> { self.log( term, format!("{} {}", Styled::new("[error]").fg(Color::Red), message), ) } fn success(&mut self, term: &mut dyn Terminal, message: String) -> Result<(), Error> { self.log( term, format!("{} {}", Styled::new("[success]").fg(Color::Green), message), ) } fn step(&mut self, term: &mut dyn Terminal, message: String) -> Result<(), Error> { self.log( term, format!("{} {}", Styled::new(S_STEP).fg(Color::Cyan), message), ) } fn begin(&mut self, term: &mut dyn Terminal, intro: Option) -> Result<(), Error> { term.cursor_hide()?; if let Some(intro) = intro { term.writeln(&format!("INTRO: '{}'", intro))?; } term.flush()?; Ok(()) } fn render( &mut self, term: &mut dyn Terminal, payload: promptuity::RenderSnapshot, ) -> Result<(), Error> { if self.prev_lines > 0 { term.move_previous_line(self.prev_lines)?; term.clear_cursor_down()?; } let mut output = String::new(); match payload.state { PromptState::Active => { output.push_str(&self.fmt_message(payload.message)); output.push_str(&self.fmt_input_active(payload.input, payload.placeholder)); output.push_str(&self.fmt_body_active(payload.body)); output.push_str(&self.fmt_hint(payload.hint)); self.prev_lines = output.lines().count() as u16; } PromptState::Error(msg) | PromptState::Fatal(msg) => { output.push_str(&self.fmt_message(payload.message)); output.push_str(&self.fmt_input_active(payload.input, payload.placeholder)); output.push_str(&self.fmt_body_active(payload.body)); output.push_str(&self.fmt_hint(payload.hint)); output.push_str(&format!("\n{}", Styled::new(msg).fg(Color::Red))); self.prev_lines = output.lines().count() as u16; } PromptState::Submit => { output.push_str(&self.fmt_message(payload.message)); output.push_str(&self.fmt_input_submit(payload.input)); output.push_str(&self.fmt_body_submit(payload.body)); output.push_str(&self.fmt_hint(payload.hint)); self.prev_lines = 0; } PromptState::Cancel => { output.push_str(&self.fmt_message(payload.message)); self.prev_lines = 0; } } term.writeln(&output)?; term.flush()?; Ok(()) } fn finish( &mut self, term: &mut dyn Terminal, _: &promptuity::PromptState, outro: Option, ) -> Result<(), Error> { term.cursor_show()?; if let Some(outro) = outro { term.writeln(&format!("OUTRO: '{}'", outro))?; } term.flush()?; Ok(()) } } fn main() -> Result<(), Error> { let mut term = Term::default(); let mut theme = SimpleTheme::new(); let mut p = Promptuity::new(&mut term, &mut theme); p.with_intro("Custom Theme DEMO").begin()?; let _ = p.prompt(Input::new("input message").as_mut())?; let _ = p.prompt( Select::new( "select message", vec![ SelectOption::new("value1", "value1"), SelectOption::new("value2", "value2"), SelectOption::new("value3", "value3"), ], ) .as_mut(), )?; p.with_outro("Outro Message").finish()?; Ok(()) }