cntp_icon_tool_macros/
lib.rs1use cntp_config::ContemporaryConfig;
2use cntp_icon_tool_core::contemporary_icon::ContemporaryIcon;
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::parse::{Parse, ParseStream};
6use syn::{Error, LitStr, parse_macro_input};
7
8static APPLICATION_ICON_ASSET_PATH: &str = "contemporary-icon:/application";
9
10struct ApplicationIconMacroInput {
11 pub icon_file: LitStr,
12}
13
14impl Parse for ApplicationIconMacroInput {
15 fn parse(input: ParseStream) -> syn::Result<Self> {
16 let icon_file: LitStr = input.parse()?;
17 Ok(ApplicationIconMacroInput { icon_file })
20 }
21}
22
23#[proc_macro]
24pub fn application_icon(body: TokenStream) -> TokenStream {
25 let input = parse_macro_input!(body as ApplicationIconMacroInput);
26
27 let Some(config) = ContemporaryConfig::new_from_build_env() else {
28 return Error::new(
29 proc_macro::Span::call_site().into(),
30 "Unable to read Contemporary.toml from build environment.",
31 )
32 .to_compile_error()
33 .into();
34 };
35
36 if config.application.theme_colors.len() != 2 {
37 return Error::new(
38 proc_macro::Span::call_site().into(),
39 "theme_colors must contain exactly 2 elements.",
40 )
41 .to_compile_error()
42 .into();
43 }
44
45 let Some(current_file) = proc_macro::Span::call_site().local_file() else {
46 return Error::new(
47 proc_macro::Span::call_site().into(),
48 "Macro used in non-file context",
49 )
50 .to_compile_error()
51 .into();
52 };
53
54 let icon_path = current_file.parent().unwrap().join(input.icon_file.value());
55 if !icon_path.exists() {
56 return Error::new(
57 input.icon_file.span(),
58 format!("Unable to find icon file: {}", icon_path.to_str().unwrap()),
59 )
60 .to_compile_error()
61 .into();
62 }
63
64 let icon = ContemporaryIcon::new(icon_path, false, config.is_blueprint());
65 let icon_source = icon.generate(
66 &config.application.theme_colors[0],
67 &config.application.theme_colors[1],
68 );
69 quote! {
70 {
71 use contemporary::assets::global_manager::{ASSET_MANAGER, ManagerSource};
72 use gpui::SharedString;
73 use std::borrow::Cow;
74 use contemporary::icon_tool::Url;
75
76 pub struct ApplicationIconSource;
77
78 impl ManagerSource for ApplicationIconSource {
79 fn scheme(&self) -> &'static str {
80 "contemporary-icon"
81 }
82
83 #[allow(unused_variables)]
84 #[allow(unreachable_code)]
85 fn load(&self, url: &Url) -> gpui::Result<Option<Cow<'static, [u8]>>> {
86 if url.scheme() != "contemporary-icon" {
87 return Ok(None);
88 }
89
90 if url.path() != "/application" {
91 return Ok(None);
92 }
93
94 return Ok(Some(Cow::Owned(#icon_source.to_string().into_bytes())));
95 }
96
97 fn list(&self, path: &str) -> gpui::Result<Vec<SharedString>> {
98 Ok(Vec::new())
99 }
100 }
101
102 ASSET_MANAGER
103 .read()
104 .unwrap()
105 .add_source(Box::new(ApplicationIconSource));
106 }
107 }
108 .into()
109}
110
111#[proc_macro]
112pub fn application_icon_asset_path(_body: TokenStream) -> TokenStream {
113 quote! {
114 #APPLICATION_ICON_ASSET_PATH
115 }
116 .into()
117}
118
119#[proc_macro]
120pub fn application_icon_source(_body: TokenStream) -> TokenStream {
121 quote! {
122 {
123 use gpui::{ImageSource, Resource, SharedString};
124
125 ImageSource::Resource(Resource::Embedded(
126 SharedString::new_static(#APPLICATION_ICON_ASSET_PATH)
127 ))
128 }
129 }
130 .into()
131}