tauri_macros/lib.rs
1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! Create macros for `tauri::Context`, invoke handler and commands leveraging the `tauri-codegen` crate.
6
7#![doc(
8 html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
9 html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
10)]
11
12use std::path::PathBuf;
13
14use crate::context::ContextItems;
15use proc_macro::TokenStream;
16use quote::{quote, ToTokens};
17use syn::{parse2, parse_macro_input, LitStr};
18use tauri_codegen::image::CachedIcon;
19
20mod command;
21mod menu;
22mod mobile;
23mod runtime;
24
25#[macro_use]
26mod context;
27
28/// Mark a function as a command handler. It creates a wrapper function with the necessary glue code.
29///
30/// # Stability
31/// The output of this macro is managed internally by Tauri,
32/// and should not be accessed directly on normal applications.
33/// It may have breaking changes in the future.
34#[proc_macro_attribute]
35pub fn command(attributes: TokenStream, item: TokenStream) -> TokenStream {
36 command::wrapper(attributes, item)
37}
38
39#[proc_macro_attribute]
40pub fn mobile_entry_point(attributes: TokenStream, item: TokenStream) -> TokenStream {
41 mobile::entry_point(attributes, item)
42}
43
44/// Accepts a list of command functions. Creates a handler that allows commands to be called from JS with invoke().
45///
46/// You can optionally annotate the commands with a inner attribute tag `#![plugin(your_plugin_name)]`
47/// for `build > removeUnusedCommands` to work for plugins not defined in a standalone crate like `tauri-plugin-fs`
48///
49/// # Examples
50///
51/// ```rust,ignore
52/// use tauri_macros::{command, generate_handler};
53/// #[command]
54/// fn command_one() {
55/// println!("command one called");
56/// }
57/// #[command]
58/// fn command_two() {
59/// println!("command two called");
60/// }
61/// fn main() {
62/// let _handler = generate_handler![command_one, command_two];
63/// }
64/// ```
65///
66/// # Stability
67///
68/// The output of this macro is managed internally by Tauri,
69/// and should not be accessed directly on normal applications.
70/// It may have breaking changes in the future.
71#[proc_macro]
72pub fn generate_handler(item: TokenStream) -> TokenStream {
73 parse_macro_input!(item as command::Handler).into()
74}
75
76/// Reads a Tauri config file and generates a `::tauri::Context` based on the content.
77///
78/// # Stability
79/// The output of this macro is managed internally by Tauri,
80/// and should not be accessed directly on normal applications.
81/// It may have breaking changes in the future.
82#[proc_macro]
83pub fn generate_context(items: TokenStream) -> TokenStream {
84 // this macro is exported from the context module
85 let path = parse_macro_input!(items as ContextItems);
86 context::generate_context(path).into()
87}
88
89/// Adds the default type for the last parameter (assumed to be runtime) for a specific feature.
90///
91/// e.g. To default the runtime generic to type `crate::Wry` when the `wry` feature is enabled, the
92/// syntax would look like `#[default_runtime(crate::Wry, wry)`. This is **always** set for the last
93/// generic, so make sure the last generic is the runtime when using this macro.
94#[doc(hidden)]
95#[proc_macro_attribute]
96pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStream {
97 let attributes = parse_macro_input!(attributes as runtime::Attributes);
98 let input = parse_macro_input!(input as runtime::Input);
99 runtime::default_runtime(attributes, input).into()
100}
101
102/// Accepts a closure-like syntax to call arbitrary code on a menu item
103/// after matching against `kind` and retrieving it from `resources_table` using `rid`.
104///
105/// You can optionally pass a 5th parameter to select which item kinds
106/// to match against, by providing a `|` separated list of item kinds
107/// ```ignore
108/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), Check | Submenu);
109/// ```
110/// You could also provide a negated list
111/// ```ignore
112/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check);
113/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | !Submenu);
114/// ```
115/// but you can't have mixed negations and positive kinds.
116/// ```ignore
117/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | Submenu);
118/// ```
119///
120/// #### Example
121///
122/// ```ignore
123/// let rid = 23;
124/// let kind = ItemKind::Check;
125/// let resources_table = app.resources_table();
126/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text))
127/// ```
128/// which will expand into:
129/// ```ignore
130/// let rid = 23;
131/// let kind = ItemKind::Check;
132/// let resources_table = app.resources_table();
133/// match kind {
134/// ItemKind::Submenu => {
135/// let i = resources_table.get::<Submenu<R>>(rid)?;
136/// i.set_text(text)
137/// }
138/// ItemKind::MenuItem => {
139/// let i = resources_table.get::<MenuItem<R>>(rid)?;
140/// i.set_text(text)
141/// }
142/// ItemKind::Predefined => {
143/// let i = resources_table.get::<PredefinedMenuItem<R>>(rid)?;
144/// i.set_text(text)
145/// }
146/// ItemKind::Check => {
147/// let i = resources_table.get::<CheckMenuItem<R>>(rid)?;
148/// i.set_text(text)
149/// }
150/// ItemKind::Icon => {
151/// let i = resources_table.get::<IconMenuItem<R>>(rid)?;
152/// i.set_text(text)
153/// }
154/// _ => unreachable!(),
155/// }
156/// ```
157#[proc_macro]
158pub fn do_menu_item(input: TokenStream) -> TokenStream {
159 let tokens = parse_macro_input!(input as menu::DoMenuItemInput);
160 menu::do_menu_item(tokens).into()
161}
162
163/// Convert a .png or .ico icon to an Image
164/// for things like `tauri::tray::TrayIconBuilder` to consume,
165/// relative paths are resolved from `CARGO_MANIFEST_DIR`, not current file
166///
167/// ### Examples
168///
169/// ```ignore
170/// const APP_ICON: Image<'_> = include_image!("./icons/32x32.png");
171///
172/// // then use it with tray
173/// TrayIconBuilder::new().icon(APP_ICON).build().unwrap();
174///
175/// // or with window
176/// WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
177/// .icon(APP_ICON)
178/// .unwrap()
179/// .build()
180/// .unwrap();
181///
182/// // or with any other functions that takes `Image` struct
183/// ```
184///
185/// Note: this stores the image in raw pixels to the final binary,
186/// so keep the icon size (width and height) small
187/// or else it's going to bloat your final executable
188#[proc_macro]
189pub fn include_image(tokens: TokenStream) -> TokenStream {
190 let path = match parse2::<LitStr>(tokens.into()) {
191 Ok(path) => path,
192 Err(err) => return err.into_compile_error().into(),
193 };
194 let path = PathBuf::from(path.value());
195 let resolved_path = if path.is_relative() {
196 if let Ok(base_dir) = std::env::var("CARGO_MANIFEST_DIR").map(PathBuf::from) {
197 base_dir.join(path)
198 } else {
199 return quote!(compile_error!("$CARGO_MANIFEST_DIR is not defined")).into();
200 }
201 } else {
202 path
203 };
204 if !resolved_path.exists() {
205 let error_string = format!(
206 "Provided Image path \"{}\" doesn't exists",
207 resolved_path.display()
208 );
209 return quote!(compile_error!(#error_string)).into();
210 }
211
212 match CachedIcon::new("e!(::tauri), &resolved_path).map_err(|error| error.to_string()) {
213 Ok(icon) => icon.into_token_stream(),
214 Err(error) => quote!(compile_error!(#error)),
215 }
216 .into()
217}