adabraka_gpui_macros/
gpui_macros.rs1#![recursion_limit = "512"]
2
3mod derive_action;
4mod derive_app_context;
5mod derive_into_element;
6mod derive_render;
7mod derive_visual_context;
8mod register_action;
9mod styles;
10mod test;
11
12#[cfg(any(feature = "inspector", debug_assertions))]
13mod derive_inspector_reflection;
14
15use proc_macro::TokenStream;
16use proc_macro_crate::{FoundCrate, crate_name};
17use quote::quote;
18use std::{env, fs, path::PathBuf, sync::OnceLock};
19use syn::{DeriveInput, Ident};
20use toml_edit::DocumentMut;
21
22#[proc_macro_derive(Action, attributes(action))]
24pub fn derive_action(input: TokenStream) -> TokenStream {
25 derive_action::derive_action(input)
26}
27
28#[proc_macro]
32pub fn register_action(ident: TokenStream) -> TokenStream {
33 register_action::register_action(ident)
34}
35
36#[proc_macro_derive(IntoElement)]
39pub fn derive_into_element(input: TokenStream) -> TokenStream {
40 derive_into_element::derive_into_element(input)
41}
42
43#[proc_macro_derive(Render)]
44#[doc(hidden)]
45pub fn derive_render(input: TokenStream) -> TokenStream {
46 derive_render::derive_render(input)
47}
48
49#[proc_macro_derive(AppContext, attributes(app))]
63pub fn derive_app_context(input: TokenStream) -> TokenStream {
64 derive_app_context::derive_app_context(input)
65}
66
67#[proc_macro_derive(VisualContext, attributes(window, app))]
96pub fn derive_visual_context(input: TokenStream) -> TokenStream {
97 derive_visual_context::derive_visual_context(input)
98}
99
100#[proc_macro]
102#[doc(hidden)]
103pub fn style_helpers(input: TokenStream) -> TokenStream {
104 styles::style_helpers(input)
105}
106
107#[doc(hidden)]
109#[proc_macro]
110pub fn visibility_style_methods(input: TokenStream) -> TokenStream {
111 styles::visibility_style_methods(input)
112}
113
114#[doc(hidden)]
116#[proc_macro]
117pub fn margin_style_methods(input: TokenStream) -> TokenStream {
118 styles::margin_style_methods(input)
119}
120
121#[doc(hidden)]
123#[proc_macro]
124pub fn padding_style_methods(input: TokenStream) -> TokenStream {
125 styles::padding_style_methods(input)
126}
127
128#[doc(hidden)]
130#[proc_macro]
131pub fn position_style_methods(input: TokenStream) -> TokenStream {
132 styles::position_style_methods(input)
133}
134
135#[doc(hidden)]
137#[proc_macro]
138pub fn overflow_style_methods(input: TokenStream) -> TokenStream {
139 styles::overflow_style_methods(input)
140}
141
142#[doc(hidden)]
144#[proc_macro]
145pub fn cursor_style_methods(input: TokenStream) -> TokenStream {
146 styles::cursor_style_methods(input)
147}
148
149#[doc(hidden)]
151#[proc_macro]
152pub fn border_style_methods(input: TokenStream) -> TokenStream {
153 styles::border_style_methods(input)
154}
155
156#[doc(hidden)]
158#[proc_macro]
159pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
160 styles::box_shadow_style_methods(input)
161}
162
163#[proc_macro_attribute]
201pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
202 test::test(args, function)
203}
204
205#[cfg(any(feature = "inspector", debug_assertions))]
222#[proc_macro_attribute]
223pub fn derive_inspector_reflection(_args: TokenStream, input: TokenStream) -> TokenStream {
224 derive_inspector_reflection::derive_inspector_reflection(_args, input)
225}
226
227pub(crate) fn gpui_crate_path() -> proc_macro2::TokenStream {
228 if let Some(path) = configured_gpui_crate_path() {
229 return path;
230 }
231
232 let facade = crate_name("fc-gpui").ok();
233 let core = crate_name("fc-gpui-core").ok();
234
235 select_gpui_crate_path(facade, core)
236}
237
238fn select_gpui_crate_path(
239 facade: Option<FoundCrate>,
240 core: Option<FoundCrate>,
241) -> proc_macro2::TokenStream {
242 match (facade, core) {
243 (Some(_), Some(_)) => panic!(
244 "both fc-gpui and fc-gpui-core are direct dependencies; \
245 set [package.metadata.gpui-macros] crate = \"<dependency identifier>\" \
246 in Cargo.toml to select the crate used by GPUI macros"
247 ),
248 (Some(found), None) => crate_path(found, "fc_gpui", "gpui"),
249 (None, Some(found)) => crate_path(found, "fc_gpui_core", "gpui_core"),
250 (None, None) => quote!(::gpui),
251 }
252}
253
254fn crate_path(
255 found: FoundCrate,
256 package_identifier: &str,
257 library_identifier: &str,
258) -> proc_macro2::TokenStream {
259 match found {
260 FoundCrate::Itself => quote!(::gpui),
261 FoundCrate::Name(name) => {
262 let name = if name == package_identifier {
264 library_identifier
265 } else {
266 &name
267 };
268 let ident = Ident::new(&name.replace('-', "_"), proc_macro2::Span::call_site());
269 quote!(::#ident)
270 }
271 }
272}
273
274fn configured_gpui_crate_path() -> Option<proc_macro2::TokenStream> {
275 static CONFIGURED_CRATE: OnceLock<Option<String>> = OnceLock::new();
276
277 let crate_name = CONFIGURED_CRATE.get_or_init(|| {
278 let manifest_path = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR")?).join("Cargo.toml");
279 let manifest = fs::read_to_string(manifest_path).ok()?;
280 let document = manifest.parse::<DocumentMut>().ok()?;
281 document
282 .get("package")?
283 .get("metadata")?
284 .get("gpui-macros")?
285 .get("crate")?
286 .as_str()
287 .map(str::to_owned)
288 });
289 let ident = syn::parse_str::<Ident>(crate_name.as_deref()?).unwrap_or_else(|_| {
290 panic!(
291 "package.metadata.gpui-macros.crate must be a Rust crate identifier, got {crate_name:?}"
292 )
293 });
294
295 Some(quote!(::#ident))
296}
297
298pub(crate) fn get_simple_attribute_field(ast: &DeriveInput, name: &'static str) -> Option<Ident> {
299 match &ast.data {
300 syn::Data::Struct(data_struct) => data_struct
301 .fields
302 .iter()
303 .find(|field| field.attrs.iter().any(|attr| attr.path().is_ident(name)))
304 .map(|field| field.ident.clone().unwrap()),
305 syn::Data::Enum(_) => None,
306 syn::Data::Union(_) => None,
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::{FoundCrate, select_gpui_crate_path};
313
314 #[test]
315 #[should_panic(expected = "both fc-gpui and fc-gpui-core are direct dependencies")]
316 fn ambiguous_direct_dependencies_require_an_explicit_crate() {
317 select_gpui_crate_path(
318 Some(FoundCrate::Name("facade_ui".into())),
319 Some(FoundCrate::Name("core_ui".into())),
320 );
321 }
322}