mirror of https://github.com/tauri-apps/tauri
feat(core): support generics (especially Param) in #[command] (#1622)
* 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>
This commit is contained in:
parent
c78db1b399
commit
1453d4bf84
|
@ -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
|
|
@ -1,151 +0,0 @@
|
|||
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, TokenStreamExt};
|
||||
use syn::{
|
||||
parse::Parser, punctuated::Punctuated, FnArg, Ident, ItemFn, Pat, Path, ReturnType, Token, Type,
|
||||
Visibility,
|
||||
};
|
||||
|
||||
fn fn_wrapper(function: &ItemFn) -> (&Visibility, Ident) {
|
||||
(
|
||||
&function.vis,
|
||||
format_ident!("{}_wrapper", function.sig.ident),
|
||||
)
|
||||
}
|
||||
|
||||
fn err(function: ItemFn, error_message: &str) -> TokenStream {
|
||||
let (vis, wrap) = fn_wrapper(&function);
|
||||
quote! {
|
||||
#function
|
||||
|
||||
#vis fn #wrap<P: ::tauri::Params>(_message: ::tauri::InvokeMessage<P>) {
|
||||
compile_error!(#error_message);
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_command(function: ItemFn) -> TokenStream {
|
||||
let fn_name = function.sig.ident.clone();
|
||||
let fn_name_str = fn_name.to_string();
|
||||
let (vis, fn_wrapper) = fn_wrapper(&function);
|
||||
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,
|
||||
};
|
||||
|
||||
let mut invoke_arg_names: Vec<Ident> = Default::default();
|
||||
let mut invoke_arg_types: Vec<Path> = Default::default();
|
||||
let mut invoke_args: TokenStream = Default::default();
|
||||
|
||||
for param in &function.sig.inputs {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
let arg_name_ = arg_name.unwrap();
|
||||
let arg_name_s = arg_name_.to_string();
|
||||
|
||||
let arg_type = match arg_type {
|
||||
Some(arg_type) => arg_type,
|
||||
None => {
|
||||
return err(
|
||||
function.clone(),
|
||||
&format!("invalid type for arg: {}", arg_name_),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let item = quote!(::tauri::command::CommandItem {
|
||||
name: #fn_name_str,
|
||||
key: #arg_name_s,
|
||||
message: &__message,
|
||||
});
|
||||
|
||||
invoke_args.append_all(quote!(let #arg_name_ = <#arg_type>::from_command(#item)?;));
|
||||
invoke_arg_names.push(arg_name_);
|
||||
invoke_arg_types.push(arg_type);
|
||||
}
|
||||
|
||||
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!(::core::result::Result::Ok(#fn_name(#(#invoke_arg_names),*)#await_maybe?))
|
||||
} else {
|
||||
quote! { ::core::result::Result::<_, ::tauri::InvokeError>::Ok(#fn_name(#(#invoke_arg_names),*)#await_maybe) }
|
||||
};
|
||||
|
||||
// double underscore prefix temporary until underlying scoping issue is fixed (planned)
|
||||
quote! {
|
||||
#function
|
||||
|
||||
#vis fn #fn_wrapper<P: ::tauri::Params>(invoke: ::tauri::Invoke<P>) {
|
||||
use ::tauri::command::CommandArg;
|
||||
let ::tauri::Invoke { message: __message, resolver: __resolver } = invoke;
|
||||
__resolver.respond_async(async move {
|
||||
#invoke_args
|
||||
#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! {
|
||||
move |invoke| {
|
||||
let cmd = invoke.message.command();
|
||||
match cmd {
|
||||
#(stringify!(#fn_names) => #fn_wrappers(invoke),)*
|
||||
_ => {
|
||||
invoke.resolver.reject(format!("command {} not found", cmd))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,0 +1,186 @@
|
|||
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use std::convert::TryFrom;
|
||||
use syn::{spanned::Spanned, FnArg, Ident, ItemFn, Pat, ReturnType, Type, Visibility};
|
||||
|
||||
/// The command wrapper created for a function marked with `#[command]`.
|
||||
pub struct Wrapper {
|
||||
function: ItemFn,
|
||||
visibility: Visibility,
|
||||
maybe_export: TokenStream,
|
||||
wrapper: Ident,
|
||||
body: syn::Result<WrapperBody>,
|
||||
}
|
||||
|
||||
impl Wrapper {
|
||||
/// Create a new [`Wrapper`] from the function and the generated code parsed from the function.
|
||||
pub fn new(function: ItemFn, body: syn::Result<WrapperBody>) -> Self {
|
||||
// macros used with `pub use my_macro;` need to be exported with `#[macro_export]`
|
||||
let maybe_export = match &function.vis {
|
||||
Visibility::Public(_) => quote!(#[macro_export]),
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let visibility = function.vis.clone();
|
||||
let wrapper = super::format_command_wrapper(&function.sig.ident);
|
||||
|
||||
Self {
|
||||
function,
|
||||
visibility,
|
||||
maybe_export,
|
||||
wrapper,
|
||||
body,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Wrapper> for proc_macro::TokenStream {
|
||||
fn from(
|
||||
Wrapper {
|
||||
function,
|
||||
maybe_export,
|
||||
wrapper,
|
||||
body,
|
||||
visibility,
|
||||
}: Wrapper,
|
||||
) -> Self {
|
||||
// either use the successful body or a `compile_error!` of the error occurred while parsing it.
|
||||
let body = body
|
||||
.as_ref()
|
||||
.map(ToTokens::to_token_stream)
|
||||
.unwrap_or_else(syn::Error::to_compile_error);
|
||||
|
||||
// we `use` the macro so that other modules can resolve the with the same path as the function.
|
||||
// this is dependent on rust 2018 edition.
|
||||
quote!(
|
||||
#function
|
||||
#maybe_export
|
||||
macro_rules! #wrapper { ($path:path, $invoke:ident) => {{ #body }}; }
|
||||
#visibility use #wrapper;
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Body of the wrapper that maps the command parameters into callable arguments from [`Invoke`].
|
||||
///
|
||||
/// This is possible because we require the command parameters to be [`CommandArg`] and use type
|
||||
/// inference to put values generated from that trait into the arguments of the called command.
|
||||
///
|
||||
/// [`CommandArg`]: https://docs.rs/tauri/*/tauri/command/trait.CommandArg.html
|
||||
/// [`Invoke`]: https://docs.rs/tauri/*/tauri/struct.Invoke.html
|
||||
pub struct WrapperBody(TokenStream);
|
||||
|
||||
impl TryFrom<&ItemFn> for WrapperBody {
|
||||
type Error = syn::Error;
|
||||
|
||||
fn try_from(function: &ItemFn) -> syn::Result<Self> {
|
||||
// the name of the #[command] function is the name of the command to handle
|
||||
let command = function.sig.ident.clone();
|
||||
|
||||
// automatically append await when the #[command] function is async
|
||||
let maybe_await = match function.sig.asyncness {
|
||||
Some(_) => quote!(.await),
|
||||
None => Default::default(),
|
||||
};
|
||||
|
||||
// todo: detect command return types automatically like params, removes parsing type 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 == "Result")
|
||||
.unwrap_or_default(),
|
||||
_ => false,
|
||||
},
|
||||
ReturnType::Default => false,
|
||||
};
|
||||
|
||||
let mut args = Vec::new();
|
||||
for param in &function.sig.inputs {
|
||||
args.push(parse_arg(&command, param)?);
|
||||
}
|
||||
|
||||
// todo: change this to automatically detect result returns (see above result todo)
|
||||
// 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 result = if returns_result {
|
||||
quote! {
|
||||
let result = $path(#(#args?),*);
|
||||
::core::result::Result::Ok(result #maybe_await?)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let result = $path(#(#args?),*);
|
||||
::core::result::Result::<_, ::tauri::InvokeError>::Ok(result #maybe_await)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self(result))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for WrapperBody {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let body = &self.0;
|
||||
|
||||
// we #[allow(unused_variables)] because a command with no arguments will not use message.
|
||||
tokens.append_all(quote!(
|
||||
#[allow(unused_variables)]
|
||||
let ::tauri::Invoke { message, resolver } = $invoke;
|
||||
resolver.respond_async(async move { #body });
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform a [`FnArg`] into a command argument. Expects borrowable binding `message` to exist.
|
||||
fn parse_arg(command: &Ident, arg: &FnArg) -> syn::Result<TokenStream> {
|
||||
// we have no use for self arguments
|
||||
let mut arg = match arg {
|
||||
FnArg::Typed(arg) => arg.pat.as_ref().clone(),
|
||||
FnArg::Receiver(arg) => {
|
||||
return Err(syn::Error::new(
|
||||
arg.span(),
|
||||
"unable to use self as a command function parameter",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// we only support patterns supported as arguments to a `ItemFn`.
|
||||
let key = match &mut arg {
|
||||
Pat::Ident(arg) => arg.ident.to_string(),
|
||||
Pat::Wild(_) => "_".into(),
|
||||
Pat::Struct(s) => super::path_to_command(&mut s.path).ident.to_string(),
|
||||
Pat::TupleStruct(s) => super::path_to_command(&mut s.path).ident.to_string(),
|
||||
err => {
|
||||
return Err(syn::Error::new(
|
||||
err.span(),
|
||||
"only named, wildcard, struct, and tuple struct arguments allowed",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// also catch self arguments that use FnArg::Typed syntax
|
||||
if key == "self" {
|
||||
return Err(syn::Error::new(
|
||||
key.span(),
|
||||
"unable to use self as a command function parameter",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(quote!(::tauri::command::CommandArg::from_command(
|
||||
::tauri::command::CommandItem {
|
||||
name: stringify!(#command),
|
||||
key: #key,
|
||||
message: &message,
|
||||
}
|
||||
)))
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
extern crate proc_macro;
|
||||
use crate::context::ContextItems;
|
||||
use proc_macro::TokenStream;
|
||||
use std::convert::TryFrom;
|
||||
use syn::{parse_macro_input, ItemFn};
|
||||
|
||||
mod command;
|
||||
|
@ -15,14 +16,13 @@ mod context;
|
|||
#[proc_macro_attribute]
|
||||
pub fn command(_attrs: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let function = parse_macro_input!(item as ItemFn);
|
||||
let gen = command::generate_command(function);
|
||||
gen.into()
|
||||
let body = command::WrapperBody::try_from(&function);
|
||||
command::Wrapper::new(function, body).into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn generate_handler(item: TokenStream) -> TokenStream {
|
||||
let gen = command::generate_handler(item);
|
||||
gen.into()
|
||||
parse_macro_input!(item as command::Handler).into()
|
||||
}
|
||||
|
||||
/// Reads a Tauri config file and generates a `::tauri::Context` based on the content.
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
const container = document.querySelector('#container')
|
||||
const commands = [
|
||||
{ name: 'window_label', required: true },
|
||||
{ name: 'simple_command', required: true },
|
||||
{ name: 'stateful_command', required: false },
|
||||
{ name: 'async_simple_command', required: true },
|
||||
|
@ -32,14 +33,18 @@
|
|||
{ name: 'stateful_command_with_result', required: false },
|
||||
{ name: 'async_simple_command_with_result', required: true },
|
||||
{ name: 'async_stateful_command_with_result', required: false },
|
||||
{ name: 'command_arguments_wild', required: true },
|
||||
{ name: 'command_arguments_struct', required: true, args: { "Person": { "name": "ferris", age: 6 } } },
|
||||
{ name: 'command_arguments_tuple_struct', required: true, args: { "InlinePerson": [ "ferris", 6 ] } },
|
||||
]
|
||||
|
||||
for (command of commands) {
|
||||
for (const command of commands) {
|
||||
const { name, required } = command
|
||||
const args = command.args ?? { argument: 'value' }
|
||||
const button = document.createElement('button')
|
||||
button.innerHTML = `Run ${name}`;
|
||||
button.addEventListener("click", function () {
|
||||
runCommand(name, { argument: 'value' })
|
||||
runCommand(name, args)
|
||||
if (!required) {
|
||||
setTimeout(() => {
|
||||
runCommand(name, {})
|
||||
|
@ -52,4 +57,4 @@
|
|||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[tauri::command]
|
||||
pub fn simple_command(argument: String) {
|
||||
println!("{}", argument);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn stateful_command(argument: Option<String>, state: tauri::State<'_, super::MyState>) {
|
||||
println!("{:?} {:?}", argument, state.inner());
|
||||
}
|
|
@ -7,31 +7,33 @@
|
|||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
// we move some basic commands to a separate module just to show it works
|
||||
mod commands;
|
||||
|
||||
use serde::Deserialize;
|
||||
use tauri::{command, Params, State, Window};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MyState {
|
||||
pub struct MyState {
|
||||
value: u64,
|
||||
label: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn simple_command(argument: String) {
|
||||
println!("{}", argument);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn stateful_command(argument: Option<String>, state: tauri::State<'_, MyState>) {
|
||||
println!("{:?} {:?}", argument, state.inner());
|
||||
// ------------------------ Commands using Window ------------------------
|
||||
#[command]
|
||||
fn window_label(window: Window<impl Params<Label = String>>) {
|
||||
println!("window label: {}", window.label());
|
||||
}
|
||||
|
||||
// Async commands
|
||||
|
||||
#[tauri::command]
|
||||
#[command]
|
||||
async fn async_simple_command(argument: String) {
|
||||
println!("{}", argument);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn async_stateful_command(argument: Option<String>, state: tauri::State<'_, MyState>) {
|
||||
#[command]
|
||||
async fn async_stateful_command(argument: Option<String>, state: State<'_, MyState>) {
|
||||
println!("{:?} {:?}", argument, state.inner());
|
||||
}
|
||||
|
||||
|
@ -39,16 +41,16 @@ async fn async_stateful_command(argument: Option<String>, state: tauri::State<'_
|
|||
|
||||
type Result<T> = std::result::Result<T, ()>;
|
||||
|
||||
#[tauri::command]
|
||||
#[command]
|
||||
fn simple_command_with_result(argument: String) -> Result<String> {
|
||||
println!("{}", argument);
|
||||
(!argument.is_empty()).then(|| argument).ok_or(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[command]
|
||||
fn stateful_command_with_result(
|
||||
argument: Option<String>,
|
||||
state: tauri::State<'_, MyState>,
|
||||
state: State<'_, MyState>,
|
||||
) -> Result<String> {
|
||||
println!("{:?} {:?}", argument, state.inner());
|
||||
argument.ok_or(())
|
||||
|
@ -56,21 +58,47 @@ fn stateful_command_with_result(
|
|||
|
||||
// Async commands
|
||||
|
||||
#[tauri::command]
|
||||
#[command]
|
||||
async fn async_simple_command_with_result(argument: String) -> Result<String> {
|
||||
println!("{}", argument);
|
||||
Ok(argument)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[command]
|
||||
async fn async_stateful_command_with_result(
|
||||
argument: Option<String>,
|
||||
state: tauri::State<'_, MyState>,
|
||||
state: State<'_, MyState>,
|
||||
) -> Result<String> {
|
||||
println!("{:?} {:?}", argument, state.inner());
|
||||
Ok(argument.unwrap_or_else(|| "".to_string()))
|
||||
}
|
||||
|
||||
// Non-Ident command function arguments
|
||||
|
||||
#[command]
|
||||
fn command_arguments_wild<P: Params>(_: Window<P>) {
|
||||
println!("we saw the wildcard!")
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Person<'a> {
|
||||
name: &'a str,
|
||||
age: u8,
|
||||
}
|
||||
|
||||
#[command]
|
||||
fn command_arguments_struct(Person { name, age }: Person) {
|
||||
println!("received person struct with name: {} | age: {}", name, age)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct InlinePerson<'a>(&'a str, u8);
|
||||
|
||||
#[command]
|
||||
fn command_arguments_tuple_struct(InlinePerson(name, age): InlinePerson) {
|
||||
println!("received person tuple with name: {} | age: {}", name, age)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(MyState {
|
||||
|
@ -78,14 +106,18 @@ fn main() {
|
|||
label: "Tauri!".into(),
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
simple_command,
|
||||
stateful_command,
|
||||
window_label,
|
||||
commands::simple_command,
|
||||
commands::stateful_command,
|
||||
async_simple_command,
|
||||
async_stateful_command,
|
||||
command_arguments_wild,
|
||||
command_arguments_struct,
|
||||
simple_command_with_result,
|
||||
stateful_command_with_result,
|
||||
command_arguments_tuple_struct,
|
||||
async_simple_command_with_result,
|
||||
async_stateful_command_with_result
|
||||
async_stateful_command_with_result,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
|
|
@ -53,6 +53,7 @@ mod ui {
|
|||
|
||||
#[tauri::command]
|
||||
fn close_splashscreen<P: Params>(
|
||||
_: Window<P>, // force inference of P
|
||||
splashscreen: State<'_, SplashscreenWindow<P>>,
|
||||
main: State<'_, MainWindow<P>>,
|
||||
) {
|
||||
|
|
Loading…
Reference in New Issue