Skip to content

Commit

Permalink
feat(core): support generics (especially Param) in #[command] (#1622)
Browse files Browse the repository at this point in the history
* wip: param argument proof of concept for #[command]

* use macros for automatic type inference in commands

* refactor command for better error handling

* remove redundant ToTokens impl for Wrapper and Handler

* create `StateP` to allow state to use type inference during commands

* wrap State instead of T

* remove accidental edit of attribute

* remove StateP

because we recommend `_: Window<P>` for type inference, the following
function types are now supported:
* Pat::Wild (arg: "_")
* Pat::Struct (arg: final path segment)
* Pat::TupleStruct (arg: final path segment)

* add wildcard, struct, and tuple struct commands to examples

* better unsupported command argument message

* feat(examples): move some commands to a separate module

* add change file

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
  • Loading branch information
chippers and lucasfernog committed May 5, 2021
1 parent c78db1b commit 1453d4b
Show file tree
Hide file tree
Showing 10 changed files with 363 additions and 179 deletions.
6 changes: 6 additions & 0 deletions .changes/command-generics.md
@@ -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
@@ -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
@@ -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")
}

0 comments on commit 1453d4b

Please sign in to comment.