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) => {
25                match AbilityArgs::from_list(&list) {
26                    Ok(args) => args,
27                    Err(e) => {
28                        return TokenStream::from(e.write_errors());
29                    }
30                }
31            }
32            Err(e) => {
33                return TokenStream::from(e.to_compile_error());
34            }
35        }
36    };
37
38    let protocol_registrations = args
39        .protocol
40        .as_ref()
41        .map(|protocols| {
42            protocols
43                .split(",")
44                .map(|protocol| {
45                    let protocol_lit = syn::LitStr::new(protocol, proc_macro2::Span::call_site());
46                    quote::quote! {
47                        openharmony_ability::native_web::CustomProtocol::add_protocol(#protocol_lit);
48                    }
49                })
50                .collect::<Vec<_>>()
51        })
52        .unwrap_or_default();
53
54    let render = if args.webview {
55        quote::quote! {
56            #[openharmony_ability::napi_derive::napi]
57            pub fn webview_render<'a>(
58                env: &'a openharmony_ability::napi::Env,
59                helper: openharmony_ability::napi::bindgen_prelude::ObjectRef,
60            ) -> openharmony_ability::napi::Result<openharmony_ability::WebViewComponentEventCallback<'a>> {
61                let callback = openharmony_ability::render(env, helper, (*APP).clone())?;
62                Ok(callback)
63            }
64        }
65    } else {
66        quote::quote! {
67            #[openharmony_ability::napi_derive::napi]
68            pub fn render<'a>(
69                env: &'a openharmony_ability::napi::Env,
70                helper: openharmony_ability::napi::bindgen_prelude::ObjectRef,
71                slot: openharmony_ability::arkui::ArkUIHandle,
72            ) -> openharmony_ability::napi::Result<()> {
73                let root = openharmony_ability::render(env, helper, slot, (*APP).clone())?;
74                ROOT_NODE.replace(Some(root));
75                Ok(())
76            }
77        }
78    };
79
80    // Register custom protocol if protocol is specified and webview is enabled
81    let protocol_registrations_apply = if args.protocol.is_some() && args.webview {
82        quote::quote! {
83            #[openharmony_ability::napi_derive::napi]
84            pub fn register_custom_protocol<'a>(
85                env: &'a openharmony_ability::napi::Env,
86            ) -> openharmony_ability::napi::Result<()> {
87                #(#protocol_registrations)*
88
89                openharmony_ability::native_web::CustomProtocol::register();
90
91                Ok(())
92            }
93        }
94    } else {
95        quote::quote! {}
96    };
97
98    let f = quote::quote! {
99        pub(crate) fn #fn_name(#arg) #block
100
101        mod openharmony_ability_mod {
102            use super::*;
103            use openharmony_ability::napi as napi_ohos;
104
105            static APP: std::sync::LazyLock<openharmony_ability::OpenHarmonyApp> =
106                std::sync::LazyLock::new(|| openharmony_ability::OpenHarmonyApp::new());
107
108            thread_local! {
109                pub static ROOT_NODE: std::cell::RefCell<Option<openharmony_ability::arkui::RootNode>> = std::cell::RefCell::new(None);
110            }
111
112            #protocol_registrations_apply
113
114            #[openharmony_ability::napi_derive::napi]
115            pub fn init<'a>(
116                env: &'a openharmony_ability::napi::Env,
117            ) -> openharmony_ability::napi::Result<openharmony_ability::ApplicationLifecycle<'a>> {
118                let lifecycle_handle = openharmony_ability::create_lifecycle_handle(env, (*APP).clone())?;
119                #fn_name((*APP).clone());
120                Ok(lifecycle_handle)
121            }
122
123            #render
124        }
125    };
126
127    f.into()
128}