-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Co-authored-by: chip <[email protected]> Co-authored-by: Lucas Nogueira <[email protected]> Co-authored-by: Lucas Fernandes Nogueira <[email protected]>
- Loading branch information
1 parent
6951cae
commit 1f2e7a3
Showing
13 changed files
with
210 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"tauri-macros": minor | ||
--- | ||
|
||
Added new macros to simplify the creation of commands that can be called by the webview. |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::{format_ident, quote}; | ||
use syn::{ | ||
parse::Parser, punctuated::Punctuated, FnArg, Ident, ItemFn, Meta, NestedMeta, Pat, Path, | ||
ReturnType, Token, Type, | ||
}; | ||
|
||
pub fn generate_command(attrs: Vec<NestedMeta>, function: ItemFn) -> TokenStream { | ||
// Check if "with_manager" attr was passed to macro | ||
let uses_manager = attrs.iter().any(|a| { | ||
if let NestedMeta::Meta(Meta::Path(path)) = a { | ||
path | ||
.get_ident() | ||
.map(|i| *i == "with_manager") | ||
.unwrap_or(false) | ||
} else { | ||
false | ||
} | ||
}); | ||
|
||
let fn_name = function.sig.ident.clone(); | ||
let fn_name_str = fn_name.to_string(); | ||
let fn_wrapper = format_ident!("{}_wrapper", fn_name); | ||
let returns_result = match function.sig.output { | ||
ReturnType::Type(_, ref ty) => match &**ty { | ||
Type::Path(type_path) => { | ||
type_path | ||
.path | ||
.segments | ||
.first() | ||
.map(|seg| seg.ident.to_string()) | ||
== Some("Result".to_string()) | ||
} | ||
_ => false, | ||
}, | ||
ReturnType::Default => false, | ||
}; | ||
|
||
// Split function args into names and types | ||
let (mut names, mut types): (Vec<Ident>, Vec<Path>) = function | ||
.sig | ||
.inputs | ||
.iter() | ||
.map(|param| { | ||
let mut arg_name = None; | ||
let mut arg_type = None; | ||
if let FnArg::Typed(arg) = param { | ||
if let Pat::Ident(ident) = arg.pat.as_ref() { | ||
arg_name = Some(ident.ident.clone()); | ||
} | ||
if let Type::Path(path) = arg.ty.as_ref() { | ||
arg_type = Some(path.path.clone()); | ||
} | ||
} | ||
( | ||
arg_name.clone().unwrap(), | ||
arg_type.unwrap_or_else(|| panic!("Invalid type for arg \"{}\"", arg_name.unwrap())), | ||
) | ||
}) | ||
.unzip(); | ||
|
||
// If function doesn't take the webview manager, wrapper just takes webview manager generically and ignores it | ||
// Otherwise the wrapper uses the specific type from the original function declaration | ||
let mut manager_arg_type = quote!(::tauri::WebviewManager<A>); | ||
let mut application_ext_generic = quote!(<A: ::tauri::ApplicationExt>); | ||
let manager_arg_maybe = match types.first() { | ||
Some(first_type) if uses_manager => { | ||
// Give wrapper specific type | ||
manager_arg_type = quote!(#first_type); | ||
// Generic is no longer needed | ||
application_ext_generic = quote!(); | ||
// Remove webview manager arg from list so it isn't expected as arg from JS | ||
types.drain(0..1); | ||
names.drain(0..1); | ||
// Tell wrapper to pass webview manager to original function | ||
quote!(_manager,) | ||
} | ||
// Tell wrapper not to pass webview manager to original function | ||
_ => quote!(), | ||
}; | ||
let await_maybe = if function.sig.asyncness.is_some() { | ||
quote!(.await) | ||
} else { | ||
quote!() | ||
}; | ||
|
||
// if the command handler returns a Result, | ||
// we just map the values to the ones expected by Tauri | ||
// otherwise we wrap it with an `Ok()`, converting the return value to tauri::InvokeResponse | ||
// note that all types must implement `serde::Serialize`. | ||
let return_value = if returns_result { | ||
quote! { | ||
match #fn_name(#manager_arg_maybe #(parsed_args.#names),*)#await_maybe { | ||
Ok(value) => ::core::result::Result::Ok(value.into()), | ||
Err(e) => ::core::result::Result::Err(tauri::Error::Command(::serde_json::to_value(e)?)), | ||
} | ||
} | ||
} else { | ||
quote! { ::core::result::Result::Ok(#fn_name(#manager_arg_maybe #(parsed_args.#names),*)#await_maybe.into()) } | ||
}; | ||
|
||
quote! { | ||
#function | ||
pub async fn #fn_wrapper #application_ext_generic(_manager: #manager_arg_type, arg: ::serde_json::Value) -> ::tauri::Result<::tauri::InvokeResponse> { | ||
#[derive(::serde::Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct ParsedArgs { | ||
#(#names: #types),* | ||
} | ||
let parsed_args: ParsedArgs = ::serde_json::from_value(arg).map_err(|e| ::tauri::Error::InvalidArgs(#fn_name_str, e))?; | ||
#return_value | ||
} | ||
} | ||
} | ||
|
||
pub fn generate_handler(item: proc_macro::TokenStream) -> TokenStream { | ||
// Get paths of functions passed to macro | ||
let paths = <Punctuated<Path, Token![,]>>::parse_terminated | ||
.parse(item) | ||
.expect("generate_handler!: Failed to parse list of command functions"); | ||
|
||
// Get names of functions, used for match statement | ||
let fn_names = paths | ||
.iter() | ||
.map(|p| p.segments.last().unwrap().ident.clone()); | ||
|
||
// Get paths to wrapper functions | ||
let fn_wrappers = paths.iter().map(|func| { | ||
let mut func = func.clone(); | ||
let mut last_segment = func.segments.last_mut().unwrap(); | ||
last_segment.ident = format_ident!("{}_wrapper", last_segment.ident); | ||
func | ||
}); | ||
|
||
quote! { | ||
|webview_manager, arg| async move { | ||
let dispatch: ::std::result::Result<::tauri::DispatchInstructions, ::serde_json::Error> = | ||
::serde_json::from_str(&arg); | ||
match dispatch { | ||
Err(e) => Err(e.into()), | ||
Ok(dispatch) => { | ||
match dispatch.cmd.as_str() { | ||
#(stringify!(#fn_names) => #fn_wrappers(webview_manager, dispatch.args).await,)* | ||
_ => Err(tauri::Error::UnknownApi(None)), | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,19 @@ | ||
use serde::Deserialize; | ||
use tauri::command; | ||
|
||
#[derive(Debug, Deserialize)] | ||
pub struct RequestBody { | ||
id: i32, | ||
name: String, | ||
} | ||
|
||
#[derive(Deserialize)] | ||
#[serde(tag = "cmd", rename_all = "camelCase")] | ||
pub enum Cmd { | ||
LogOperation { | ||
event: String, | ||
payload: Option<String>, | ||
}, | ||
PerformRequest { | ||
endpoint: String, | ||
body: RequestBody, | ||
}, | ||
#[command] | ||
pub fn log_operation(event: String, payload: Option<String>) { | ||
println!("{} {:?}", event, payload); | ||
} | ||
|
||
#[command] | ||
pub fn perform_request(endpoint: String, body: RequestBody) -> String { | ||
println!("{} {:?}", endpoint, body); | ||
"message response".into() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters