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