mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-23 06:51:01 +01:00
Merge pull request #20 from teloxide/update-dispatching
Remove support for the old dispatching
This commit is contained in:
commit
dfba097c71
3 changed files with 8 additions and 172 deletions
|
@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## unreleased
|
||||
|
||||
### Removed
|
||||
|
||||
- Support for the old dispatching: `#[teloxide(subtransition)]`.
|
||||
|
||||
## 0.5.1 - 2022-03-23
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -99,9 +99,9 @@ pub fn expand(item: ItemEnum) -> Result<TokenStream, syn::Error> {
|
|||
fn assert_clone<T: Clone>() {}
|
||||
|
||||
use teloxide::dptree;
|
||||
use teloxide::dispatching2::dialogue::Dialogue;
|
||||
use teloxide::dispatching::dialogue::Dialogue;
|
||||
|
||||
impl #self_params_with_bounds teloxide::dispatching2::HandlerFactory for #enum_ident #self_params #where_clause {
|
||||
impl #self_params_with_bounds teloxide::dispatching::HandlerFactory for #enum_ident #self_params #where_clause {
|
||||
type Out = #out;
|
||||
|
||||
fn handler() -> dptree::Handler<'static, dptree::di::DependencyMap, Self::Out> {
|
||||
|
|
172
src/lib.rs
172
src/lib.rs
|
@ -18,185 +18,17 @@ use crate::{
|
|||
};
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{
|
||||
parse_macro_input, DeriveInput, Fields, FnArg, ItemEnum, ItemFn,
|
||||
ReturnType, Type,
|
||||
};
|
||||
|
||||
use std::fmt::Write;
|
||||
use syn::{parse_macro_input, DeriveInput, Fields, ItemEnum};
|
||||
|
||||
#[proc_macro_derive(DialogueState, attributes(handler, handler_out, store))]
|
||||
pub fn derive_dialogue_state(item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as ItemEnum);
|
||||
match dialogue_state::expand(input) {
|
||||
Ok(s) => s.into(),
|
||||
Err(e) => e.into_compile_error().into(),
|
||||
Err(e) => e.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The docs is below.
|
||||
///
|
||||
/// The only accepted form at the current moment is `#[teloxide(subtransition)]`
|
||||
/// on an asynchronous function. Either this:
|
||||
///
|
||||
/// ```no_compile
|
||||
/// #[teloxide(subtransition)]
|
||||
/// async fn my_transition(state: MyState, cx: TransitionIn, ans: T) -> TransitionOut<MyDialogue> {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or this:
|
||||
///
|
||||
/// ```no_compile
|
||||
/// #[teloxide(subtransition)]
|
||||
/// async fn my_transition(state: MyState, cx: TransitionIn) -> TransitionOut<MyDialogue> {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Notice the presence/absence of `ans: T`. In the first case, it generates
|
||||
/// `impl SubTransition for MyState { type Aux = T; type Dialogue = MyDialogue;
|
||||
/// ... }`. In the second case, the `Aux` type defaults to `()`.
|
||||
#[proc_macro_attribute]
|
||||
pub fn teloxide(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
match attr.to_string().as_ref() {
|
||||
"subtransition" => {
|
||||
let item_cloned = item.clone();
|
||||
let input = parse_macro_input!(item as ItemFn);
|
||||
let params = input.sig.inputs.iter().collect::<Vec<&FnArg>>();
|
||||
|
||||
if params.len() != 2 && params.len() != 3 {
|
||||
panic!(
|
||||
"An transition function must accept two/three parameters: \
|
||||
a state type, TransitionIn, and an optional data."
|
||||
);
|
||||
}
|
||||
|
||||
// This is actually used inside the quite! { ... } below.
|
||||
#[allow(unused_variables)]
|
||||
let state_type = match params[0] {
|
||||
FnArg::Typed(pat_type) => &pat_type.ty,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let fn_name = input.sig.ident;
|
||||
let fn_return_type = match input.sig.output {
|
||||
ReturnType::Type(_arrow, _type) => _type,
|
||||
_ => panic!(
|
||||
"A subtransition must return TransitionOut<your dialogue \
|
||||
type>"
|
||||
),
|
||||
};
|
||||
let requester_param_type = match params
|
||||
.get(1)
|
||||
.expect("A requester parameter must be specified")
|
||||
{
|
||||
FnArg::Typed(typed) => typed.ty.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let aux_param_type = match params.get(2) {
|
||||
Some(data_param_type) => match *data_param_type {
|
||||
FnArg::Typed(typed) => typed.ty.clone(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => {
|
||||
let unit_type = proc_macro::TokenStream::from(quote! {()});
|
||||
Box::new(parse_macro_input!(unit_type as Type))
|
||||
}
|
||||
};
|
||||
let call_fn = match params.get(2) {
|
||||
Some(_) => {
|
||||
quote! { #fn_name(self, cx, aux) }
|
||||
}
|
||||
None => quote! { #fn_name(self, cx) },
|
||||
};
|
||||
|
||||
let item = proc_macro2::TokenStream::from(item_cloned);
|
||||
|
||||
let impl_transition = quote! {
|
||||
impl teloxide::dispatching::dialogue::Subtransition for #state_type {
|
||||
type Aux = #aux_param_type;
|
||||
type Dialogue = <#fn_return_type as teloxide::dispatching::dialogue::SubtransitionOutputType>::Output;
|
||||
type Error = <#fn_return_type as teloxide::dispatching::dialogue::SubtransitionOutputType>::Error;
|
||||
type Requester = <#requester_param_type as teloxide::dispatching::UpdateWithCxRequesterType>::Requester;
|
||||
|
||||
fn react(self, cx: teloxide::dispatching::dialogue::TransitionIn<Self::Requester>, aux: #aux_param_type)
|
||||
-> futures::future::BoxFuture<'static, #fn_return_type> {
|
||||
#item
|
||||
futures::future::FutureExt::boxed(#call_fn)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
impl_transition.into()
|
||||
}
|
||||
_ => {
|
||||
panic!("Unrecognised attribute '{}'", attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The docs is below.
|
||||
///
|
||||
/// All the variants must be of the form `VariantName(MyStateType)`, and
|
||||
/// `MyStateType` must implement `Subtransition`. All `MyStateType`s must have
|
||||
/// the same `Subtransition::Aux` and `Subtransition::Error`, which will be also
|
||||
/// used in the generated implementation.
|
||||
#[proc_macro_derive(Transition)]
|
||||
pub fn derive_transition(item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as ItemEnum);
|
||||
let mut dispatch_fn = "".to_owned();
|
||||
|
||||
let enum_name = input.ident;
|
||||
let field_type_of_first_variant =
|
||||
match &input.variants.iter().next().unwrap().fields {
|
||||
Fields::Unnamed(fields) => {
|
||||
fields
|
||||
.unnamed
|
||||
.iter()
|
||||
.next()
|
||||
// .unwrap() because empty enumerations are not yet allowed
|
||||
// in stable Rust.
|
||||
.unwrap()
|
||||
.ty
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
}
|
||||
_ => panic!("Only one unnamed field per variant is allowed"),
|
||||
};
|
||||
|
||||
write!(
|
||||
dispatch_fn,
|
||||
"impl teloxide::dispatching::dialogue::Transition for {1} {{type Aux \
|
||||
= <{0} as teloxide::dispatching::dialogue::Subtransition>::Aux;type \
|
||||
Error = <{0} as \
|
||||
teloxide::dispatching::dialogue::Subtransition>::Error;type \
|
||||
Requester = <{0} as \
|
||||
teloxide::dispatching::dialogue::Subtransition>::Requester;fn \
|
||||
react(self, cx: \
|
||||
teloxide::dispatching::dialogue::TransitionIn<Self::Requester>, aux: \
|
||||
Self::Aux) -> futures::future::BoxFuture<'static, \
|
||||
teloxide::dispatching::dialogue::TransitionOut<Self, Self::Error>> \
|
||||
{{ futures::future::FutureExt::boxed(async move {{ match self {{",
|
||||
field_type_of_first_variant, enum_name
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for variant in input.variants.iter() {
|
||||
write!(
|
||||
dispatch_fn,
|
||||
"{}::{}(state) => \
|
||||
teloxide::dispatching::dialogue::Subtransition::react(state, cx, \
|
||||
aux).await,",
|
||||
enum_name, variant.ident
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
write!(dispatch_fn, "}} }}) }} }}").unwrap();
|
||||
dispatch_fn.parse().unwrap()
|
||||
}
|
||||
|
||||
macro_rules! get_or_return {
|
||||
($($some:tt)*) => {
|
||||
match $($some)* {
|
||||
|
|
Loading…
Reference in a new issue