Skip to main content

cntp_icon_tool_macros/
lib.rs

1use 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        // let comma: Token![,] = input.parse()?;
18
19        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}