diff --git a/src/lib.rs b/src/lib.rs index 2bab74f..db03729 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,15 @@ mod answer; mod error; mod question; -pub use answer::{Answer, Answers, ExpandItem, ListItem}; use crossterm::tty::IsTty; -pub use question::{Choice::Choice, Choice::Separator, Question}; + +pub use answer::{Answer, Answers, ExpandItem, ListItem}; +pub use question::{Choice::Choice, Choice::Separator, Plugin, Question}; + +pub mod plugin { + pub use crate::Plugin; + pub use ui; +} pub struct PromptModule { questions: Q, @@ -71,7 +77,10 @@ where } } -pub fn prompt(questions: Vec) -> error::Result { +pub fn prompt<'m, 'w, 'f, 'v, 't, Q>(questions: Q) -> error::Result +where + Q: IntoIterator>, +{ PromptModule::new(questions).prompt_all() } diff --git a/src/question/mod.rs b/src/question/mod.rs index c393636..a14ab0a 100644 --- a/src/question/mod.rs +++ b/src/question/mod.rs @@ -9,12 +9,14 @@ mod number; #[macro_use] mod options; mod password; +mod plugin; mod rawlist; use crate::{error, Answer, Answers}; pub use choice::Choice; use choice::{get_sep_str, ChoiceList}; use options::Options; +pub use plugin::Plugin; use std::io::prelude::*; @@ -56,11 +58,13 @@ enum QuestionKind<'f, 'v, 't> { Checkbox(checkbox::Checkbox<'f, 'v, 't>), Password(password::Password<'f, 'v, 't>), Editor(editor::Editor<'f, 'v, 't>), + // random lifetime so that it doesn't have to be static + Plugin(Box), } impl Question<'_, '_, '_, '_, '_> { pub fn ask( - self, + mut self, answers: &Answers, w: &mut W, ) -> error::Result> { @@ -88,6 +92,7 @@ impl Question<'_, '_, '_, '_, '_> { QuestionKind::Checkbox(c) => c.ask(message, answers, w)?, QuestionKind::Password(p) => p.ask(message, answers, w)?, QuestionKind::Editor(e) => e.ask(message, answers, w)?, + QuestionKind::Plugin(ref mut o) => o.ask(message, answers, w)?, }; Ok(Some((name, res))) diff --git a/src/question/plugin.rs b/src/question/plugin.rs new file mode 100644 index 0000000..8f12be5 --- /dev/null +++ b/src/question/plugin.rs @@ -0,0 +1,55 @@ +use crate::{error, Answer, Answers}; + +use super::{Options, Question}; + +pub trait Plugin: std::fmt::Debug { + fn ask( + &mut self, + message: String, + answers: &Answers, + stdout: &mut dyn std::io::Write, + ) -> error::Result; +} + +pub struct PluginBuilder<'m, 'w, 'p> { + opts: Options<'m, 'w>, + plugin: Box, +} + +impl<'p, P: Plugin + 'p> From

for Box { + fn from(plugin: P) -> Self { + Box::new(plugin) + } +} + +impl Question<'static, 'static, 'static, 'static, 'static> { + pub fn plugin<'a, N, P>(name: N, plugin: P) -> PluginBuilder<'static, 'static, 'a> + where + N: Into, + P: Into>, + { + PluginBuilder { + opts: Options::new(name.into()), + plugin: plugin.into(), + } + } +} + +crate::impl_options_builder!(PluginBuilder<'q>; (this, opts) => { + PluginBuilder { + opts, + plugin: this.plugin, + } +}); + +impl<'m, 'w, 'q> PluginBuilder<'m, 'w, 'q> { + pub fn build(self) -> Question<'m, 'w, 'q, 'static, 'static> { + Question::new(self.opts, super::QuestionKind::Plugin(self.plugin)) + } +} + +impl<'m, 'w, 'q> From> for Question<'m, 'w, 'q, 'static, 'static> { + fn from(builder: PluginBuilder<'m, 'w, 'q>) -> Self { + builder.build() + } +}