diff --git a/src/question/mod.rs b/src/question/mod.rs index b2b1d65..206f413 100644 --- a/src/question/mod.rs +++ b/src/question/mod.rs @@ -28,6 +28,7 @@ pub use choice::Choice; use choice::{get_sep_str, ChoiceList}; use options::Options; pub use plugin::Plugin; +use plugin::PluginInteral; use ui::{backend::Backend, error, events::KeyEvent}; use std::fmt; @@ -39,7 +40,7 @@ pub struct Question<'a> { } impl<'a> Question<'a> { - pub(crate) fn new(opts: Options<'a>, kind: QuestionKind<'a>) -> Self { + fn new(opts: Options<'a>, kind: QuestionKind<'a>) -> Self { Self { kind, opts } } } @@ -85,17 +86,18 @@ impl Question<'static> { EditorBuilder::new(name.into()) } - pub fn plugin<'a, N, P>(name: N, plugin: P) -> PluginBuilder<'a> + pub fn plugin<'a, N, P, I>(name: N, plugin: I) -> PluginBuilder<'a> where N: Into, - P: Into>, + P: Plugin + 'a, + I: Into

, { - PluginBuilder::new(name.into(), plugin.into()) + PluginBuilder::new(name.into(), Box::new(Some(plugin.into()))) } } #[derive(Debug)] -pub(crate) enum QuestionKind<'a> { +enum QuestionKind<'a> { Input(input::Input<'a>), Int(number::Int<'a>), Float(number::Float<'a>), @@ -106,8 +108,7 @@ pub(crate) enum QuestionKind<'a> { Checkbox(checkbox::Checkbox<'a>), Password(password::Password<'a>), Editor(editor::Editor<'a>), - // random lifetime so that it doesn't have to be static - Plugin(Box), + Plugin(Box), } impl Question<'_> { diff --git a/src/question/plugin.rs b/src/question/plugin.rs index c2c0e22..704c168 100644 --- a/src/question/plugin.rs +++ b/src/question/plugin.rs @@ -4,6 +4,23 @@ use super::{Options, Question, QuestionKind}; use crate::{Answer, Answers}; pub trait Plugin: std::fmt::Debug { + fn ask( + self, + message: String, + answers: &Answers, + stdout: &mut dyn Backend, + events: &mut dyn Iterator>, + ) -> error::Result; +} + +/// The same trait as `Plugin`, except it take `&mut self` instead of `self`. +/// +/// This is required since traits with functions that take `self` are not object safe, and so +/// implementors of the trait would have to use &mut self even though it will only be called once. +/// +/// Now instead of QuestionKind::Plugin having a `dyn Plugin`, it has a `dyn PluginInteral`, which +/// is an `Option`. +pub(super) trait PluginInteral: std::fmt::Debug { fn ask( &mut self, message: String, @@ -13,19 +30,33 @@ pub trait Plugin: std::fmt::Debug { ) -> error::Result; } -pub struct PluginBuilder<'a> { - opts: Options<'a>, - plugin: Box, +impl PluginInteral for Option { + fn ask( + &mut self, + message: String, + answers: &Answers, + stdout: &mut dyn Backend, + events: &mut dyn Iterator>, + ) -> error::Result { + self.take() + .expect("Plugin::ask called twice") + .ask(message, answers, stdout, events) + } } -impl<'a, P: Plugin + 'a> From

for Box { +pub struct PluginBuilder<'a> { + opts: Options<'a>, + plugin: Box, +} + +impl<'a, P: PluginInteral + 'a> From

for Box { fn from(plugin: P) -> Self { Box::new(plugin) } } impl<'a> PluginBuilder<'a> { - pub(crate) fn new(name: String, plugin: Box) -> Self { + pub(super) fn new(name: String, plugin: Box) -> Self { Self { opts: Options::new(name), plugin,