bevy_auto_plugin_nightly_shared/
lib.rs1#![cfg_attr(feature = "nightly_proc_macro_span", feature(proc_macro_span))]
2use bevy_auto_plugin_shared::AutoPluginContext;
3use bevy_auto_plugin_shared::util::{Target, path_to_string};
4use quote::quote;
5use std::cell::RefCell;
6use std::collections::HashMap;
7use syn::Path;
8use thiserror::Error;
9
10thread_local! {
11 static FILE_STATE_MAP: RefCell<HashMap<String, FileState>> = RefCell::new(HashMap::new());
12}
13
14#[derive(Default)]
17pub struct FileState {
18 pub plugin_registered: bool,
19 pub context: AutoPluginContext,
20}
21
22pub fn get_file_path() -> String {
23 {
24 #[cfg(feature = "nightly_proc_macro_span")]
25 {
26 #[cfg(all(feature = "nightly_pre_2025_04_16", not(feature = "nightly")))]
27 {
28 use proc_macro2::Span;
29 Span::call_site()
30 .unwrap()
31 .source_file()
32 .path()
33 .display()
34 .to_string()
35 }
36
37 #[cfg(all(
38 feature = "nightly",
39 any(not(feature = "nightly_pre_2025_04_16"), feature = "_all")
40 ))]
41 {
42 use proc_macro2::Span;
43 Span::call_site()
44 .unwrap()
45 .local_file()
46 .expect("failed to resolve local file from span")
47 .display()
48 .to_string()
49 }
50 }
51 #[cfg(all(
52 feature = "nightly",
53 feature = "nightly_pre_2025_04_16",
54 not(feature = "_all")
55 ))]
56 {
57 panic!("nightly_pre_2025_04_16 feature is not compatible with nightly");
58 }
59 #[cfg(not(feature = "nightly_proc_macro_span"))]
60 {
61 panic!("proc_macro_span feature is required for this crate");
62 }
63 }
64}
65
66pub fn update_file_state<R>(file_path: String, update_fn: impl FnOnce(&mut FileState) -> R) -> R {
67 FILE_STATE_MAP.with(|map| {
68 let mut map = map.borrow_mut();
69 let file_state = map.entry(file_path).or_default();
70 update_fn(file_state)
71 })
72}
73
74pub fn update_state(
75 file_path: String,
76 path: Path,
77 target: Target,
78) -> std::result::Result<(), UpdateStateError> {
79 FILE_STATE_MAP.with(|map| {
80 let mut map = map.borrow_mut();
81 let entry = map.entry(file_path).or_default();
82 if entry.plugin_registered {
83 return Err(UpdateStateError::PluginAlreadyRegistered);
84 }
85 let path = path_to_string(&path, false);
86 let inserted = match target {
87 Target::RegisterTypes => entry.context.register_types.insert(path),
88 Target::RegisterStateTypes => entry.context.register_state_types.insert(path),
89 Target::AddEvents => entry.context.add_events.insert(path),
90 Target::InitResources => entry.context.init_resources.insert(path),
91 Target::InitStates => entry.context.init_states.insert(path),
92 Target::RequiredComponentAutoName => entry.context.auto_names.insert(path),
93 };
94 if !inserted {
95 return Err(UpdateStateError::Duplicate);
96 }
97 Ok(())
98 })
99}
100
101fn get_files_missing_plugin() -> Vec<String> {
102 FILE_STATE_MAP.with(|map| {
103 let map = map.borrow();
104 let mut files_missing_plugin = Vec::new();
105 for (file_path, file_state) in map.iter() {
106 if file_state.plugin_registered {
107 continue;
108 }
109 files_missing_plugin.push(file_path.clone());
110 }
111 files_missing_plugin
112 })
113}
114
115pub fn files_missing_plugin_ts() -> proc_macro2::TokenStream {
116 #[allow(unused_mut)]
117 let mut output = quote! {};
118 let missing_plugin_files = get_files_missing_plugin();
119 if !missing_plugin_files.is_empty() {
120 #[allow(unused_variables)]
121 let messages = missing_plugin_files
122 .into_iter()
123 .map(|file_path| format!("missing #[auto_plugin(...)] attribute in file: {file_path}"))
124 .collect::<Vec<_>>();
125 #[cfg(feature = "missing_auto_plugin_is_error")]
126 {
127 output.extend(messages.iter().map(|message| {
128 quote! {
129 log::error!(#message);
130 }
131 }));
132 }
133 #[cfg(feature = "missing_auto_plugin_is_warning")]
134 {
135 output.extend(messages.iter().map(|message| {
136 quote! {
137 log::warn!(#message);
138 }
139 }));
140 }
141 #[cfg(feature = "missing_auto_plugin_is_compile_error")]
142 return syn::Error::new(Span::call_site(), messages.join("\n")).to_compile_error();
143 }
144 output
145}
146
147#[derive(Error, Debug)]
148pub enum UpdateStateError {
149 #[error("duplicate attribute")]
150 Duplicate,
151 #[error("plugin already registered above, move plugin fn to the bottom of the file")]
152 PluginAlreadyRegistered,
153}