added questions
and prompt_module
macros
This commit is contained in:
parent
bf43beabfb
commit
2cc107cffe
|
@ -7,13 +7,15 @@ edition = "2018"
|
|||
[workspace]
|
||||
members = [
|
||||
".",
|
||||
"inquisition-ui"
|
||||
"inquisition-ui",
|
||||
"inquisition-macros",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
tempfile = "3"
|
||||
atty = "0.2"
|
||||
ui = { package = "inquisition-ui", path = "./inquisition-ui" }
|
||||
macros = { package = "inquisition-macros", path = "./inquisition-macros" }
|
||||
|
||||
ahash = { version = "0.7", optional = true }
|
||||
async-trait = { version = "0.1", optional = true }
|
||||
|
@ -26,6 +28,9 @@ smol-dep = { package = "smol", version = "1.2", optional = true }
|
|||
tokio-dep = { package = "tokio", version = "1.5", optional = true, features = ["fs", "process", "io-util"] }
|
||||
async-std-dep = { package = "async-std", version = "1.9", optional = true, features = ["unstable"] }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0.42"
|
||||
|
||||
[features]
|
||||
default = ["crossterm", "ahash"]
|
||||
crossterm = ["ui/crossterm"]
|
||||
|
|
14
inquisition-macros/Cargo.toml
Normal file
14
inquisition-macros/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "inquisition-macros"
|
||||
version = "0.0.1"
|
||||
authors = ["Lutetium Vanadium"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
syn = { version = "1", features = ["full"] }
|
||||
quote = "1"
|
||||
bitflags = "1.2"
|
204
inquisition-macros/src/helpers.rs
Normal file
204
inquisition-macros/src/helpers.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::{parse::Parse, spanned::Spanned, Token};
|
||||
|
||||
pub(crate) fn parse_optional_comma(
|
||||
input: syn::parse::ParseStream,
|
||||
) -> syn::Result<Option<Token![,]>> {
|
||||
let lookahead = input.lookahead1();
|
||||
if !lookahead.peek(Token![,]) {
|
||||
if input.is_empty() {
|
||||
return Ok(None);
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(input.parse::<Token![,]>()?))
|
||||
}
|
||||
|
||||
pub(crate) fn insert_non_dup<T: Parse>(
|
||||
ident: syn::Ident,
|
||||
item: &mut Option<T>,
|
||||
input: syn::parse::ParseStream,
|
||||
) -> syn::Result<()> {
|
||||
insert_non_dup_parse(ident, item, input, T::parse)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_non_dup_parse<T>(
|
||||
ident: syn::Ident,
|
||||
item: &mut Option<T>,
|
||||
input: syn::parse::ParseStream,
|
||||
parser: fn(syn::parse::ParseStream) -> syn::Result<T>,
|
||||
) -> syn::Result<()> {
|
||||
match item {
|
||||
Some(_) => Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("duplicate option `{}`", ident),
|
||||
)),
|
||||
None => {
|
||||
*item = Some(parser(input)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub(crate) enum Choices {
|
||||
Array(syn::punctuated::Punctuated<Choice, Token![,]>),
|
||||
Expr(syn::Expr),
|
||||
}
|
||||
|
||||
impl Choices {
|
||||
fn parse_impl(
|
||||
input: syn::parse::ParseStream,
|
||||
parser: fn(syn::parse::ParseStream) -> syn::Result<Choice>,
|
||||
) -> syn::Result<Self> {
|
||||
if input.peek(syn::token::Bracket) {
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
|
||||
Ok(Choices::Array(content.parse_terminated(parser)?))
|
||||
} else {
|
||||
Ok(Choices::Expr(input.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_choice(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Choices::parse_impl(input, Choice::parse)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_checkbox_choice(
|
||||
input: syn::parse::ParseStream,
|
||||
) -> syn::Result<Self> {
|
||||
Choices::parse_impl(input, parse_checkbox_choice)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Choices {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
match self {
|
||||
Choices::Array(ref elems) => {
|
||||
let mut choices = proc_macro2::TokenStream::new();
|
||||
for elem in elems.iter() {
|
||||
elem.to_tokens(&mut choices);
|
||||
choices.extend(quote! { , })
|
||||
}
|
||||
|
||||
tokens.extend(quote_spanned! {
|
||||
elems.span() => ::std::array::IntoIter::new([ #choices ])
|
||||
});
|
||||
}
|
||||
Choices::Expr(ref choices) => {
|
||||
choices.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum Choice {
|
||||
Choice(syn::Expr),
|
||||
Separator(syn::Expr),
|
||||
DefaultSeparator,
|
||||
}
|
||||
|
||||
impl Parse for Choice {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
match input.fork().parse::<syn::Ident>() {
|
||||
Ok(i) if i == "sep" || i == "separator" => {
|
||||
input.parse::<syn::Ident>()?;
|
||||
if input.is_empty() || input.peek(Token![,]) {
|
||||
Ok(Choice::DefaultSeparator)
|
||||
} else {
|
||||
Ok(Choice::Separator(input.parse()?))
|
||||
}
|
||||
}
|
||||
_ => Ok(Choice::Choice(input.parse()?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Choice {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let (ident_name, elem) = match self {
|
||||
Choice::Choice(elem) => ("Choice", elem),
|
||||
Choice::Separator(elem) => ("Separator", elem),
|
||||
Choice::DefaultSeparator => {
|
||||
tokens.extend(quote! { ::inquisition::DefaultSeparator });
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let ident = syn::Ident::new(ident_name, elem.span());
|
||||
|
||||
tokens.extend(quote_spanned! {
|
||||
elem.span() => ::inquisition::#ident(
|
||||
#[allow(clippy::useless_conversion)]
|
||||
#elem.into()
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn make_into(expr: syn::Expr) -> syn::Expr {
|
||||
syn::ExprMethodCall {
|
||||
attrs: Vec::new(),
|
||||
dot_token: syn::token::Dot(expr.span()),
|
||||
method: syn::Ident::new("into", expr.span()),
|
||||
paren_token: syn::token::Paren(expr.span()),
|
||||
turbofish: None,
|
||||
args: syn::punctuated::Punctuated::new(),
|
||||
receiver: Box::new(expr),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
// For checkbox, defaults can be given for each option, this method, takes option
|
||||
// (`choice`), and the default value to put (`default`), and produces the following as
|
||||
// an `Expr`:
|
||||
// ```
|
||||
// (choice.into(), default.into())`
|
||||
// ```
|
||||
fn make_checkbox_tuple(choice: syn::Expr, default: syn::Expr) -> syn::Expr {
|
||||
let paren_token = syn::token::Paren(
|
||||
choice
|
||||
.span()
|
||||
.join(default.span())
|
||||
.unwrap_or_else(|| choice.span()),
|
||||
);
|
||||
|
||||
let mut elems = syn::punctuated::Punctuated::new();
|
||||
elems.push_value(make_into(choice));
|
||||
elems.push(make_into(default));
|
||||
|
||||
syn::ExprTuple {
|
||||
attrs: Vec::new(),
|
||||
paren_token,
|
||||
elems,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn parse_checkbox_choice(input: syn::parse::ParseStream) -> syn::Result<Choice> {
|
||||
let choice = input.parse()?;
|
||||
|
||||
let choice = match choice {
|
||||
Choice::Choice(choice) if input.peek(Token![default]) => {
|
||||
input.parse::<Token![default]>()?;
|
||||
Choice::Choice(make_checkbox_tuple(choice, input.parse()?))
|
||||
}
|
||||
Choice::Choice(choice) => {
|
||||
let span = choice.span();
|
||||
Choice::Choice(make_checkbox_tuple(
|
||||
choice,
|
||||
syn::ExprLit {
|
||||
lit: syn::LitBool { value: false, span }.into(),
|
||||
attrs: Vec::new(),
|
||||
}
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
sep => sep,
|
||||
};
|
||||
|
||||
Ok(choice)
|
||||
}
|
36
inquisition-macros/src/lib.rs
Normal file
36
inquisition-macros/src/lib.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod helpers;
|
||||
mod question;
|
||||
|
||||
use question::*;
|
||||
use quote::quote;
|
||||
use syn::{parse::Parse, parse_macro_input, Token};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn questions(item: TokenStream) -> TokenStream {
|
||||
let p = parse_macro_input!(item as Questions);
|
||||
|
||||
let questions = p.questions.into_iter();
|
||||
|
||||
let ts = quote! {
|
||||
::std::array::IntoIter::new([
|
||||
#(#questions),*
|
||||
])
|
||||
};
|
||||
|
||||
ts.into()
|
||||
}
|
||||
|
||||
struct Questions {
|
||||
questions: syn::punctuated::Punctuated<Question, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for Questions {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self {
|
||||
questions: input.parse_terminated(Question::parse)?,
|
||||
})
|
||||
}
|
||||
}
|
469
inquisition-macros/src/question.rs
Normal file
469
inquisition-macros/src/question.rs
Normal file
|
@ -0,0 +1,469 @@
|
|||
use std::fmt;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::{parse::Parse, spanned::Spanned, Token};
|
||||
|
||||
use crate::helpers::*;
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct BuilderMethods: u8 {
|
||||
const DEFAULT = 0b000_0001;
|
||||
const TRANSFORM = 0b000_0010;
|
||||
const VAL_FIL = 0b000_0100;
|
||||
const LIST = 0b000_1000;
|
||||
const MASK = 0b001_0000;
|
||||
const EXTENSION = 0b010_0000;
|
||||
const PLUGIN = 0b100_0000;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum QuestionKind {
|
||||
Input,
|
||||
Int,
|
||||
Float,
|
||||
Confirm,
|
||||
List,
|
||||
Rawlist,
|
||||
Expand,
|
||||
Checkbox,
|
||||
Password,
|
||||
Editor,
|
||||
Plugin,
|
||||
}
|
||||
|
||||
impl QuestionKind {
|
||||
fn as_str(&self) -> &str {
|
||||
match self {
|
||||
QuestionKind::Input => "input",
|
||||
QuestionKind::Int => "int",
|
||||
QuestionKind::Float => "float",
|
||||
QuestionKind::Confirm => "confirm",
|
||||
QuestionKind::List => "list",
|
||||
QuestionKind::Rawlist => "rawlist",
|
||||
QuestionKind::Expand => "expand",
|
||||
QuestionKind::Checkbox => "checkbox",
|
||||
QuestionKind::Password => "password",
|
||||
QuestionKind::Editor => "editor",
|
||||
QuestionKind::Plugin => "plugin",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_builder_methods(&self) -> BuilderMethods {
|
||||
match *self {
|
||||
QuestionKind::Input | QuestionKind::Int | QuestionKind::Float => {
|
||||
BuilderMethods::DEFAULT
|
||||
| BuilderMethods::TRANSFORM
|
||||
| BuilderMethods::VAL_FIL
|
||||
}
|
||||
QuestionKind::Confirm => {
|
||||
BuilderMethods::DEFAULT | BuilderMethods::TRANSFORM
|
||||
}
|
||||
QuestionKind::List | QuestionKind::Rawlist | QuestionKind::Expand => {
|
||||
BuilderMethods::DEFAULT
|
||||
| BuilderMethods::TRANSFORM
|
||||
| BuilderMethods::LIST
|
||||
}
|
||||
QuestionKind::Checkbox => {
|
||||
BuilderMethods::DEFAULT
|
||||
| BuilderMethods::TRANSFORM
|
||||
| BuilderMethods::VAL_FIL
|
||||
| BuilderMethods::LIST
|
||||
}
|
||||
QuestionKind::Password => {
|
||||
BuilderMethods::TRANSFORM
|
||||
| BuilderMethods::VAL_FIL
|
||||
| BuilderMethods::MASK
|
||||
}
|
||||
QuestionKind::Editor => {
|
||||
BuilderMethods::DEFAULT
|
||||
| BuilderMethods::TRANSFORM
|
||||
| BuilderMethods::VAL_FIL
|
||||
| BuilderMethods::EXTENSION
|
||||
}
|
||||
QuestionKind::Plugin => BuilderMethods::PLUGIN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for QuestionKind {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
|
||||
let kind = if ident == "input" {
|
||||
QuestionKind::Input
|
||||
} else if ident == "int" {
|
||||
QuestionKind::Int
|
||||
} else if ident == "float" {
|
||||
QuestionKind::Float
|
||||
} else if ident == "confirm" {
|
||||
QuestionKind::Confirm
|
||||
} else if ident == "list" {
|
||||
QuestionKind::List
|
||||
} else if ident == "rawlist" {
|
||||
QuestionKind::Rawlist
|
||||
} else if ident == "expand" {
|
||||
QuestionKind::Expand
|
||||
} else if ident == "checkbox" {
|
||||
QuestionKind::Checkbox
|
||||
} else if ident == "password" {
|
||||
QuestionKind::Password
|
||||
} else if ident == "editor" {
|
||||
QuestionKind::Editor
|
||||
} else if ident == "plugin" {
|
||||
QuestionKind::Plugin
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("unknown question kind {}", ident),
|
||||
));
|
||||
};
|
||||
|
||||
Ok(kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for QuestionKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct QuestionOpts {
|
||||
pub(crate) message: Option<syn::Expr>,
|
||||
pub(crate) when: Option<syn::Expr>,
|
||||
pub(crate) ask_if_answered: Option<syn::Expr>,
|
||||
|
||||
pub(crate) default: Option<syn::Expr>,
|
||||
|
||||
pub(crate) validate: Option<syn::Expr>,
|
||||
pub(crate) filter: Option<syn::Expr>,
|
||||
pub(crate) transform: Option<syn::Expr>,
|
||||
pub(crate) validate_async: Option<syn::Expr>,
|
||||
pub(crate) filter_async: Option<syn::Expr>,
|
||||
pub(crate) transform_async: Option<syn::Expr>,
|
||||
|
||||
pub(crate) choices: Option<Choices>,
|
||||
pub(crate) page_size: Option<syn::Expr>,
|
||||
pub(crate) should_loop: Option<syn::Expr>,
|
||||
|
||||
pub(crate) mask: Option<syn::Expr>,
|
||||
pub(crate) extension: Option<syn::Expr>,
|
||||
|
||||
pub(crate) plugin: Option<syn::Expr>,
|
||||
}
|
||||
|
||||
impl Default for QuestionOpts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
message: None,
|
||||
when: None,
|
||||
ask_if_answered: None,
|
||||
|
||||
default: None,
|
||||
|
||||
validate: None,
|
||||
filter: None,
|
||||
transform: None,
|
||||
validate_async: None,
|
||||
filter_async: None,
|
||||
transform_async: None,
|
||||
|
||||
choices: None,
|
||||
page_size: None,
|
||||
should_loop: None,
|
||||
|
||||
mask: None,
|
||||
extension: None,
|
||||
|
||||
plugin: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a _valid_ ident is disallowed
|
||||
fn check_disallowed(
|
||||
ident: &syn::Ident,
|
||||
kind: QuestionKind,
|
||||
allowed: BuilderMethods,
|
||||
) -> syn::Result<()> {
|
||||
#[rustfmt::skip]
|
||||
fn disallowed(ident: &syn::Ident, allowed: BuilderMethods) -> bool {
|
||||
(ident == "default" &&
|
||||
!allowed.contains(BuilderMethods::DEFAULT)) ||
|
||||
|
||||
((ident == "transform_async" ||
|
||||
ident == "transform") &&
|
||||
!allowed.contains(BuilderMethods::TRANSFORM)) ||
|
||||
|
||||
((ident == "validate_async" ||
|
||||
ident == "validate" ||
|
||||
ident == "filter" ||
|
||||
ident == "filter_async") &&
|
||||
!allowed.contains(BuilderMethods::VAL_FIL)) ||
|
||||
|
||||
((ident == "choices" ||
|
||||
ident == "page_size" ||
|
||||
ident == "should_loop") &&
|
||||
!allowed.contains(BuilderMethods::LIST)) ||
|
||||
|
||||
(ident == "mask" &&
|
||||
!allowed.contains(BuilderMethods::MASK)) ||
|
||||
|
||||
(ident == "extension" &&
|
||||
!allowed.contains(BuilderMethods::EXTENSION)) ||
|
||||
|
||||
(ident == "plugin" &&
|
||||
!allowed.contains(BuilderMethods::PLUGIN))
|
||||
}
|
||||
|
||||
if disallowed(ident, allowed) {
|
||||
Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("option `{}` does not exist for kind `{}`", ident, kind),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Question {
|
||||
pub(crate) kind: QuestionKind,
|
||||
pub(crate) name: syn::Expr,
|
||||
pub(crate) opts: QuestionOpts,
|
||||
}
|
||||
|
||||
impl Parse for Question {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let kind = QuestionKind::parse(input)?;
|
||||
let content;
|
||||
let brace = syn::braced!(content in input);
|
||||
|
||||
let mut opts = QuestionOpts::default();
|
||||
let mut name = None;
|
||||
|
||||
let allowed_methods = kind.get_builder_methods();
|
||||
|
||||
while !content.is_empty() {
|
||||
if content.peek(Token![async]) {
|
||||
let asynct = content.parse::<Token![async]>()?;
|
||||
let ident = content.parse::<syn::Ident>()?;
|
||||
|
||||
content.parse::<Token![:]>()?;
|
||||
|
||||
let full_ident_str = format!("{}_async", ident);
|
||||
let full_ident = syn::Ident::new(
|
||||
&full_ident_str,
|
||||
asynct
|
||||
.span
|
||||
.join(ident.span())
|
||||
.unwrap_or_else(|| ident.span()),
|
||||
);
|
||||
|
||||
check_disallowed(&full_ident, kind, allowed_methods)?;
|
||||
if ident == "validate" {
|
||||
insert_non_dup(full_ident, &mut opts.validate_async, &content)?;
|
||||
} else if ident == "filter" {
|
||||
insert_non_dup(full_ident, &mut opts.filter_async, &content)?;
|
||||
} else if ident == "transform" {
|
||||
insert_non_dup(full_ident, &mut opts.transform_async, &content)?;
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("unknown question option `{}`", full_ident_str),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
let ident = content.parse::<syn::Ident>()?;
|
||||
|
||||
content.parse::<Token![:]>()?;
|
||||
|
||||
// it is not an issue if ident doesn't correspond to valid option
|
||||
// since check_allowed only checks if valid idents are disallowed
|
||||
check_disallowed(&ident, kind, allowed_methods)?;
|
||||
|
||||
// default options which are always there
|
||||
if ident == "name" {
|
||||
insert_non_dup(ident, &mut name, &content)?;
|
||||
} else if ident == "message" {
|
||||
insert_non_dup(ident, &mut opts.message, &content)?;
|
||||
} else if ident == "when" {
|
||||
insert_non_dup(ident, &mut opts.when, &content)?;
|
||||
} else if ident == "ask_if_answered" {
|
||||
insert_non_dup(ident, &mut opts.ask_if_answered, &content)?;
|
||||
} else {
|
||||
// the rest may or may not be there, so must be checked
|
||||
// it is not an issue if ident doesn't correspond to valid option
|
||||
// since check_allowed only checks if valid idents are disallowed
|
||||
check_disallowed(&ident, kind, allowed_methods)?;
|
||||
|
||||
if ident == "default" {
|
||||
insert_non_dup(ident, &mut opts.default, &content)?;
|
||||
} else if ident == "validate" {
|
||||
insert_non_dup(ident, &mut opts.validate, &content)?;
|
||||
} else if ident == "filter" {
|
||||
insert_non_dup(ident, &mut opts.filter, &content)?;
|
||||
} else if ident == "transform" {
|
||||
insert_non_dup(ident, &mut opts.transform, &content)?;
|
||||
} else if ident == "choices" {
|
||||
match kind {
|
||||
QuestionKind::Checkbox => insert_non_dup_parse(
|
||||
ident,
|
||||
&mut opts.choices,
|
||||
&content,
|
||||
Choices::parse_checkbox_choice,
|
||||
)?,
|
||||
_ => insert_non_dup_parse(
|
||||
ident,
|
||||
&mut opts.choices,
|
||||
&content,
|
||||
Choices::parse_choice,
|
||||
)?,
|
||||
}
|
||||
} else if ident == "page_size" {
|
||||
insert_non_dup(ident, &mut opts.page_size, &content)?;
|
||||
} else if ident == "should_loop" {
|
||||
insert_non_dup(ident, &mut opts.should_loop, &content)?;
|
||||
} else if ident == "mask" {
|
||||
insert_non_dup(ident, &mut opts.mask, &content)?;
|
||||
} else if ident == "extension" {
|
||||
insert_non_dup(ident, &mut opts.extension, &content)?;
|
||||
} else if ident == "plugin" {
|
||||
insert_non_dup(ident, &mut opts.plugin, &content)?;
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("unknown question option `{}`", ident),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if parse_optional_comma(&content)?.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let QuestionKind::Plugin = kind {
|
||||
if opts.plugin.is_none() {
|
||||
return Err(syn::Error::new(
|
||||
brace.span,
|
||||
"missing required option `plugin`",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
kind,
|
||||
name: name.ok_or_else(|| {
|
||||
syn::Error::new(brace.span, "missing required option `name`")
|
||||
})?,
|
||||
opts,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Question {
|
||||
fn write_main_opts(&self, tokens: &mut TokenStream) {
|
||||
if let Some(ref message) = self.opts.message {
|
||||
tokens.extend(quote_spanned! { message.span() => .message(#message) });
|
||||
}
|
||||
if let Some(ref when) = self.opts.when {
|
||||
tokens.extend(quote_spanned! { when.span() => .when(#when) });
|
||||
}
|
||||
if let Some(ref ask_if_answered) = self.opts.ask_if_answered {
|
||||
tokens.extend(quote_spanned! {
|
||||
ask_if_answered.span() => .ask_if_answered(#ask_if_answered)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl quote::ToTokens for Question {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let name = &self.name;
|
||||
|
||||
if let QuestionKind::Plugin = self.kind {
|
||||
let plugin = self.opts.plugin.as_ref().unwrap();
|
||||
// If just the name was passed into Question::plugin, type errors associated
|
||||
// with its conversion to a string would take the span _including_ that of
|
||||
// plugin. Explicitly performing `String::from`, makes the error span due to
|
||||
// the `From` trait will show the span of the name only
|
||||
let name = quote_spanned! {
|
||||
name.span() => String::from(#name)
|
||||
};
|
||||
tokens.extend(quote_spanned! {
|
||||
plugin.span() => ::inquisition::Question::plugin(#name, #plugin)
|
||||
});
|
||||
self.write_main_opts(tokens);
|
||||
tokens.extend(quote! { .build() });
|
||||
return;
|
||||
}
|
||||
|
||||
let kind = syn::Ident::new(self.kind.as_str(), name.span());
|
||||
|
||||
tokens.extend(quote_spanned! {
|
||||
name.span() => ::inquisition::Question::#kind(#name)
|
||||
});
|
||||
|
||||
self.write_main_opts(tokens);
|
||||
if let Some(ref default) = self.opts.default {
|
||||
tokens.extend(quote_spanned! { default.span() => .default(#default) });
|
||||
}
|
||||
if let Some(ref validate) = self.opts.validate {
|
||||
tokens
|
||||
.extend(quote_spanned! { validate.span() => .validate(#validate) });
|
||||
}
|
||||
if let Some(ref validate_async) = self.opts.validate_async {
|
||||
tokens.extend(quote_spanned! {
|
||||
validate_async.span() => .validate_async(#validate_async)
|
||||
});
|
||||
}
|
||||
if let Some(ref filter) = self.opts.filter {
|
||||
tokens.extend(quote_spanned! { filter.span() => .filter(#filter) });
|
||||
}
|
||||
if let Some(ref filter_async) = self.opts.filter_async {
|
||||
tokens.extend(quote_spanned! {
|
||||
filter_async.span() => .filter_async(#filter_async)
|
||||
});
|
||||
}
|
||||
if let Some(ref transform) = self.opts.transform {
|
||||
tokens.extend(
|
||||
quote_spanned! { transform.span() => .transform(#transform) },
|
||||
);
|
||||
}
|
||||
if let Some(ref transform_async) = self.opts.transform_async {
|
||||
tokens.extend(quote_spanned! {
|
||||
transform_async.span() => .transform_async(#transform_async)
|
||||
});
|
||||
}
|
||||
if let Some(ref choices) = self.opts.choices {
|
||||
tokens.extend(match self.kind {
|
||||
QuestionKind::Checkbox => {
|
||||
quote_spanned! { choices.span() => .choices_with_default(#choices) }
|
||||
}
|
||||
_ => quote_spanned! { choices.span() => .choices(#choices) },
|
||||
});
|
||||
}
|
||||
if let Some(ref page_size) = self.opts.page_size {
|
||||
tokens.extend(
|
||||
quote_spanned! { page_size.span() => .page_size(#page_size) },
|
||||
);
|
||||
}
|
||||
if let Some(ref should_loop) = self.opts.should_loop {
|
||||
tokens.extend(
|
||||
quote_spanned! { should_loop.span() => .should_loop(#should_loop) },
|
||||
);
|
||||
}
|
||||
if let Some(ref mask) = self.opts.mask {
|
||||
tokens.extend(quote_spanned! { mask.span() => .mask(#mask) });
|
||||
}
|
||||
if let Some(ref extension) = self.opts.extension {
|
||||
tokens.extend(
|
||||
quote_spanned! { extension.span() => .extension(#extension) },
|
||||
);
|
||||
}
|
||||
tokens.extend(quote! { .build() });
|
||||
}
|
||||
}
|
|
@ -124,9 +124,12 @@ pub struct ExpandItem {
|
|||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<(char, String)> for ExpandItem {
|
||||
fn from((key, name): (char, String)) -> Self {
|
||||
Self { key, name }
|
||||
impl<I: Into<String>> From<(char, I)> for ExpandItem {
|
||||
fn from((key, name): (char, I)) -> Self {
|
||||
Self {
|
||||
key,
|
||||
name: name.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
16
src/lib.rs
16
src/lib.rs
|
@ -4,11 +4,27 @@ pub mod question;
|
|||
use ui::{backend, error, events};
|
||||
|
||||
pub use answer::{Answer, Answers, ExpandItem, ListItem};
|
||||
pub use macros::questions;
|
||||
pub use question::{
|
||||
Choice::Choice, Choice::DefaultSeparator, Choice::Separator, Question,
|
||||
};
|
||||
pub use ui::error::{ErrorKind, Result};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! prompt_module {
|
||||
($($tt:tt)*) => {
|
||||
$crate::PromptModule::new($crate::questions! [ $($tt)* ])
|
||||
};
|
||||
}
|
||||
|
||||
pub mod plugin {
|
||||
pub use crate::{question::Plugin, Answer, Answers};
|
||||
pub use ui::{backend::Backend, events::Events};
|
||||
crate::cfg_async! {
|
||||
pub use ui::events::AsyncEvents;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PromptModule<Q> {
|
||||
questions: Q,
|
||||
|
|
|
@ -84,7 +84,7 @@ pub enum Choice<T> {
|
|||
|
||||
impl<T> Choice<T> {
|
||||
pub(crate) fn is_separator(&self) -> bool {
|
||||
matches!(self, Choice::Separator(_))
|
||||
matches!(self, Choice::Separator(_) | Choice::DefaultSeparator)
|
||||
}
|
||||
|
||||
pub(crate) fn as_ref(&self) -> Choice<&T> {
|
||||
|
@ -96,10 +96,9 @@ impl<T> Choice<T> {
|
|||
}
|
||||
|
||||
pub(crate) fn unwrap_choice(self) -> T {
|
||||
if let Choice::Choice(c) = self {
|
||||
c
|
||||
} else {
|
||||
panic!("Called unwrap_choice on separator")
|
||||
match self {
|
||||
Choice::Choice(c) => c,
|
||||
_ => panic!("Called unwrap_choice on separator"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ pub struct RawlistBuilder<'m, 'w, 't> {
|
|||
impl<'m, 'w, 't> RawlistBuilder<'m, 'w, 't> {
|
||||
pub(crate) fn new(name: String) -> Self {
|
||||
RawlistBuilder {
|
||||
opts: Options::new(name.into()),
|
||||
opts: Options::new(name),
|
||||
list: Default::default(),
|
||||
// It is one indexed for the user
|
||||
choice_count: 1,
|
||||
|
|
208
tests/macros.rs
Normal file
208
tests/macros.rs
Normal file
|
@ -0,0 +1,208 @@
|
|||
struct Runner {
|
||||
cases: trybuild::TestCases,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl Runner {
|
||||
fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
cases: trybuild::TestCases::new(),
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_fail(&self, test_name: &str) {
|
||||
self.cases
|
||||
.compile_fail(format!("tests/macros/{}/{}.rs", self.name, test_name))
|
||||
}
|
||||
|
||||
fn pass(&self, test_name: &str) {
|
||||
self.cases
|
||||
.pass(format!("tests/macros/{}/{}.rs", self.name, test_name))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate() {
|
||||
let t = Runner::new("duplicate");
|
||||
t.compile_fail("name");
|
||||
t.compile_fail("message");
|
||||
t.compile_fail("when");
|
||||
t.compile_fail("ask_if_answered");
|
||||
t.compile_fail("default");
|
||||
t.compile_fail("validate");
|
||||
t.compile_fail("filter");
|
||||
t.compile_fail("transform");
|
||||
t.compile_fail("validate_async");
|
||||
t.compile_fail("filter_async");
|
||||
t.compile_fail("transform_async");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown() {
|
||||
let t = Runner::new("unknown");
|
||||
t.compile_fail("kind");
|
||||
t.compile_fail("option");
|
||||
t.compile_fail("async-unknown");
|
||||
t.compile_fail("async-option");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checkbox() {
|
||||
let t = Runner::new("checkbox");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("default");
|
||||
t.compile_fail("default_with_sep");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirm() {
|
||||
let t = Runner::new("confirm");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("filter");
|
||||
t.compile_fail("filter_async");
|
||||
t.compile_fail("validate");
|
||||
t.compile_fail("validate_async");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor() {
|
||||
let t = Runner::new("editor");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand() {
|
||||
let t = Runner::new("expand");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("filter");
|
||||
t.compile_fail("filter_async");
|
||||
t.compile_fail("validate");
|
||||
t.compile_fail("validate_async");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float() {
|
||||
let t = Runner::new("float");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input() {
|
||||
let t = Runner::new("input");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int() {
|
||||
let t = Runner::new("int");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list() {
|
||||
let t = Runner::new("list");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("filter");
|
||||
t.compile_fail("filter_async");
|
||||
t.compile_fail("validate");
|
||||
t.compile_fail("validate_async");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn password() {
|
||||
let t = Runner::new("password");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("default");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin() {
|
||||
let t = Runner::new("plugin");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("default");
|
||||
t.compile_fail("transform");
|
||||
t.compile_fail("transform_async");
|
||||
t.compile_fail("filter");
|
||||
t.compile_fail("filter_async");
|
||||
t.compile_fail("validate");
|
||||
t.compile_fail("validate_async");
|
||||
t.compile_fail("choices");
|
||||
t.compile_fail("should_loop");
|
||||
t.compile_fail("page_size");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rawlist() {
|
||||
let t = Runner::new("rawlist");
|
||||
|
||||
t.pass("valid");
|
||||
t.compile_fail("filter");
|
||||
t.compile_fail("filter_async");
|
||||
t.compile_fail("validate");
|
||||
t.compile_fail("validate_async");
|
||||
t.compile_fail("mask");
|
||||
t.compile_fail("extension");
|
||||
t.compile_fail("plugin");
|
||||
}
|
3
tests/macros/checkbox/default.rs
Normal file
3
tests/macros/checkbox/default.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
inquisition::questions![checkbox { default: todo!() }];
|
||||
}
|
5
tests/macros/checkbox/default.stderr
Normal file
5
tests/macros/checkbox/default.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: missing required option `name`
|
||||
--> $DIR/default.rs:2:38
|
||||
|
|
||||
2 | inquisition::questions![checkbox { default: todo!() }];
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
10
tests/macros/checkbox/default_with_sep.rs
Normal file
10
tests/macros/checkbox/default_with_sep.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
fn main() {
|
||||
inquisition::questions! [
|
||||
checkbox {
|
||||
name: "name",
|
||||
choices: [
|
||||
sep "separator" default true,
|
||||
],
|
||||
}
|
||||
];
|
||||
}
|
5
tests/macros/checkbox/default_with_sep.stderr
Normal file
5
tests/macros/checkbox/default_with_sep.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: expected `,`
|
||||
--> $DIR/default_with_sep.rs:6:33
|
||||
|
|
||||
6 | sep "separator" default true,
|
||||
| ^^^^^^^
|
3
tests/macros/checkbox/extension.rs
Normal file
3
tests/macros/checkbox/extension.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![checkbox { extension: todo!() }];
|
||||
}
|
5
tests/macros/checkbox/extension.stderr
Normal file
5
tests/macros/checkbox/extension.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `extension` does not exist for kind `checkbox`
|
||||
--> $DIR/extension.rs:2:48
|
||||
|
|
||||
2 | let q = inquisition::questions![checkbox { extension: todo!() }];
|
||||
| ^^^^^^^^^
|
3
tests/macros/checkbox/mask.rs
Normal file
3
tests/macros/checkbox/mask.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![checkbox { mask: todo!() }];
|
||||
}
|
5
tests/macros/checkbox/mask.stderr
Normal file
5
tests/macros/checkbox/mask.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `mask` does not exist for kind `checkbox`
|
||||
--> $DIR/mask.rs:2:48
|
||||
|
|
||||
2 | let q = inquisition::questions![checkbox { mask: todo!() }];
|
||||
| ^^^^
|
3
tests/macros/checkbox/plugin.rs
Normal file
3
tests/macros/checkbox/plugin.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![checkbox { plugin: todo!() }];
|
||||
}
|
5
tests/macros/checkbox/plugin.stderr
Normal file
5
tests/macros/checkbox/plugin.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `plugin` does not exist for kind `checkbox`
|
||||
--> $DIR/plugin.rs:2:48
|
||||
|
|
||||
2 | let q = inquisition::questions![checkbox { plugin: todo!() }];
|
||||
| ^^^^^^
|
27
tests/macros/checkbox/valid.rs
Normal file
27
tests/macros/checkbox/valid.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
fn main() {
|
||||
let choice = "choice";
|
||||
let default_choice = true;
|
||||
|
||||
inquisition::questions! [
|
||||
checkbox {
|
||||
name: "name",
|
||||
transform: |_, _, _| Ok(()),
|
||||
async transform: |_, _, _| Box::pin(async { Ok(()) }),
|
||||
validate: |_, _| Ok(()),
|
||||
async validate: |_, _| Box::pin(async { Ok(()) }),
|
||||
filter: |t, _| t,
|
||||
async filter: |t, _| Box::pin(async move { t }),
|
||||
choices: [
|
||||
sep,
|
||||
sep "separator",
|
||||
separator,
|
||||
separator "separator",
|
||||
"choice",
|
||||
"choice" default true,
|
||||
choice default default_choice || false,
|
||||
],
|
||||
page_size: 0,
|
||||
should_loop: true,
|
||||
}
|
||||
];
|
||||
}
|
3
tests/macros/confirm/choices.rs
Normal file
3
tests/macros/confirm/choices.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
inquisition::questions![confirm { choices: todo!() }];
|
||||
}
|
5
tests/macros/confirm/choices.stderr
Normal file
5
tests/macros/confirm/choices.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `choices` does not exist for kind `confirm`
|
||||
--> $DIR/choices.rs:2:39
|
||||
|
|
||||
2 | inquisition::questions![confirm { choices: todo!() }];
|
||||
| ^^^^^^^
|
3
tests/macros/confirm/extension.rs
Normal file
3
tests/macros/confirm/extension.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![confirm { extension: todo!() }];
|
||||
}
|
5
tests/macros/confirm/extension.stderr
Normal file
5
tests/macros/confirm/extension.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `extension` does not exist for kind `confirm`
|
||||
--> $DIR/extension.rs:2:47
|
||||
|
|
||||
2 | let q = inquisition::questions![confirm { extension: todo!() }];
|
||||
| ^^^^^^^^^
|
3
tests/macros/confirm/filter.rs
Normal file
3
tests/macros/confirm/filter.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![confirm { filter: todo!() }];
|
||||
}
|
5
tests/macros/confirm/filter.stderr
Normal file
5
tests/macros/confirm/filter.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `filter` does not exist for kind `confirm`
|
||||
--> $DIR/filter.rs:2:47
|
||||
|
|
||||
2 | let q = inquisition::questions![confirm { filter: todo!() }];
|
||||
| ^^^^^^
|
3
tests/macros/confirm/filter_async.rs
Normal file
3
tests/macros/confirm/filter_async.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![confirm { async filter: todo!() }];
|
||||
}
|
5
tests/macros/confirm/filter_async.stderr
Normal file
5
tests/macros/confirm/filter_async.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `filter_async` does not exist for kind `confirm`
|
||||
--> $DIR/filter_async.rs:2:53
|
||||
|
|
||||
2 | let q = inquisition::questions![confirm { async filter: todo!() }];
|
||||
| ^^^^^^
|
3
tests/macros/confirm/mask.rs
Normal file
3
tests/macros/confirm/mask.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![confirm { mask: todo!() }];
|
||||
}
|
5
tests/macros/confirm/mask.stderr
Normal file
5
tests/macros/confirm/mask.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `mask` does not exist for kind `confirm`
|
||||
--> $DIR/mask.rs:2:47
|
||||
|
|
||||
2 | let q = inquisition::questions![confirm { mask: todo!() }];
|
||||
| ^^^^
|
3
tests/macros/confirm/page_size.rs
Normal file
3
tests/macros/confirm/page_size.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
inquisition::questions![confirm { page_size: todo!() }];
|
||||
}
|
5
tests/macros/confirm/page_size.stderr
Normal file
5
tests/macros/confirm/page_size.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `page_size` does not exist for kind `confirm`
|
||||
--> $DIR/page_size.rs:2:39
|
||||
|
|
||||
2 | inquisition::questions![confirm { page_size: todo!() }];
|
||||
| ^^^^^^^^^
|
3
tests/macros/confirm/plugin.rs
Normal file
3
tests/macros/confirm/plugin.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![confirm { plugin: todo!() }];
|
||||
}
|
5
tests/macros/confirm/plugin.stderr
Normal file
5
tests/macros/confirm/plugin.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `plugin` does not exist for kind `confirm`
|
||||
--> $DIR/plugin.rs:2:47
|
||||
|
|
||||
2 | let q = inquisition::questions![confirm { plugin: todo!() }];
|
||||
| ^^^^^^
|
5
tests/macros/confirm/should_loop.rs
Normal file
5
tests/macros/confirm/should_loop.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
inquisition::questions![confirm {
|
||||
should_loop: todo!()
|
||||
}];
|
||||
}
|
5
tests/macros/confirm/should_loop.stderr
Normal file
5
tests/macros/confirm/should_loop.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `should_loop` does not exist for kind `confirm`
|
||||
--> $DIR/should_loop.rs:3:9
|
||||
|
|
||||
3 | should_loop: todo!()
|
||||
| ^^^^^^^^^^^
|
8
tests/macros/confirm/valid.rs
Normal file
8
tests/macros/confirm/valid.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
fn main() {
|
||||
inquisition::questions![confirm {
|
||||
name: "name",
|
||||
default: true,
|
||||
transform: |_, _, _| Ok(()),
|
||||
async transform: |_, _, _| Box::pin(async { Ok(()) }),
|
||||
}];
|
||||
}
|
3
tests/macros/confirm/validate.rs
Normal file
3
tests/macros/confirm/validate.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![confirm { validate: todo!() }];
|
||||
}
|
5
tests/macros/confirm/validate.stderr
Normal file
5
tests/macros/confirm/validate.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `validate` does not exist for kind `confirm`
|
||||
--> $DIR/validate.rs:2:47
|
||||
|
|
||||
2 | let q = inquisition::questions![confirm { validate: todo!() }];
|
||||
| ^^^^^^^^
|
3
tests/macros/confirm/validate_async.rs
Normal file
3
tests/macros/confirm/validate_async.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![confirm { async validate: todo!() }];
|
||||
}
|
5
tests/macros/confirm/validate_async.stderr
Normal file
5
tests/macros/confirm/validate_async.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `validate_async` does not exist for kind `confirm`
|
||||
--> $DIR/validate_async.rs:2:53
|
||||
|
|
||||
2 | let q = inquisition::questions![confirm { async validate: todo!() }];
|
||||
| ^^^^^^^^
|
6
tests/macros/duplicate/ask_if_answered.rs
Normal file
6
tests/macros/duplicate/ask_if_answered.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
ask_if_answered: todo!(),
|
||||
ask_if_answered: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/ask_if_answered.stderr
Normal file
5
tests/macros/duplicate/ask_if_answered.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `ask_if_answered`
|
||||
--> $DIR/ask_if_answered.rs:4:9
|
||||
|
|
||||
4 | ask_if_answered: todo!(),
|
||||
| ^^^^^^^^^^^^^^^
|
6
tests/macros/duplicate/choices.rs
Normal file
6
tests/macros/duplicate/choices.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![list {
|
||||
choices: todo!(),
|
||||
choices: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/choices.stderr
Normal file
5
tests/macros/duplicate/choices.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `choices`
|
||||
--> $DIR/choices.rs:4:9
|
||||
|
|
||||
4 | choices: todo!(),
|
||||
| ^^^^^^^
|
6
tests/macros/duplicate/default.rs
Normal file
6
tests/macros/duplicate/default.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
default: todo!(),
|
||||
default: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/default.stderr
Normal file
5
tests/macros/duplicate/default.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `default`
|
||||
--> $DIR/default.rs:4:9
|
||||
|
|
||||
4 | default: todo!(),
|
||||
| ^^^^^^^
|
6
tests/macros/duplicate/extension.rs
Normal file
6
tests/macros/duplicate/extension.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![editor {
|
||||
extension: todo!(),
|
||||
extension: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/extension.stderr
Normal file
5
tests/macros/duplicate/extension.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `extension`
|
||||
--> $DIR/extension.rs:4:9
|
||||
|
|
||||
4 | extension: todo!(),
|
||||
| ^^^^^^^^^
|
6
tests/macros/duplicate/filter.rs
Normal file
6
tests/macros/duplicate/filter.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
filter: todo!(),
|
||||
filter: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/filter.stderr
Normal file
5
tests/macros/duplicate/filter.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `filter`
|
||||
--> $DIR/filter.rs:4:9
|
||||
|
|
||||
4 | filter: todo!(),
|
||||
| ^^^^^^
|
6
tests/macros/duplicate/filter_async.rs
Normal file
6
tests/macros/duplicate/filter_async.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
filter_async: todo!(),
|
||||
filter_async: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/filter_async.stderr
Normal file
5
tests/macros/duplicate/filter_async.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: unknown question option `filter_async`
|
||||
--> $DIR/filter_async.rs:3:9
|
||||
|
|
||||
3 | filter_async: todo!(),
|
||||
| ^^^^^^^^^^^^
|
6
tests/macros/duplicate/mask.rs
Normal file
6
tests/macros/duplicate/mask.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![password {
|
||||
mask: todo!(),
|
||||
mask: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/mask.stderr
Normal file
5
tests/macros/duplicate/mask.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `mask`
|
||||
--> $DIR/mask.rs:4:9
|
||||
|
|
||||
4 | mask: todo!(),
|
||||
| ^^^^
|
6
tests/macros/duplicate/message.rs
Normal file
6
tests/macros/duplicate/message.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
message: todo!(),
|
||||
message: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/message.stderr
Normal file
5
tests/macros/duplicate/message.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `message`
|
||||
--> $DIR/message.rs:4:9
|
||||
|
|
||||
4 | message: todo!(),
|
||||
| ^^^^^^^
|
6
tests/macros/duplicate/name.rs
Normal file
6
tests/macros/duplicate/name.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
name: todo!(),
|
||||
name: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/name.stderr
Normal file
5
tests/macros/duplicate/name.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `name`
|
||||
--> $DIR/name.rs:4:9
|
||||
|
|
||||
4 | name: todo!(),
|
||||
| ^^^^
|
6
tests/macros/duplicate/page_size.rs
Normal file
6
tests/macros/duplicate/page_size.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![list {
|
||||
page_size: todo!(),
|
||||
page_size: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/page_size.stderr
Normal file
5
tests/macros/duplicate/page_size.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `page_size`
|
||||
--> $DIR/page_size.rs:4:9
|
||||
|
|
||||
4 | page_size: todo!(),
|
||||
| ^^^^^^^^^
|
6
tests/macros/duplicate/plugin.rs
Normal file
6
tests/macros/duplicate/plugin.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![plugin {
|
||||
plugin: todo!(),
|
||||
plugin: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/plugin.stderr
Normal file
5
tests/macros/duplicate/plugin.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `plugin`
|
||||
--> $DIR/plugin.rs:4:9
|
||||
|
|
||||
4 | plugin: todo!(),
|
||||
| ^^^^^^
|
6
tests/macros/duplicate/should_loop.rs
Normal file
6
tests/macros/duplicate/should_loop.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![list {
|
||||
should_loop: todo!(),
|
||||
should_loop: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/should_loop.stderr
Normal file
5
tests/macros/duplicate/should_loop.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `should_loop`
|
||||
--> $DIR/should_loop.rs:4:9
|
||||
|
|
||||
4 | should_loop: todo!(),
|
||||
| ^^^^^^^^^^^
|
6
tests/macros/duplicate/transform.rs
Normal file
6
tests/macros/duplicate/transform.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
transform: todo!(),
|
||||
transform: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/transform.stderr
Normal file
5
tests/macros/duplicate/transform.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `transform`
|
||||
--> $DIR/transform.rs:4:9
|
||||
|
|
||||
4 | transform: todo!(),
|
||||
| ^^^^^^^^^
|
6
tests/macros/duplicate/transform_async.rs
Normal file
6
tests/macros/duplicate/transform_async.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
transform_async: todo!(),
|
||||
transform_async: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/transform_async.stderr
Normal file
5
tests/macros/duplicate/transform_async.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: unknown question option `transform_async`
|
||||
--> $DIR/transform_async.rs:3:9
|
||||
|
|
||||
3 | transform_async: todo!(),
|
||||
| ^^^^^^^^^^^^^^^
|
6
tests/macros/duplicate/validate.rs
Normal file
6
tests/macros/duplicate/validate.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
validate: todo!(),
|
||||
validate: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/validate.stderr
Normal file
5
tests/macros/duplicate/validate.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `validate`
|
||||
--> $DIR/validate.rs:4:9
|
||||
|
|
||||
4 | validate: todo!(),
|
||||
| ^^^^^^^^
|
6
tests/macros/duplicate/validate_async.rs
Normal file
6
tests/macros/duplicate/validate_async.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
validate_async: todo!(),
|
||||
validate_async: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/validate_async.stderr
Normal file
5
tests/macros/duplicate/validate_async.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: unknown question option `validate_async`
|
||||
--> $DIR/validate_async.rs:3:9
|
||||
|
|
||||
3 | validate_async: todo!(),
|
||||
| ^^^^^^^^^^^^^^
|
6
tests/macros/duplicate/when.rs
Normal file
6
tests/macros/duplicate/when.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![input {
|
||||
when: todo!(),
|
||||
when: todo!(),
|
||||
}];
|
||||
}
|
5
tests/macros/duplicate/when.stderr
Normal file
5
tests/macros/duplicate/when.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: duplicate option `when`
|
||||
--> $DIR/when.rs:4:9
|
||||
|
|
||||
4 | when: todo!(),
|
||||
| ^^^^
|
3
tests/macros/editor/choices.rs
Normal file
3
tests/macros/editor/choices.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
inquisition::questions![editor { choices: todo!() }];
|
||||
}
|
5
tests/macros/editor/choices.stderr
Normal file
5
tests/macros/editor/choices.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `choices` does not exist for kind `editor`
|
||||
--> $DIR/choices.rs:2:38
|
||||
|
|
||||
2 | inquisition::questions![editor { choices: todo!() }];
|
||||
| ^^^^^^^
|
3
tests/macros/editor/mask.rs
Normal file
3
tests/macros/editor/mask.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![editor { mask: todo!() }];
|
||||
}
|
5
tests/macros/editor/mask.stderr
Normal file
5
tests/macros/editor/mask.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `mask` does not exist for kind `editor`
|
||||
--> $DIR/mask.rs:2:46
|
||||
|
|
||||
2 | let q = inquisition::questions![editor { mask: todo!() }];
|
||||
| ^^^^
|
3
tests/macros/editor/page_size.rs
Normal file
3
tests/macros/editor/page_size.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
inquisition::questions![editor { page_size: todo!() }];
|
||||
}
|
5
tests/macros/editor/page_size.stderr
Normal file
5
tests/macros/editor/page_size.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `page_size` does not exist for kind `editor`
|
||||
--> $DIR/page_size.rs:2:38
|
||||
|
|
||||
2 | inquisition::questions![editor { page_size: todo!() }];
|
||||
| ^^^^^^^^^
|
3
tests/macros/editor/plugin.rs
Normal file
3
tests/macros/editor/plugin.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![editor { plugin: todo!() }];
|
||||
}
|
5
tests/macros/editor/plugin.stderr
Normal file
5
tests/macros/editor/plugin.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `plugin` does not exist for kind `editor`
|
||||
--> $DIR/plugin.rs:2:46
|
||||
|
|
||||
2 | let q = inquisition::questions![editor { plugin: todo!() }];
|
||||
| ^^^^^^
|
5
tests/macros/editor/should_loop.rs
Normal file
5
tests/macros/editor/should_loop.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
inquisition::questions![editor {
|
||||
should_loop: todo!()
|
||||
}];
|
||||
}
|
5
tests/macros/editor/should_loop.stderr
Normal file
5
tests/macros/editor/should_loop.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `should_loop` does not exist for kind `editor`
|
||||
--> $DIR/should_loop.rs:3:9
|
||||
|
|
||||
3 | should_loop: todo!()
|
||||
| ^^^^^^^^^^^
|
15
tests/macros/editor/valid.rs
Normal file
15
tests/macros/editor/valid.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
fn main() {
|
||||
inquisition::questions! [
|
||||
editor {
|
||||
name: "name",
|
||||
default: "hello world",
|
||||
extension: ".rs",
|
||||
transform: |_, _, _| Ok(()),
|
||||
async transform: |_, _, _| Box::pin(async { Ok(()) }),
|
||||
validate: |_, _| Ok(()),
|
||||
async validate: |_, _| Box::pin(async { Ok(()) }),
|
||||
filter: |t, _| t,
|
||||
async filter: |t, _| Box::pin(async move { t }),
|
||||
}
|
||||
];
|
||||
}
|
3
tests/macros/expand/extension.rs
Normal file
3
tests/macros/expand/extension.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![expand { extension: todo!() }];
|
||||
}
|
5
tests/macros/expand/extension.stderr
Normal file
5
tests/macros/expand/extension.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `extension` does not exist for kind `expand`
|
||||
--> $DIR/extension.rs:2:46
|
||||
|
|
||||
2 | let q = inquisition::questions![expand { extension: todo!() }];
|
||||
| ^^^^^^^^^
|
3
tests/macros/expand/filter.rs
Normal file
3
tests/macros/expand/filter.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![expand { filter: todo!() }];
|
||||
}
|
5
tests/macros/expand/filter.stderr
Normal file
5
tests/macros/expand/filter.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `filter` does not exist for kind `expand`
|
||||
--> $DIR/filter.rs:2:46
|
||||
|
|
||||
2 | let q = inquisition::questions![expand { filter: todo!() }];
|
||||
| ^^^^^^
|
3
tests/macros/expand/filter_async.rs
Normal file
3
tests/macros/expand/filter_async.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![expand { async filter: todo!() }];
|
||||
}
|
5
tests/macros/expand/filter_async.stderr
Normal file
5
tests/macros/expand/filter_async.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `filter_async` does not exist for kind `expand`
|
||||
--> $DIR/filter_async.rs:2:52
|
||||
|
|
||||
2 | let q = inquisition::questions![expand { async filter: todo!() }];
|
||||
| ^^^^^^
|
3
tests/macros/expand/mask.rs
Normal file
3
tests/macros/expand/mask.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![expand { mask: todo!() }];
|
||||
}
|
5
tests/macros/expand/mask.stderr
Normal file
5
tests/macros/expand/mask.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `mask` does not exist for kind `expand`
|
||||
--> $DIR/mask.rs:2:46
|
||||
|
|
||||
2 | let q = inquisition::questions![expand { mask: todo!() }];
|
||||
| ^^^^
|
3
tests/macros/expand/plugin.rs
Normal file
3
tests/macros/expand/plugin.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![expand { plugin: todo!() }];
|
||||
}
|
5
tests/macros/expand/plugin.stderr
Normal file
5
tests/macros/expand/plugin.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `plugin` does not exist for kind `expand`
|
||||
--> $DIR/plugin.rs:2:46
|
||||
|
|
||||
2 | let q = inquisition::questions![expand { plugin: todo!() }];
|
||||
| ^^^^^^
|
13
tests/macros/expand/valid.rs
Normal file
13
tests/macros/expand/valid.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
fn main() {
|
||||
inquisition::questions! [
|
||||
expand {
|
||||
name: "name",
|
||||
default: 'c',
|
||||
transform: |_, _, _| Ok(()),
|
||||
async transform: |_, _, _| Box::pin(async { Ok(()) }),
|
||||
choices: [('c', "choice")],
|
||||
page_size: 0,
|
||||
should_loop: true,
|
||||
}
|
||||
];
|
||||
}
|
3
tests/macros/expand/validate.rs
Normal file
3
tests/macros/expand/validate.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let q = inquisition::questions![expand { validate: todo!() }];
|
||||
}
|
5
tests/macros/expand/validate.stderr
Normal file
5
tests/macros/expand/validate.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: option `validate` does not exist for kind `expand`
|
||||
--> $DIR/validate.rs:2:46
|
||||
|
|
||||
2 | let q = inquisition::questions![expand { validate: todo!() }];
|
||||
| ^^^^^^^^
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user