Allow support for using custom prompts

Due to [object safety](https://doc.rust-lang.org/reference/items/traits.html#object-safety),
unlike in built prompts, the plugins can only take `&mut self` and be
given a `&mut dyn Write`.
This commit is contained in:
Lutetium-Vanadium 2021-04-20 14:15:07 +05:30
parent dd4a077bdf
commit 4148198cb7
3 changed files with 73 additions and 4 deletions

View File

@ -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<Q> {
questions: Q,
@ -71,7 +77,10 @@ where
}
}
pub fn prompt(questions: Vec<Question>) -> error::Result<Answers> {
pub fn prompt<'m, 'w, 'f, 'v, 't, Q>(questions: Q) -> error::Result<Answers>
where
Q: IntoIterator<Item = Question<'m, 'w, 'f, 'v, 't>>,
{
PromptModule::new(questions).prompt_all()
}

View File

@ -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<dyn Plugin + 'f>),
}
impl Question<'_, '_, '_, '_, '_> {
pub fn ask<W: Write>(
self,
mut self,
answers: &Answers,
w: &mut W,
) -> error::Result<Option<(String, Answer)>> {
@ -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)))

55
src/question/plugin.rs Normal file
View File

@ -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<Answer>;
}
pub struct PluginBuilder<'m, 'w, 'p> {
opts: Options<'m, 'w>,
plugin: Box<dyn Plugin + 'p>,
}
impl<'p, P: Plugin + 'p> From<P> for Box<dyn Plugin + 'p> {
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<String>,
P: Into<Box<dyn Plugin + 'a>>,
{
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<PluginBuilder<'m, 'w, 'q>> for Question<'m, 'w, 'q, 'static, 'static> {
fn from(builder: PluginBuilder<'m, 'w, 'q>) -> Self {
builder.build()
}
}