From ff7bf89cb2b419aba7fd8fd98685abaccd407753 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Fri, 24 May 2024 23:48:17 +1000 Subject: [PATCH] allow for custom names to be specified for a handler --- .changes/handler-names.md | 5 ++++ crates/qubit-macros/src/handler.rs | 37 ++++++++++++++++++++++-------- crates/qubit-macros/src/lib.rs | 14 ++++++----- examples/counter/bindings.ts | 2 +- examples/counter/src/main.rs | 4 ++-- 5 files changed, 44 insertions(+), 18 deletions(-) create mode 100644 .changes/handler-names.md diff --git a/.changes/handler-names.md b/.changes/handler-names.md new file mode 100644 index 0000000..0c3c460 --- /dev/null +++ b/.changes/handler-names.md @@ -0,0 +1,5 @@ +--- +"qubit-macros": patch:feat +--- + +specify custom names for handlers using `#[handler(name = "my_handler")]` diff --git a/crates/qubit-macros/src/handler.rs b/crates/qubit-macros/src/handler.rs index d9b1623..f37c2b1 100644 --- a/crates/qubit-macros/src/handler.rs +++ b/crates/qubit-macros/src/handler.rs @@ -1,8 +1,8 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ - meta::ParseNestedMeta, spanned::Spanned, Error, FnArg, ItemFn, Pat, Result, ReturnType, Type, - TypeImplTrait, + meta::ParseNestedMeta, spanned::Spanned, Error, FnArg, ItemFn, LitStr, Pat, Result, ReturnType, + Type, TypeImplTrait, }; /// Handlers can have different variations depending on how they interact with the client. @@ -15,14 +15,33 @@ pub enum HandlerKind { Subscription, } -impl HandlerKind { +/// Options that may be attached to a handler. +#[derive(Default)] +pub struct HandlerOptions { + /// The kind of handler. + pub kind: Option, + + /// Overridden name for the handler. + pub name: Option, +} + +impl HandlerOptions { /// Attempt to parse the handler kind from [`ParseNestedMeta`]. pub fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> { if meta.path.is_ident("query") { - *self = Self::Query; + self.kind = Some(HandlerKind::Query); Ok(()) } else if meta.path.is_ident("subscription") { - *self = Self::Subscription; + self.kind = Some(HandlerKind::Subscription); + Ok(()) + } else if meta.path.is_ident("name") { + // Extract name from the attribute + let name = meta.value()?.parse::()?.value(); + + // Create the ident for the handler name + let ident = Ident::new(&name, meta.input.span()); + + self.name = Some(ident); Ok(()) } else { Err(meta.error("unsupported handler property")) @@ -34,7 +53,7 @@ impl HandlerKind { /// [`HandlerKind`] is required alter how the handler is applied to the router. This could be /// induced based on the return type of the handler (whether it retrusn a [`futures::Stream`]) or /// not), but that might cause problems. -pub fn generate_handler(handler: ItemFn, kind: HandlerKind) -> Result { +pub fn generate_handler(handler: ItemFn, options: HandlerOptions) -> Result { let span = handler.span().clone(); // Handlers must be async @@ -50,8 +69,8 @@ pub fn generate_handler(handler: ItemFn, kind: HandlerKind) -> Result Result ( quote! { rpc_builder.query(#function_name_str, |ctx, params| async move { diff --git a/crates/qubit-macros/src/lib.rs b/crates/qubit-macros/src/lib.rs index 548a5e0..f8fabec 100644 --- a/crates/qubit-macros/src/lib.rs +++ b/crates/qubit-macros/src/lib.rs @@ -1,7 +1,9 @@ -use handler::{generate_handler, HandlerKind}; +use handler::generate_handler; use quote::quote; use syn::{meta, parse_macro_input, spanned::Spanned, Error, Item}; +use crate::handler::HandlerOptions; + mod handler; /// See [`qubit::builder::handler`] for more information. @@ -11,21 +13,21 @@ pub fn handler( input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { // Extract information from the attribute - let kind = { - let mut kind = HandlerKind::Query; + let options = { + let mut options = HandlerOptions::default(); - let attribute_parser = meta::parser(|meta| kind.parse(meta)); + let attribute_parser = meta::parser(|meta| options.parse(meta)); parse_macro_input!(attr with attribute_parser); - kind + options }; // Attempt to match as a function syn::parse::(input) .and_then(|item| { if let Item::Fn(handler) = item { - generate_handler(handler, kind) + generate_handler(handler, options) } else { Err(Error::new(item.span(), "handlers must be a method")) } diff --git a/examples/counter/bindings.ts b/examples/counter/bindings.ts index cf24f35..0525235 100644 --- a/examples/counter/bindings.ts +++ b/examples/counter/bindings.ts @@ -2,4 +2,4 @@ import type { Stream } from "@qubit-rs/client"; export type Metadata = { param_a: string, param_b: number, param_c: boolean, more_metadata: Metadata | null, }; export type Test = { a: number, b: boolean, }; export type User = { name: string, email: string, age: number, metadata: Metadata, }; -export type Server = { version: () => Promise, count: () => Promise, countdown: (min: number, max: number) => Stream, array: () => Promise>, user: { get: (_id: string) => Promise, create: (name: string, email: string, age: number) => Promise, list: () => Promise>, asdf: () => Promise } }; \ No newline at end of file +export type Server = { version: () => Promise, count: () => Promise, countdown: (min: number, max: number) => Stream, array: () => Promise>, user: { someHandler: (_id: string) => Promise, create: (name: string, email: string, age: number) => Promise, list: () => Promise>, asdf: () => Promise } }; \ No newline at end of file diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index 7d44a24..a6b64cb 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -65,13 +65,13 @@ mod user { pub fn create_router() -> Router { Router::new() - .handler(get) + .handler(someHandler) .handler(create) .handler(list) .handler(nested::asdf) } - #[handler] + #[handler(name = "someHandler")] async fn get(_ctx: UserCtx, _id: String) -> User { User { name: "some user".to_string(),