tauri_macros/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

//! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
//!
//! Create macros for `tauri::Context`, invoke handler and commands leveraging the `tauri-codegen` crate.

#![doc(
  html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
  html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
)]

use std::path::PathBuf;

use crate::context::ContextItems;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse2, parse_macro_input, LitStr};
use tauri_codegen::image::CachedIcon;

mod command;
mod menu;
mod mobile;
mod runtime;

#[macro_use]
mod context;

/// Mark a function as a command handler. It creates a wrapper function with the necessary glue code.
///
/// # Stability
/// The output of this macro is managed internally by Tauri,
/// and should not be accessed directly on normal applications.
/// It may have breaking changes in the future.
#[proc_macro_attribute]
pub fn command(attributes: TokenStream, item: TokenStream) -> TokenStream {
  command::wrapper(attributes, item)
}

#[proc_macro_attribute]
pub fn mobile_entry_point(attributes: TokenStream, item: TokenStream) -> TokenStream {
  mobile::entry_point(attributes, item)
}

/// Accepts a list of command functions. Creates a handler that allows commands to be called from JS with invoke().
///
/// # Examples
/// ```rust,ignore
/// use tauri_macros::{command, generate_handler};
/// #[command]
/// fn command_one() {
///   println!("command one called");
/// }
/// #[command]
/// fn command_two() {
///   println!("command two called");
/// }
/// fn main() {
///   let _handler = generate_handler![command_one, command_two];
/// }
/// ```
/// # Stability
/// The output of this macro is managed internally by Tauri,
/// and should not be accessed directly on normal applications.
/// It may have breaking changes in the future.
#[proc_macro]
pub fn generate_handler(item: TokenStream) -> TokenStream {
  parse_macro_input!(item as command::Handler).into()
}

/// Reads a Tauri config file and generates a `::tauri::Context` based on the content.
///
/// # Stability
/// The output of this macro is managed internally by Tauri,
/// and should not be accessed directly on normal applications.
/// It may have breaking changes in the future.
#[proc_macro]
pub fn generate_context(items: TokenStream) -> TokenStream {
  // this macro is exported from the context module
  let path = parse_macro_input!(items as ContextItems);
  context::generate_context(path).into()
}

/// Adds the default type for the last parameter (assumed to be runtime) for a specific feature.
///
/// e.g. To default the runtime generic to type `crate::Wry` when the `wry` feature is enabled, the
/// syntax would look like `#[default_runtime(crate::Wry, wry)`. This is **always** set for the last
/// generic, so make sure the last generic is the runtime when using this macro.
#[doc(hidden)]
#[proc_macro_attribute]
pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStream {
  let attributes = parse_macro_input!(attributes as runtime::Attributes);
  let input = parse_macro_input!(input as runtime::Input);
  runtime::default_runtime(attributes, input).into()
}

/// Accepts a closure-like syntax to call arbitrary code on a menu item
/// after matching against `kind` and retrieving it from `resources_table` using `rid`.
///
/// You can optionally pass a 5th parameter to select which item kinds
/// to match against, by providing a `|` separated list of item kinds
/// ```ignore
/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), Check | Submenu);
/// ```
/// You could also provide a negated list
/// ```ignore
/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check);
/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | !Submenu);
/// ```
/// but you can't have mixed negations and positive kinds.
/// ```ignore
/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | Submenu);
/// ```
///
/// #### Example
///
/// ```ignore
///  let rid = 23;
///  let kind = ItemKind::Check;
///  let resources_table = app.resources_table();
///  do_menu_item!(resources_table, rid, kind, |i| i.set_text(text))
/// ```
/// which will expand into:
/// ```ignore
///  let rid = 23;
///  let kind = ItemKind::Check;
///  let resources_table = app.resources_table();
///  match kind {
///  ItemKind::Submenu => {
///    let i = resources_table.get::<Submenu<R>>(rid)?;
///    i.set_text(text)
///  }
///  ItemKind::MenuItem => {
///    let i = resources_table.get::<MenuItem<R>>(rid)?;
///    i.set_text(text)
///  }
///  ItemKind::Predefined => {
///    let i = resources_table.get::<PredefinedMenuItem<R>>(rid)?;
///    i.set_text(text)
///  }
///  ItemKind::Check => {
///    let i = resources_table.get::<CheckMenuItem<R>>(rid)?;
///    i.set_text(text)
///  }
///  ItemKind::Icon => {
///    let i = resources_table.get::<IconMenuItem<R>>(rid)?;
///    i.set_text(text)
///  }
///  _ => unreachable!(),
///  }
/// ```
#[proc_macro]
pub fn do_menu_item(input: TokenStream) -> TokenStream {
  let tokens = parse_macro_input!(input as menu::DoMenuItemInput);
  menu::do_menu_item(tokens).into()
}

/// Convert a .png or .ico icon to an Image
/// for things like `tauri::tray::TrayIconBuilder` to consume,
/// relative paths are resolved from `CARGO_MANIFEST_DIR`, not current file
///
/// ### Examples
///
/// ```ignore
/// const APP_ICON: Image<'_> = include_image!("./icons/32x32.png");
///
/// // then use it with tray
/// TrayIconBuilder::new().icon(APP_ICON).build().unwrap();
///
/// // or with window
/// WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
///     .icon(APP_ICON)
///     .unwrap()
///     .build()
///     .unwrap();
///
/// // or with any other functions that takes `Image` struct
/// ```
///
/// Note: this stores the image in raw pixels to the final binary,
/// so keep the icon size (width and height) small
/// or else it's going to bloat your final executable
#[proc_macro]
pub fn include_image(tokens: TokenStream) -> TokenStream {
  let path = match parse2::<LitStr>(tokens.into()) {
    Ok(path) => path,
    Err(err) => return err.into_compile_error().into(),
  };
  let path = PathBuf::from(path.value());
  let resolved_path = if path.is_relative() {
    if let Ok(base_dir) = std::env::var("CARGO_MANIFEST_DIR").map(PathBuf::from) {
      base_dir.join(path)
    } else {
      return quote!(compile_error!("$CARGO_MANIFEST_DIR is not defined")).into();
    }
  } else {
    path
  };
  if !resolved_path.exists() {
    let error_string = format!(
      "Provided Image path \"{}\" doesn't exists",
      resolved_path.display()
    );
    return quote!(compile_error!(#error_string)).into();
  }

  match CachedIcon::new(&quote!(::tauri), &resolved_path).map_err(|error| error.to_string()) {
    Ok(icon) => icon.into_token_stream(),
    Err(error) => quote!(compile_error!(#error)),
  }
  .into()
}