Skip to content

Commit

Permalink
allow for custom names to be specified for a handler
Browse files Browse the repository at this point in the history
  • Loading branch information
andogq committed May 24, 2024
1 parent 907a3e1 commit ff7bf89
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changes/handler-names.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"qubit-macros": patch:feat
---

specify custom names for handlers using `#[handler(name = "my_handler")]`
37 changes: 28 additions & 9 deletions crates/qubit-macros/src/handler.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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<HandlerKind>,

/// Overridden name for the handler.
pub name: Option<Ident>,
}

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::<LitStr>()?.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"))
Expand All @@ -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<TokenStream> {
pub fn generate_handler(handler: ItemFn, options: HandlerOptions) -> Result<TokenStream> {
let span = handler.span().clone();

// Handlers must be async
Expand All @@ -50,8 +69,8 @@ pub fn generate_handler(handler: ItemFn, kind: HandlerKind) -> Result<TokenStrea
};

// Extract out the function name
let function_name_str = handler.sig.ident.to_string();
let function_ident = handler.sig.ident;
let function_ident = options.name.unwrap_or(handler.sig.ident);
let function_name_str = function_ident.to_string();
let function_visibility = handler.vis;

// Extract out the return type
Expand Down Expand Up @@ -102,7 +121,7 @@ pub fn generate_handler(handler: ItemFn, kind: HandlerKind) -> Result<TokenStrea
}
});

let (register_impl, signature) = match kind {
let (register_impl, signature) = match options.kind.unwrap_or(HandlerKind::Query) {
HandlerKind::Query => (
quote! {
rpc_builder.query(#function_name_str, |ctx, params| async move {
Expand Down
14 changes: 8 additions & 6 deletions crates/qubit-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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::<Item>(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"))
}
Expand Down
2 changes: 1 addition & 1 deletion examples/counter/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>, count: () => Promise<number>, countdown: (min: number, max: number) => Stream<number>, array: () => Promise<Array<string>>, user: { get: (_id: string) => Promise<User>, create: (name: string, email: string, age: number) => Promise<User>, list: () => Promise<Array<Test>>, asdf: () => Promise<null> } };
export type Server = { version: () => Promise<string>, count: () => Promise<number>, countdown: (min: number, max: number) => Stream<number>, array: () => Promise<Array<string>>, user: { someHandler: (_id: string) => Promise<User>, create: (name: string, email: string, age: number) => Promise<User>, list: () => Promise<Array<Test>>, asdf: () => Promise<null> } };
4 changes: 2 additions & 2 deletions examples/counter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ mod user {

pub fn create_router() -> Router<AppCtx> {
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(),
Expand Down

0 comments on commit ff7bf89

Please sign in to comment.