openharmony_ability_derive/
lib.rs

1use darling::FromMeta;
2use proc_macro::TokenStream;
3use syn::ItemFn;
4
5#[derive(FromMeta, Default, Debug)]
6struct AbilityArgs {
7    #[darling(default)]
8    webview: bool,
9    #[darling(default)]
10    protocol: Option<String>,
11}
12
13#[proc_macro_attribute]
14pub fn ability(attr: TokenStream, item: TokenStream) -> TokenStream {
15    let ast = syn::parse_macro_input!(item as ItemFn);
16    let fn_name = &ast.sig.ident;
17    let block = &ast.block;
18    let arg = &ast.sig.inputs;
19
20    let args = if attr.is_empty() {
21        AbilityArgs::default()
22    } else {
23        match darling::ast::NestedMeta::parse_meta_list(proc_macro2::TokenStream::from(attr)) {
24            Ok(list) => match AbilityArgs::from_list(&list) {
25                Ok(args) => args,
26                Err(e) => {
27                    return TokenStream::from(e.write_errors());
28                }
29            },
30            Err(e) => {
31                return TokenStream::from(e.to_compile_error());
32            }
33        }
34    };
35
36    let protocol_registrations = args
37        .protocol
38        .as_ref()
39        .map(|protocols| {
40            protocols
41                .split(",")
42                .map(|protocol| {
43                    let protocol_lit = syn::LitStr::new(protocol, proc_macro2::Span::call_site());
44                    quote::quote! {
45                        openharmony_ability::native_web::CustomProtocol::add_protocol_with_option(#protocol_lit, 
46                            openharmony_ability::native_web::CustomProtocolOption::Standard | 
47                            openharmony_ability::native_web::CustomProtocolOption::CorsEnabled | 
48                            openharmony_ability::native_web::CustomProtocolOption::CspBypassing |
49                            openharmony_ability::native_web::CustomProtocolOption::FetchEnabled |
50                            openharmony_ability::native_web::CustomProtocolOption::CodeCacheEnabled
51                        );
52                    }
53                })
54                .collect::<Vec<_>>()
55        })
56        .unwrap_or_default();
57
58    let render = if args.webview {
59        quote::quote! {
60            #[openharmony_ability::napi_derive::napi]
61            pub fn webview_render<'a>(
62                env: &'a openharmony_ability::napi::Env,
63                helper: openharmony_ability::napi::bindgen_prelude::ObjectRef,
64            ) -> openharmony_ability::napi::Result<openharmony_ability::WebViewComponentEventCallback<'a>> {
65                let callback = openharmony_ability::render(env, helper, (*APP).clone())?;
66                Ok(callback)
67            }
68        }
69    } else {
70        quote::quote! {
71            #[openharmony_ability::napi_derive::napi]
72            pub fn render<'a>(
73                env: &'a openharmony_ability::napi::Env,
74                helper: openharmony_ability::napi::bindgen_prelude::ObjectRef,
75                slot: openharmony_ability::arkui::ArkUIHandle,
76            ) -> openharmony_ability::napi::Result<()> {
77                let root = openharmony_ability::render(env, helper, slot, (*APP).clone())?;
78                ROOT_NODE.replace(Some(root));
79                Ok(())
80            }
81        }
82    };
83
84    // Register custom protocol if protocol is specified and webview is enabled
85    let protocol_registrations_apply = if args.protocol.is_some() && args.webview {
86        quote::quote! {
87            #[openharmony_ability::napi_derive::napi]
88            pub fn register_custom_protocol<'a>(
89                env: &'a openharmony_ability::napi::Env,
90            ) -> openharmony_ability::napi::Result<()> {
91                #(#protocol_registrations)*
92
93                openharmony_ability::native_web::CustomProtocol::register();
94
95                Ok(())
96            }
97        }
98    } else {
99        quote::quote! {}
100    };
101
102    let f = quote::quote! {
103        pub(crate) fn #fn_name(#arg) #block
104
105        mod openharmony_ability_mod {
106            use super::*;
107            use openharmony_ability::napi as napi_ohos;
108
109            static APP: std::sync::LazyLock<openharmony_ability::OpenHarmonyApp> =
110                std::sync::LazyLock::new(|| openharmony_ability::OpenHarmonyApp::new());
111
112            thread_local! {
113                pub static ROOT_NODE: std::cell::RefCell<Option<openharmony_ability::arkui::RootNode>> = std::cell::RefCell::new(None);
114            }
115
116            #protocol_registrations_apply
117
118            #[openharmony_ability::napi_derive::napi]
119            pub fn init<'a>(
120                env: &'a openharmony_ability::napi::Env,
121            ) -> openharmony_ability::napi::Result<openharmony_ability::ApplicationLifecycle<'a>> {
122                let lifecycle_handle = openharmony_ability::create_lifecycle_handle(env, (*APP).clone())?;
123                #fn_name((*APP).clone());
124                Ok(lifecycle_handle)
125            }
126
127            #render
128        }
129    };
130
131    f.into()
132}