Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): support generics (especially Param) in #[command] #1622

Merged
merged 13 commits into from
May 5, 2021
Merged
6 changes: 6 additions & 0 deletions .changes/command-generics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri-macros": patch
---

`#[command]` now generates a macro instead of a function to allow passing through `Params` and other generics.
`generate_handler!` has been changed to consume the generated `#[command]` macro
151 changes: 0 additions & 151 deletions core/tauri-macros/src/command.rs

This file was deleted.

65 changes: 65 additions & 0 deletions core/tauri-macros/src/command/handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use syn::{
parse::{Parse, ParseBuffer},
Ident, Path, Token,
};

/// The items parsed from [`generate_handle!`](crate::generate_handle).
pub struct Handler {
paths: Vec<Path>,
commands: Vec<Ident>,
wrappers: Vec<Path>,
}

impl Parse for Handler {
fn parse(input: &ParseBuffer) -> syn::Result<Self> {
let paths = input.parse_terminated::<Path, Token![,]>(Path::parse)?;

// parse the command names and wrappers from the passed paths
let (commands, wrappers) = paths
.iter()
.map(|path| {
let mut wrapper = path.clone();
let last = super::path_to_command(&mut wrapper);

// the name of the actual command function
let command = last.ident.clone();

// set the path to the command function wrapper
last.ident = super::format_command_wrapper(&command);

(command, wrapper)
})
.unzip();

Ok(Self {
paths: paths.into_iter().collect(), // remove punctuation separators
commands,
wrappers,
})
}
}

impl From<Handler> for proc_macro::TokenStream {
fn from(
Handler {
paths,
commands,
wrappers,
}: Handler,
) -> Self {
quote::quote!(move |invoke| {
let cmd = invoke.message.command();
match cmd {
#(stringify!(#commands) => #wrappers!(#paths, invoke),)*
_ => {
invoke.resolver.reject(format!("command {} not found", cmd))
},
}
})
.into()
}
}
27 changes: 27 additions & 0 deletions core/tauri-macros/src/command/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use proc_macro2::Ident;
use syn::{Path, PathSegment};

pub use self::{
handler::Handler,
wrapper::{Wrapper, WrapperBody},
};

mod handler;
mod wrapper;

/// The autogenerated wrapper ident.
fn format_command_wrapper(function: &Ident) -> Ident {
quote::format_ident!("__cmd__{}", function)
}

/// This function will panic if the passed [`syn::Path`] does not have any segments.
fn path_to_command(path: &mut Path) -> &mut PathSegment {
path
.segments
.last_mut()
.expect("parsed syn::Path has no segment")
}