1#![doc = include_str!("../README.md")]
2
3pub mod backend;
4
5pub use backend::{AnswerValue, Answers, BackendError, InterviewBackend, TestBackend};
6pub use derive_wizard_macro::*;
7pub use derive_wizard_types::{interview, question};
8
9#[cfg(feature = "requestty-backend")]
10pub use backend::requestty_backend::RequesttyBackend;
11
12#[cfg(feature = "dialoguer-backend")]
13pub use backend::dialoguer_backend::DialoguerBackend;
14
15#[cfg(feature = "egui-backend")]
16pub use backend::egui_backend::EguiBackend;
17
18pub trait Wizard: Sized {
19 fn interview() -> interview::Interview;
21
22 fn interview_with_defaults(&self) -> interview::Interview;
24
25 fn from_answers(answers: &Answers) -> Result<Self, BackendError>;
27
28 fn wizard_builder() -> WizardBuilder<Self> {
30 WizardBuilder::new()
31 }
32}
33
34#[derive(Default)]
36pub struct WizardBuilder<T: Wizard> {
37 defaults: Option<T>,
38 backend: Option<Box<dyn InterviewBackend>>,
39}
40
41impl<T: Wizard> WizardBuilder<T> {
42 pub fn new() -> Self {
44 Self {
45 defaults: None,
46 backend: None,
47 }
48 }
49
50 pub fn with_defaults(mut self, defaults: T) -> Self {
52 self.defaults = Some(defaults);
53 self
54 }
55
56 pub fn with_backend<B: InterviewBackend + 'static>(mut self, backend: B) -> Self {
58 self.backend = Some(Box::new(backend));
59 self
60 }
61
62 #[cfg(feature = "requestty-backend")]
64 pub fn build(self) -> T {
65 use crate::backend::requestty_backend::RequesttyBackend;
66
67 let backend = self.backend.unwrap_or_else(|| Box::new(RequesttyBackend));
68
69 let interview = if let Some(ref defaults) = self.defaults {
70 defaults.interview_with_defaults()
71 } else {
72 T::interview()
73 };
74
75 let answers = backend
76 .execute(&interview)
77 .expect("Failed to execute interview");
78 T::from_answers(&answers).expect("Failed to build from answers")
79 }
80
81 #[cfg(not(feature = "requestty-backend"))]
83 pub fn build(self) -> T {
84 let backend = self
85 .backend
86 .expect("No backend specified and requestty-backend feature is not enabled");
87
88 let interview = if let Some(ref defaults) = self.defaults {
89 defaults.interview_with_defaults()
90 } else {
91 T::interview()
92 };
93
94 let answers = backend
95 .execute(&interview)
96 .expect("Failed to execute interview");
97 T::from_answers(&answers).expect("Failed to build from answers")
98 }
99}