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:
parent
dd4a077bdf
commit
4148198cb7
15
src/lib.rs
15
src/lib.rs
|
@ -2,9 +2,15 @@ mod answer;
|
||||||
mod error;
|
mod error;
|
||||||
mod question;
|
mod question;
|
||||||
|
|
||||||
pub use answer::{Answer, Answers, ExpandItem, ListItem};
|
|
||||||
use crossterm::tty::IsTty;
|
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> {
|
pub struct PromptModule<Q> {
|
||||||
questions: 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()
|
PromptModule::new(questions).prompt_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,14 @@ mod number;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod options;
|
mod options;
|
||||||
mod password;
|
mod password;
|
||||||
|
mod plugin;
|
||||||
mod rawlist;
|
mod rawlist;
|
||||||
|
|
||||||
use crate::{error, Answer, Answers};
|
use crate::{error, Answer, Answers};
|
||||||
pub use choice::Choice;
|
pub use choice::Choice;
|
||||||
use choice::{get_sep_str, ChoiceList};
|
use choice::{get_sep_str, ChoiceList};
|
||||||
use options::Options;
|
use options::Options;
|
||||||
|
pub use plugin::Plugin;
|
||||||
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
@ -56,11 +58,13 @@ enum QuestionKind<'f, 'v, 't> {
|
||||||
Checkbox(checkbox::Checkbox<'f, 'v, 't>),
|
Checkbox(checkbox::Checkbox<'f, 'v, 't>),
|
||||||
Password(password::Password<'f, 'v, 't>),
|
Password(password::Password<'f, 'v, 't>),
|
||||||
Editor(editor::Editor<'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<'_, '_, '_, '_, '_> {
|
impl Question<'_, '_, '_, '_, '_> {
|
||||||
pub fn ask<W: Write>(
|
pub fn ask<W: Write>(
|
||||||
self,
|
mut self,
|
||||||
answers: &Answers,
|
answers: &Answers,
|
||||||
w: &mut W,
|
w: &mut W,
|
||||||
) -> error::Result<Option<(String, Answer)>> {
|
) -> error::Result<Option<(String, Answer)>> {
|
||||||
|
@ -88,6 +92,7 @@ impl Question<'_, '_, '_, '_, '_> {
|
||||||
QuestionKind::Checkbox(c) => c.ask(message, answers, w)?,
|
QuestionKind::Checkbox(c) => c.ask(message, answers, w)?,
|
||||||
QuestionKind::Password(p) => p.ask(message, answers, w)?,
|
QuestionKind::Password(p) => p.ask(message, answers, w)?,
|
||||||
QuestionKind::Editor(e) => e.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)))
|
Ok(Some((name, res)))
|
||||||
|
|
55
src/question/plugin.rs
Normal file
55
src/question/plugin.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user