Skip to main content

cidre_macros/
lib.rs

1/// This is all dirty hacks. We need to reimplement it with syn and quote
2///
3use std::{borrow::Cow, str::FromStr};
4
5use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
6
7mod swift;
8mod swift_mangle;
9
10enum Attr {
11    Optional,
12    MsgSend(String),
13    ApiAvailable(Versions),
14    DocAvailable,
15}
16
17impl Attr {
18    fn from_stream(stream: TokenStream) -> Option<Attr> {
19        let mut iter = stream.into_iter();
20        let Some(TokenTree::Ident(ident)) = iter.next() else {
21            return None;
22        };
23
24        let str = ident.to_string();
25        if str == "doc" {
26            let Some(TokenTree::Punct(p)) = iter.next() else {
27                return None;
28            };
29            assert_eq!(p, '=');
30            let Some(TokenTree::Literal(s)) = iter.next() else {
31                return None;
32            };
33            if s.to_string() == "\" # Availability\"" {
34                return Some(Attr::DocAvailable);
35            }
36        } else if str != "objc" && str != "api" {
37            return None;
38        }
39
40        let Some(TokenTree::Punct(p)) = iter.next() else {
41            return None;
42        };
43
44        assert_eq!(p, ':');
45
46        let Some(TokenTree::Punct(p)) = iter.next() else {
47            return None;
48        };
49
50        assert_eq!(p, ':');
51
52        if let Some(tt) = iter.next() {
53            match tt {
54                TokenTree::Group(v) => panic!("didn't expect group {v}"),
55                TokenTree::Ident(v) => {
56                    let v = v.to_string();
57                    return match v.as_str() {
58                        "optional" => Some(Attr::Optional),
59                        "msg_send" => {
60                            let Some(TokenTree::Group(a)) = iter.next() else {
61                                return None;
62                            };
63                            let sel = a.stream().to_string().replace([' ', '\n'], "");
64                            Some(Attr::MsgSend(sel))
65                        }
66                        "available" => {
67                            let Some(TokenTree::Group(a)) = iter.next() else {
68                                return None;
69                            };
70                            Some(Attr::ApiAvailable(Versions::from_stream(a.stream())))
71                        }
72                        _ => None,
73                    };
74                }
75                TokenTree::Punct(v) => panic!("didn't expect punct {v}"),
76                TokenTree::Literal(v) => panic!("didn't expect literal {v}"),
77            }
78        }
79
80        panic!("Unexpected attribute")
81    }
82}
83
84/// Calls a Swift entry point named by its declaration, with the registers its
85/// convention asks for derived from the Rust signature.
86#[proc_macro_attribute]
87pub fn swift_call(decl: TokenStream, func: TokenStream) -> TokenStream {
88    swift::gen_swift_call(decl, func)
89}
90
91/// The address of the Swift entry point a declaration names.
92#[proc_macro]
93pub fn swift_symbol(args: TokenStream) -> TokenStream {
94    swift::gen_symbol(args)
95}
96
97/// The address of a Swift type's metadata accessor, from the type's name.
98#[proc_macro]
99pub fn swift_metadata_accessor(args: TokenStream) -> TokenStream {
100    swift::gen_metadata_accessor(args)
101}
102
103/// Should generate static fn sel_xxx function that gets selector.
104/// So user can check selector with is_reponds_to_sel
105#[proc_macro_attribute]
106pub fn optional(_sel: TokenStream, func: TokenStream) -> TokenStream {
107    let mut iter = func.clone().into_iter();
108
109    let extern_name = loop {
110        let Some(TokenTree::Punct(p)) = iter.next() else {
111            panic!("expect #[objc::msg_send(...)]")
112        };
113        if p != '#' {
114            panic!("expect #[objc::msg_send(...)]")
115        }
116        let Some(TokenTree::Group(g)) = iter.next() else {
117            panic!("expect #[objc::msg_send(...)]")
118        };
119        if let Some(Attr::MsgSend(extern_name)) = Attr::from_stream(g.stream()) {
120            break extern_name;
121        }
122    };
123
124    let mut fn_name = None;
125
126    while let Some(tt) = iter.next() {
127        match tt {
128            TokenTree::Ident(i) if i.to_string().eq("fn") => {
129                let Some(TokenTree::Ident(name)) = iter.next() else {
130                    panic!("expect function name");
131                };
132                fn_name = Some(name.to_string());
133            }
134            _ => {} // panic?
135        }
136    }
137
138    let Some(fn_name) = fn_name else {
139        panic!("function name not found");
140    };
141
142    let getter: TokenStream = format!(
143        "
144    /// `@selector({extern_name})` but dynamic
145    /// use this function to check if object responds to selector
146    fn sel_{fn_name}() -> &'static objc::Sel {{
147        unsafe {{ objc::sel_reg_name(c\"{extern_name}\".as_ptr()) }}
148    }}
149        "
150    )
151    .parse()
152    .unwrap();
153
154    let mut func = func;
155    func.extend(getter);
156    func
157}
158
159#[proc_macro_attribute]
160pub fn protocol(args: TokenStream, ts: TokenStream) -> TokenStream {
161    let mut original_trait = ts.clone();
162    let error_msg = "objc::protocol expects protocol name as first argument";
163    let mut args = args.into_iter();
164    let Some(TokenTree::Ident(ident)) = args.next() else {
165        panic!("{}", error_msg);
166    };
167    assert!(args.next().is_none(), "{}", error_msg);
168    let protocol_name = ident.to_string();
169    let mut trait_name = String::new();
170
171    let mut pre_tokens = Vec::<TokenTree>::with_capacity(10);
172    let mut ts = ts.into_iter();
173    let mut group_stream = None;
174    while let Some(tt) = ts.next() {
175        match tt {
176            TokenTree::Group(ref g) if g.delimiter() == Delimiter::Brace => {
177                while let Some(tt) = pre_tokens.pop() {
178                    let val = tt.to_string();
179                    if val == "trait" {
180                        pre_tokens.push(tt);
181                        break;
182                    }
183                    trait_name = val;
184                }
185                group_stream = Some(g.stream());
186                break;
187            }
188            _ => pre_tokens.push(tt),
189        }
190    }
191    if trait_name != protocol_name {
192        let alias = format!("#[doc(alias = \"{protocol_name}\")]");
193        let stream = TokenStream::from_str(&alias).unwrap();
194        let mut alias_tokens = Vec::from_iter(stream.into_iter());
195        alias_tokens.append(&mut pre_tokens);
196        pre_tokens = alias_tokens;
197    }
198    let mut is_optional = false;
199    let mut skip = false;
200    let mut sel = String::new();
201    let mut fn_name; // = "".to_string();
202    let mut generics = Vec::new();
203    //let mut fn_args = Vec::new();
204    let mut fn_args_str; // = "".to_string();
205    let mut result = Vec::new();
206    let mut fn_body = Cow::Borrowed("");
207
208    let mut impl_trait_functions = vec![];
209    let mut has_optionals = false;
210    let mut fn_names = vec![];
211
212    let mut iter = group_stream.expect("should be group").into_iter();
213    while let Some(token) = iter.next() {
214        match token {
215            TokenTree::Group(g) => println!("group {g}"),
216            TokenTree::Ident(i) => {
217                let str = i.to_string();
218                if str == "fn" {
219                    let Some(TokenTree::Ident(name)) = iter.next() else {
220                        panic!("expect fn name");
221                    };
222                    fn_name = name.to_string();
223                    let args = loop {
224                        let Some(tt) = iter.next() else {
225                            panic!("need more tokens");
226                        };
227                        match tt {
228                            TokenTree::Group(args) => break args,
229                            _ => generics.push(tt),
230                        }
231                    };
232                    fn_args_str = args.to_string();
233                    for tt in iter.by_ref() {
234                        match tt {
235                            TokenTree::Punct(ref p) if p.as_char() == ';' => {
236                                result.push(tt);
237                                break;
238                            }
239                            TokenTree::Group(ref g) if g.delimiter() == Delimiter::Brace => {
240                                fn_body = Cow::Owned(g.to_string());
241                                break;
242                            }
243                            _ => result.push(tt),
244                        }
245                    }
246
247                    let mut ext = "";
248
249                    let register_sel = if sel.is_empty() {
250                        Cow::Borrowed("None")
251                    } else {
252                        ext = "extern \"C\" ";
253                        fn_args_str = fn_args_str.replacen("(& self", "(&self", 1);
254                        fn_args_str =
255                            fn_args_str.replacen("(&self", "(&self, _cmd: Option<&objc::Sel>", 1);
256
257                        fn_args_str = fn_args_str.replacen("(& mut self", "(&mut self", 1);
258                        fn_args_str = fn_args_str.replacen(
259                            "(&mut self",
260                            "(&mut self, _cmd: Option<&objc::Sel>",
261                            1,
262                        );
263                        Cow::Owned(format!(
264                            "unsafe {{ objc::sel_reg_name(c\"{sel}\".as_ptr()) }}"
265                        ))
266                    };
267
268                    if is_optional && !sel.is_empty() && fn_body.is_empty() {
269                        result.pop(); // remove ';'
270                        fn_body = Cow::Borrowed("{ unimplemented!() }");
271                    }
272
273                    if !is_optional && sel.is_empty() {
274                        skip = true;
275                    }
276
277                    let ret = if result.is_empty() {
278                        Cow::Borrowed("")
279                    } else {
280                        Cow::Owned(TokenStream::from_iter(result.clone().into_iter()).to_string())
281                    };
282
283                    let gen = if generics.is_empty() {
284                        Cow::Borrowed("")
285                    } else {
286                        Cow::Owned(TokenStream::from_iter(generics.clone().into_iter()).to_string())
287                    };
288
289                    let gen_rar_version = ret.contains("arc :: R <") && !returns_retained(&sel);
290
291                    let impl_fn = if skip {
292                        format!(
293                            "
294    {ext}fn {fn_name}{gen}{fn_args_str}{ret} {fn_body}
295
296                                    "
297                        )
298                    } else {
299                        fn_names.push((fn_name.clone(), gen_rar_version));
300                        if gen_rar_version {
301                            let ret = ret.replacen("arc :: R <", "arc :: Rar <", 1);
302                            format!(
303                                "
304        {ext}fn impl_{fn_name}_ar{gen}{fn_args_str}{ret} {fn_body}
305
306                                        "
307                            )
308                        } else {
309                            format!(
310                                "
311    {ext}fn impl_{fn_name}{gen}{fn_args_str}{ret} {fn_body}
312
313                                    "
314                            )
315                        }
316                    };
317
318                    if !skip {
319                        impl_trait_functions.push(impl_fn);
320                    }
321
322                    if !is_optional && !skip {
323                        let impl_sel = format!(
324                            "
325    fn sel_{fn_name}() -> &'static objc::Sel {{ {register_sel} }}
326        "
327                        );
328                        impl_trait_functions.push(impl_sel);
329                    }
330
331                    is_optional = false;
332                    sel.clear();
333                    fn_name.clear();
334                    fn_body = Cow::Borrowed("");
335                    fn_args_str.clear();
336                    generics.clear();
337                    skip = false;
338                    //fn_args.clear();
339                    result.clear();
340                }
341            }
342            TokenTree::Punct(p) => match p.as_char() {
343                '#' => {
344                    let TokenTree::Group(g) = iter.next().unwrap() else {
345                        panic!("not a group");
346                    };
347                    match Attr::from_stream(g.stream()) {
348                        Some(Attr::Optional) => {
349                            has_optionals = true;
350                            is_optional = true;
351                        }
352                        Some(Attr::MsgSend(s)) => sel = s,
353                        Some(_) => continue,
354                        None => continue,
355                    }
356                }
357                _ => panic!("other char '{p}'"),
358            },
359            TokenTree::Literal(l) => println!("lit {l}"),
360        }
361    }
362    let pre = TokenStream::from_iter(pre_tokens).to_string();
363    let obj_trait_name = format!("{trait_name}Impl");
364    let fns = impl_trait_functions.join("\n");
365
366    let add_methods = if has_optionals {
367        Cow::Borrowed("fn cls_add_methods<O: objc::Obj>(cls: &objc::Class<O>);")
368    } else {
369        Cow::Owned(add_methods_fn(&fn_names))
370    };
371
372    let add_protocol = format!(
373        "
374    fn cls_add_protocol<O: objc::Obj>(cls: &objc::Class<O>) {{
375        unsafe {{
376            let cls: &objc::Class<objc::Id> = std::mem::transmute(cls);
377            if let Some(proto) =  objc::objc_getProtocol(c\"{protocol_name}\".as_ptr())  {{
378                cls.add_protocol(proto);
379            }}
380        }}
381    }}
382        "
383    );
384
385    let code = format!(
386        "
387
388{pre} {obj_trait_name}: {trait_name} {{
389    {fns}
390    {add_methods}
391    {add_protocol}
392}}
393        "
394    );
395
396    let ts: TokenStream = code.parse().unwrap();
397
398    original_trait.extend(ts);
399    original_trait
400}
401
402fn add_methods_fn(fns: &[(String, bool)]) -> String {
403    let mut res = "
404    fn cls_add_methods<O: objc::Obj>(cls: &objc::Class<O>) {
405        let cls: &objc::Class<objc::Id> = unsafe { std::mem::transmute(cls) };
406        "
407    .to_string();
408    for (f, ar) in fns {
409        let suffix = if *ar { "_ar" } else { "" };
410        let add = format!(
411            "
412        let sel = Self::sel_{f}();
413        unsafe {{
414            let imp: extern \"C\" fn() = std::mem::transmute(Self::impl_{f}{suffix} as *const u8);
415            objc::class_addMethod(cls, sel, imp, std::ptr::null());
416        }}
417            ",
418        );
419        res.push_str(&add);
420    }
421    res.push_str("\n}");
422    res
423}
424#[proc_macro_attribute]
425pub fn add_methods(_args: TokenStream, tr_impl: TokenStream) -> TokenStream {
426    let mut tokens = vec![];
427
428    let iter = tr_impl.into_iter();
429    let mut fns = vec![];
430
431    for tt in iter {
432        match tt {
433            TokenTree::Group(g) => {
434                let mut body = g.stream().into_iter();
435                while let Some(tt) = body.next() {
436                    match tt {
437                        TokenTree::Ident(i) if i.to_string().eq("fn") => {
438                            let Some(TokenTree::Ident(f)) = body.next() else {
439                                panic!("expected function name");
440                            };
441                            let f = f.to_string().replacen("impl_", "", 1);
442                            if let Some(f) = f.strip_suffix("_ar") {
443                                fns.push((f.to_string(), true));
444                            } else {
445                                fns.push((f, false));
446                            }
447                        }
448                        _ => continue,
449                    }
450                }
451                let imp: TokenStream = add_methods_fn(&fns).parse().unwrap();
452                let mut stream = g.stream();
453                stream.extend(imp);
454                let g = Group::new(g.delimiter(), stream);
455                tokens.push(TokenTree::Group(g));
456            }
457            _ => tokens.push(tt),
458        }
459    }
460
461    // println!("fns {fns:?}");
462
463    TokenStream::from_iter(tokens)
464}
465#[proc_macro_attribute]
466pub fn msg_send_debug(sel: TokenStream, func: TokenStream) -> TokenStream {
467    let x86_64 = false;
468    gen_msg_send(sel, func, x86_64, true)
469}
470
471#[proc_macro_attribute]
472pub fn msg_send(sel: TokenStream, func: TokenStream) -> TokenStream {
473    let x86_64 = false;
474    gen_msg_send(sel, func, x86_64, false)
475}
476
477#[proc_macro_attribute]
478pub fn msg_send_x86_64(sel: TokenStream, func: TokenStream) -> TokenStream {
479    let x86_64 = true;
480    gen_msg_send(sel, func, x86_64, false)
481}
482
483fn gen_msg_send(sel: TokenStream, func: TokenStream, x86_64: bool, debug: bool) -> TokenStream {
484    let sel = sel.to_string().replace([' ', '\n'], "");
485    let sel_args_count = sel.matches(':').count();
486
487    let mut iter = func.into_iter();
488    let mut meta: Vec<TokenTree> = Vec::new();
489    let mut unsafe_already = false;
490    let mut optional_already = false;
491    let mut versions = Versions::default();
492
493    while let Some(tt) = iter.next() {
494        match tt {
495            TokenTree::Group(ref g) => {
496                if g.delimiter() == Delimiter::Bracket {
497                    match Attr::from_stream(g.stream()) {
498                        Some(Attr::Optional) => optional_already = true,
499                        Some(Attr::ApiAvailable(v)) => {
500                            versions = v;
501                            meta.pop();
502                            continue;
503                        }
504                        Some(Attr::DocAvailable) => {
505                            iter.next(); // Punct('#')
506                            let Some(TokenTree::Group(g)) = iter.next() else {
507                                panic!("Expect doc with versions");
508                            };
509                            let mut doc_iter = g.stream().into_iter();
510                            doc_iter.next(); // Ident("doc")
511                            doc_iter.next(); // Punct('=')
512                            let Some(TokenTree::Literal(s)) = doc_iter.next() else {
513                                panic!("Expect doc with versions");
514                            };
515                            let str = s.to_string();
516                            versions = Versions::from_doc_str(&str[1..str.len() - 1]);
517                            meta.push(tt.clone());
518                            meta.push(TokenTree::Punct(Punct::new(
519                                '#',
520                                proc_macro::Spacing::Joint,
521                            )));
522                            meta.push(TokenTree::Group(g));
523                            continue;
524                        }
525                        Some(Attr::MsgSend(_)) => panic!("only one msg_send is allowed"),
526                        None => {}
527                    }
528                }
529            }
530            TokenTree::Ident(ref i) => {
531                let i = i.to_string();
532                match i.as_str() {
533                    "fn" => break,
534                    "unsafe" => unsafe_already = true,
535                    _ => {}
536                }
537            }
538            TokenTree::Punct(_) => {}
539            TokenTree::Literal(_) => {}
540        }
541        meta.push(tt);
542    }
543
544    let Some(TokenTree::Ident(fn_name)) = iter.next() else {
545        panic!("expected function name");
546    };
547
548    let fn_name = fn_name.to_string();
549    let mut generics = Vec::new();
550    let doc_alias = if fn_name != sel {
551        format!("#[doc(alias = \"{sel}\")]")
552    } else {
553        String::new()
554    };
555
556    let args = loop {
557        let Some(tt) = iter.next() else {
558            panic!("need more tokens");
559        };
560        match tt {
561            TokenTree::Group(args) => break args,
562            _ => generics.push(tt),
563        }
564    };
565
566    let gen = TokenStream::from_iter(generics).to_string();
567
568    let mut ret = TokenStream::from_iter(iter).to_string();
569    assert_eq!(ret.pop().expect(";"), ';');
570    let ret_full = ret.to_string();
571    if let Some((a, _)) = ret.split_once("where") {
572        ret = a.to_string();
573    }
574    let option = ret_full.contains("-> Option <");
575    if debug {
576        println!("{option}: {ret_full}");
577    }
578    let gen_rar_version = ret.contains("arc :: R <") && !returns_retained(&sel);
579
580    if debug {
581        println!("option: {option}, gen_rar_version {gen_rar_version} ret: {ret}");
582    }
583
584    let msg_send_fn = if x86_64 {
585        choose_msg_send_fn(&ret_full)
586    } else {
587        ""
588    };
589
590    let fn_args = args.to_string();
591
592    let (class, vars) = fn_args_from_stream(args.stream());
593    let fn_args_count = vars.len();
594
595    assert_eq!(
596        sel_args_count, fn_args_count,
597        "selector and function args don't match {vars:?}"
598    );
599
600    let (mut fn_args, mut call_args) = if x86_64 {
601        let fn_args = fn_args.replacen('(', "(id:", 1).replacen(
602            "self",
603            "Self, imp: *const std::ffi::c_void",
604            1,
605        );
606        (
607            fn_args,
608            format!("sig(self, x86_64_sel, {})", vars.join(", ")),
609        )
610    } else if fn_args_count == 0 {
611        let fn_args = fn_args
612            .replacen("( &", "(id: &", 1)
613            .replacen("self", "Self", 1);
614        (fn_args, "sig(self)".to_string())
615    } else {
616        let fn_args = fn_args
617            .replacen('(', "(id:", 1)
618            .replace("self", "Self, imp: *const std::ffi::c_void");
619        (
620            fn_args,
621            format!("sig(self, std::ptr::null(), {})", vars.join(", ")),
622        )
623    };
624
625    if class {
626        if x86_64 {
627            fn_args = fn_args.replacen(
628                "(id:",
629                "(cls: *const std::ffi::c_void, imp: *const std::ffi::c_void, ",
630                1,
631            );
632            call_args = call_args.replacen("sig(self", "sig(Self::cls_ptr()", 1);
633        } else if fn_args_count == 0 {
634            fn_args = fn_args.replacen('(', "(cls: *const std::ffi::c_void", 1);
635            call_args = call_args.replacen("sig(self", "sig(Self::cls_ptr()", 1);
636        } else {
637            fn_args = fn_args.replacen(
638                "(id:",
639                "(cls: *const std::ffi::c_void, imp: *const std::ffi::c_void, ",
640                1,
641            );
642            call_args = call_args.replacen("sig(self", "sig(Self::cls_ptr()", 1);
643        }
644    }
645
646    let available = versions.available_cfg();
647    let unavailable = versions.unavailable_cfg();
648
649    let mut flow = String::new();
650    let pre = TokenStream::from_iter(meta).to_string();
651    let self_ = if class { "Self::" } else { "self." };
652    let vars = vars.join(", ");
653    let mut impl_fn_name = fn_name.clone();
654    let impl_ret_full = if gen_rar_version {
655        ret_full.replacen("arc :: R <", "arc :: Rar <", 1)
656    } else {
657        ret_full.clone()
658    };
659    let impl_ret = if gen_rar_version {
660        ret.replacen("arc :: R <", "arc :: Rar <", 1)
661    } else {
662        ret
663    };
664
665    if gen_rar_version {
666        impl_fn_name.push_str("_ar");
667    }
668    if x86_64 {
669        flow.push_str(&format!(
670            "
671    {available}
672    {doc_alias}
673    #[inline]
674    {pre} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
675        extern \"C\" {{
676            #[link_name = \"{msg_send_fn}\"]
677            fn msg_send();
678        }}
679        extern \"C-unwind\" {{
680            fn sel_registerName(name: *const i8) -> *const std::ffi::c_void;
681        }}
682
683        unsafe {{
684            let x86_64_sel = sel_registerName(c\"{sel}\".as_ptr());
685            let fn_ptr = msg_send as *const std::ffi::c_void;
686            let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
687
688            {call_args}
689        }}
690    }}
691            "
692        ));
693        if versions.any() {
694            let unsafe_str = if unsafe_already { "" } else { "unsafe" };
695            let optional = if optional_already {
696                String::new()
697            } else {
698                format!(
699                    "
700    /// `@selector({sel})` but dynamic
701    /// use this function to check if object responds to selector
702    #[inline]
703    pub fn sel_{fn_name}() -> &'static objc::Sel {{
704        unsafe {{ objc::sel_reg_name(c\"{sel}\".as_ptr()) }}
705    }}
706        "
707                )
708            };
709
710            flow.push_str(&format!(
711                "
712    {optional}
713
714    {unavailable}
715    {doc_alias}
716    #[inline]
717    {pre} {unsafe_str} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
718        extern \"C\" {{
719            #[link_name = \"{msg_send_fn}\"]
720            fn msg_send();
721
722        }}
723        extern \"C-unwind\" {{
724            fn sel_registerName(name: *const i8) -> *const std::ffi::c_void;
725        }}
726
727        unsafe {{
728            let x86_64_sel = sel_registerName(c\"{sel}\".as_ptr());
729            let fn_ptr = msg_send as *const std::ffi::c_void;
730            let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
731
732            {call_args}
733        }}
734    }}
735                "
736            ));
737        }
738    } else {
739        flow.push_str(&format!(
740            "
741    {available}
742    {doc_alias}
743    #[inline]
744    {pre} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
745        extern \"C\" {{
746            #[link_name = \"objc_msgSend${sel}\"]
747            fn msg_send();
748        }}
749
750        unsafe {{
751            let fn_ptr = msg_send as *const std::ffi::c_void;
752            let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
753
754            {call_args}
755        }}
756    }}
757            "
758        ));
759        if versions.any() {
760            let unsafe_str = if unsafe_already { "" } else { "unsafe" };
761            let optional = if optional_already {
762                String::new()
763            } else {
764                format!(
765                    "
766    /// `@selector({sel})` but dynamic
767    /// use this function to check if object responds to selector
768    #[inline]
769    pub fn sel_{fn_name}() -> &'static objc::Sel {{
770        unsafe {{ objc::sel_reg_name(c\"{sel}\".as_ptr()) }}
771    }}
772        "
773                )
774            };
775
776            flow.push_str(&format!(
777                "
778    {optional}
779
780    {unavailable}
781    {doc_alias}
782    #[inline]
783    {pre} {unsafe_str} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
784        extern \"C\" {{
785            #[link_name = \"objc_msgSend${sel}\"]
786            fn msg_send();
787        }}
788
789        let fn_ptr = msg_send as *const std::ffi::c_void;
790        let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
791
792        {call_args}
793    }}
794                "
795            ));
796        }
797    };
798
799    if gen_rar_version {
800        if debug {
801            println!("get rar version");
802        }
803        if option {
804            flow.push_str(&format!(
805                "
806
807    {available}
808    {doc_alias}
809    #[inline]
810    {pre} fn {fn_name}{gen}{args}{ret_full} {{
811        arc::rar_retain_option({self_}{fn_name}_ar({vars}) )
812    }}
813                "
814            ));
815            if versions.any() {
816                let unsafe_str = if unsafe_already { "" } else { "unsafe" };
817                flow.push_str(&format!(
818                    "
819
820    {unavailable}
821    {doc_alias}
822    #[inline]
823    /// Check availability with selector1 `Self::sel_{fn_name}()`
824    {pre} {unsafe_str} fn {fn_name}{gen}{args}{ret_full} {{
825        arc::rar_retain_option({self_}{fn_name}_ar({vars}) )
826    }}
827                      ",
828                ));
829            }
830        } else {
831            // not option
832            flow.push_str(&format!(
833                "
834
835    {available}
836    {doc_alias}
837    #[inline]
838    {pre} fn {fn_name}{gen}{args}{ret_full} {{
839        arc::rar_retain({self_}{fn_name}_ar({vars}))
840    }}
841                ",
842            ));
843            if versions.any() {
844                let unsafe_str = if unsafe_already { "" } else { "unsafe" };
845                flow.push_str(&format!(
846                    "
847
848    {unavailable}
849    {doc_alias}
850    /// Check availability with selector `Self::sel_{fn_name}()`
851    #[inline]
852    {pre} {unsafe_str} fn {fn_name}{gen}{args}{ret_full} {{
853        arc::rar_retain({self_}{fn_name}_ar({vars}))
854    }}
855                "
856                ));
857            }
858        }
859    }
860    if debug {
861        println!("{flow}");
862    }
863
864    flow.parse().unwrap()
865}
866
867fn fn_args_from_stream(stream: TokenStream) -> (bool, Vec<String>) {
868    if stream.is_empty() {
869        return (true, Vec::new());
870    }
871    let mut res = Vec::new();
872    let mut pos = 0;
873    let mut self_arg = false;
874    let mut skip_ident = false;
875    let mut lifetime = false;
876    let mut nesting = 0;
877    for s in stream.into_iter() {
878        match s {
879            TokenTree::Group(_) => {}
880            TokenTree::Ident(ref i) => {
881                if lifetime {
882                    lifetime = false;
883                    continue;
884                }
885                if !skip_ident {
886                    let str = i.to_string();
887                    if str == "mut" {
888                        continue;
889                    }
890                    if pos == 0 && str == "self" {
891                        self_arg = true;
892                        continue;
893                    }
894                    res.push(str);
895                    skip_ident = true;
896                }
897            }
898            // #[objc::msg_send_debug(objectForKey:)]
899            // pub fn get<'a>(&'a self, key: &K) -> Option<&'a V>;
900            TokenTree::Punct(p) => match p.as_char() {
901                '<' => nesting += 1,
902                '>' => nesting -= 1,
903                '\'' => lifetime = true,
904                // '&' => skip_ident = true,
905                ',' if nesting == 0 => {
906                    pos += 1;
907                    skip_ident = false;
908                }
909                ':' => skip_ident = true,
910                _ => {}
911            },
912            TokenTree::Literal(ref _l) => {}
913        }
914    }
915    (!self_arg, res)
916}
917
918#[proc_macro_attribute]
919pub fn api_weak(_ts: TokenStream, body: TokenStream) -> TokenStream {
920    let mut original_body = body.clone();
921    let mut iter = body.into_iter();
922    let mut versions = None;
923    let mut tokens: Vec<TokenTree> = Vec::new();
924    let mut vars: Vec<(Versions, String, String)> = Vec::new(); // Version, Name, Type
925    while let Some(t) = iter.next() {
926        match t {
927            // extern "C" {
928            TokenTree::Group(ref p) if p.delimiter() == Delimiter::Brace => {
929                let mut group = p.stream().into_iter();
930                while let Some(t) = group.next() {
931                    match t {
932                        TokenTree::Punct(ref p) if p.as_char() == ':' => {
933                            if let Some(version) = versions.take() {
934                                let var_name = tokens.last().unwrap().to_string();
935                                let _t = group.next().unwrap(); // &
936                                let _t = group.next().unwrap(); // '
937                                if let TokenTree::Ident(ident) = group.next().unwrap() {
938                                    assert_eq!(ident.to_string(), "static");
939                                    let mut ty = Vec::new();
940                                    while let Some(t) = group.next() {
941                                        match t {
942                                            TokenTree::Punct(ref p) if p.as_char() == ';' => break,
943                                            t => ty.push(t),
944                                        }
945                                    }
946                                    let ty = if ty.len() == 1 {
947                                        ty[0].to_string()
948                                    } else {
949                                        TokenStream::from_iter(ty).to_string()
950                                    };
951                                    vars.push((version, var_name, ty));
952                                }
953                            }
954                        }
955                        TokenTree::Punct(ref p) if p.as_char() == ';' => {
956                            tokens.clear();
957                            versions = None;
958                        }
959                        TokenTree::Group(ref p) if p.delimiter() == Delimiter::Parenthesis => {
960                            if let Some(version) = versions.take() {
961                                let mut ty: Vec<TokenTree> =
962                                    TokenStream::from_str("extern \"C\" fn ")
963                                        .unwrap()
964                                        .into_iter()
965                                        .collect();
966                                let name = tokens.pop().unwrap();
967                                let var_name = name.to_string();
968                                // ty.push(name);
969                                ty.push(t.clone());
970                                while let Some(t) = group.next() {
971                                    match t {
972                                        TokenTree::Punct(ref p) if p.as_char() == ';' => break,
973                                        t => ty.push(t),
974                                    }
975                                }
976                                let ty = if ty.len() == 1 {
977                                    ty[0].to_string()
978                                } else {
979                                    TokenStream::from_iter(ty).to_string()
980                                };
981
982                                // println!("ty: {ty:?}");
983
984                                vars.push((version, var_name, ty));
985                            }
986                        }
987                        TokenTree::Group(ref p) if p.delimiter() == Delimiter::Bracket => {
988                            let mut attr = p.stream().into_iter();
989                            while let Some(ref ident) = attr.next() {
990                                match ident {
991                                    // TokenTree::Group(_) => todo!(),
992                                    TokenTree::Ident(i) => {
993                                        let st = i.to_string();
994                                        match st.as_str() {
995                                            // api::available
996                                            "api" => {
997                                                attr.next();
998                                                attr.next();
999                                                continue;
1000                                            }
1001                                            // objc::available
1002                                            "objc" => {
1003                                                attr.next();
1004                                                attr.next();
1005                                                continue;
1006                                            }
1007                                            // direct available
1008                                            "available" => {
1009                                                if let Some(TokenTree::Group(g)) = attr.next() {
1010                                                    versions =
1011                                                        Some(Versions::from_stream(g.stream()));
1012                                                    // println!("features {features:?}");
1013                                                } else {
1014                                                    break;
1015                                                }
1016                                                break;
1017                                            }
1018                                            _ => break,
1019                                        }
1020                                    }
1021                                    _ => break, // TokenTree::Punct(_) => todo!(),
1022                                                // TokenTree::Literal(_) => todo!(),
1023                                }
1024                            }
1025
1026                            // println!("found {p:?}")
1027                        }
1028                        _ => {}
1029                    }
1030                    tokens.push(t);
1031                    // println!("t: {t:?}")
1032                }
1033            }
1034            _x => {
1035                // println!("x: {x:?}")
1036            }
1037        }
1038    }
1039    // println!("{vars:?}");
1040    let vars = vars
1041        .iter()
1042        .map(|(version, name, ty)| {
1043            let upper_name = upper_case(name);
1044            let availability = version.unavailable_cfg();
1045            format!(
1046            "{availability}\nstatic {upper_name}: api::DlSym<{ty}> = api::DlSym::new(c\"{name}\");"
1047        )
1048        })
1049        .collect::<Vec<_>>()
1050        .join("\n");
1051    let stream = TokenStream::from_str(&vars).unwrap();
1052    original_body.extend(stream);
1053    original_body
1054}
1055
1056#[derive(Default, Debug, Copy, Clone)]
1057struct Version(u32, u32);
1058
1059impl Version {
1060    fn from_str(str: &str) -> Option<Self> {
1061        if let Some((major, minor)) = str.split_once('.') {
1062            Some(Self(str::parse(major).unwrap(), str::parse(minor).unwrap()))
1063        } else if let Some((major, minor)) = str.split_once('_') {
1064            Some(Self(str::parse(major).unwrap(), str::parse(minor).unwrap()))
1065        } else {
1066            None
1067        }
1068    }
1069}
1070
1071#[derive(Default, Debug)]
1072struct Versions {
1073    macos: Option<Version>,
1074    ios: Option<Version>,
1075    tvos: Option<Version>,
1076    watchos: Option<Version>,
1077    visionos: Option<Version>,
1078    maccatalyst: Option<Version>,
1079}
1080
1081impl Versions {
1082    fn any(&self) -> bool {
1083        self.macos.is_some()
1084            || self.ios.is_some()
1085            || self.tvos.is_some()
1086            || self.watchos.is_some()
1087            || self.visionos.is_some()
1088            || self.maccatalyst.is_some()
1089    }
1090
1091    fn available_cfg_ts(&self) -> TokenStream {
1092        TokenStream::from_str(&self.available_cfg()).unwrap()
1093    }
1094
1095    fn available_cfg(&self) -> String {
1096        let mut vec = Vec::with_capacity(6);
1097        if let Some(v) = self.macos {
1098            vec.push(format!(
1099                "all(target_os=\"macos\", feature=\"macos_{}_{}\")",
1100                v.0, v.1
1101            ));
1102        }
1103        if let Some(v) = self.ios {
1104            vec.push(format!(
1105                "all(target_os=\"ios\", feature=\"ios_{}_{}\")",
1106                v.0, v.1
1107            ));
1108        }
1109        if let Some(v) = self.tvos {
1110            vec.push(format!(
1111                "all(target_os=\"tvos\", feature=\"tvos_{}_{}\")",
1112                v.0, v.1,
1113            ));
1114        }
1115        if let Some(v) = self.watchos {
1116            vec.push(format!(
1117                "all(target_os=\"watchos\", feature=\"watchos_{}_{}\")",
1118                v.0, v.1,
1119            ));
1120        }
1121        if let Some(v) = self.visionos {
1122            vec.push(format!(
1123                "all(target_os=\"visionos\", feature=\"visionos_{}_{}\")",
1124                v.0, v.1
1125            ));
1126        }
1127        if let Some(v) = self.maccatalyst {
1128            vec.push(format!(
1129                "all(target_os=\"ios\", target_abi=\"macabi\", feature=\"maccatalyst_{}_{}\")",
1130                v.0, v.1
1131            ));
1132        }
1133
1134        match vec.len() {
1135            0 => String::new(),
1136            1 => format!("#[cfg({})]\n", vec[0]),
1137            _ => format!("#[cfg(any({}))]\n", vec.join(", ")),
1138        }
1139    }
1140    fn unavailable_cfg_ts(&self) -> TokenStream {
1141        TokenStream::from_str(&self.unavailable_cfg()).unwrap()
1142    }
1143
1144    fn unavailable_cfg(&self) -> String {
1145        let mut vec = Vec::with_capacity(6);
1146        if let Some(v) = self.macos {
1147            vec.push(format!(
1148                "all(target_os=\"macos\", not(feature=\"macos_{}_{}\"))",
1149                v.0, v.1
1150            ));
1151        }
1152        if let Some(v) = self.ios {
1153            vec.push(format!(
1154                "all(target_os=\"ios\", not(feature=\"ios_{}_{}\"))",
1155                v.0, v.1
1156            ));
1157        }
1158        if let Some(v) = self.tvos {
1159            vec.push(format!(
1160                "all(target_os=\"tvos\", not(feature=\"tvos_{}_{}\"))",
1161                v.0, v.1
1162            ));
1163        }
1164        if let Some(v) = self.watchos {
1165            vec.push(format!(
1166                "all(target_os=\"watchos\", not(feature=\"watchos_{}_{}\"))",
1167                v.0, v.1
1168            ));
1169        }
1170        if let Some(v) = self.visionos {
1171            vec.push(format!(
1172                "all(target_os=\"visionos\", not(feature=\"visionos_{}_{}\"))",
1173                v.0, v.1
1174            ));
1175        }
1176        if let Some(v) = self.maccatalyst {
1177            vec.push(format!(
1178                "all(target_os=\"ios\", target_abi=\"macabi\", not(feature=\"maccatalyst_{}_{}\"))",
1179                v.0, v.1
1180            ));
1181        }
1182
1183        match vec.len() {
1184            0 => String::new(),
1185            1 => format!("#[cfg({})]\n", vec[0]),
1186            _ => format!("#[cfg(any({}))]\n", vec.join(", ")),
1187        }
1188    }
1189    fn available_doc_ts(&self) -> TokenStream {
1190        TokenStream::from_str(&self.available_doc()).unwrap()
1191    }
1192
1193    fn available_doc(&self) -> String {
1194        let mut vec = Vec::with_capacity(6);
1195        if let Some(v) = self.macos {
1196            vec.push(format!("macos_{}_{}", v.0, v.1));
1197        }
1198        if let Some(v) = self.ios {
1199            vec.push(format!("ios_{}_{}", v.0, v.1));
1200        }
1201        if let Some(v) = self.tvos {
1202            vec.push(format!("tvos_{}_{}", v.0, v.1,));
1203        }
1204        if let Some(v) = self.watchos {
1205            vec.push(format!("watchos_{}_{}", v.0, v.1));
1206        }
1207        if let Some(v) = self.visionos {
1208            vec.push(format!("visionos_{}_{}", v.0, v.1));
1209        }
1210        if let Some(v) = self.maccatalyst {
1211            vec.push(format!("maccatalyst_{}_{}", v.0, v.1));
1212        }
1213
1214        match vec.len() {
1215            0 => String::new(),
1216            1 => format!("/// # Availability\n/// {0}", vec[0]),
1217            _ => format!("/// # Availability\n/// {0}", vec.join(", ")),
1218        }
1219    }
1220    fn unavailable_doc_ts(&self) -> TokenStream {
1221        TokenStream::from_str(&self.unavailable_doc()).unwrap()
1222    }
1223
1224    fn unavailable_doc(&self) -> String {
1225        let mut vec = Vec::with_capacity(6);
1226        if let Some(v) = self.macos {
1227            vec.push(format!("macos_{}_{}", v.0, v.1));
1228        }
1229        if let Some(v) = self.ios {
1230            vec.push(format!("ios_{}_{}", v.0, v.1));
1231        }
1232        if let Some(v) = self.tvos {
1233            vec.push(format!("tvos_{}_{}", v.0, v.1,));
1234        }
1235        if let Some(v) = self.watchos {
1236            vec.push(format!("watchos_{}_{}", v.0, v.1));
1237        }
1238        if let Some(v) = self.visionos {
1239            vec.push(format!("visionos_{}_{}", v.0, v.1));
1240        }
1241        if let Some(v) = self.maccatalyst {
1242            vec.push(format!("maccatalyst_{}_{}", v.0, v.1));
1243        }
1244
1245        match vec.len() {
1246            0 => String::new(),
1247            1 => format!("/// # Availability\n/// Not {0}", vec[0]),
1248            _ => format!("/// # Availability\n/// Not {0}", vec.join(", ")),
1249        }
1250    }
1251
1252    fn from_stream(versions: TokenStream) -> Self {
1253        let mut iter = versions.into_iter();
1254        let mut versions = Self::default();
1255        while let Some(t) = iter.next() {
1256            let target_os = match t {
1257                TokenTree::Ident(ident) => ident.to_string(),
1258                _ => panic!("Unexpected token {t:?}"),
1259            };
1260            let Some(TokenTree::Punct(ident)) = iter.next() else {
1261                panic!("Expecting = ");
1262            };
1263
1264            assert_eq!(ident, '=', "expecting =");
1265
1266            let Some(TokenTree::Literal(val)) = iter.next() else {
1267                panic!("expecting version");
1268            };
1269
1270            let v = Version::from_str(&val.to_string());
1271            match target_os.as_str() {
1272            "macos" => versions.macos = v,
1273            "ios" => versions.ios = v,
1274            "tvos" => versions.tvos = v,
1275            "watchos" => versions.watchos = v,
1276            "visionos" => versions.visionos = v,
1277            "maccatalyst" => versions.maccatalyst = v,
1278            t => panic!("Unsupported platform. Platform should be macos, ios, watchos, visionos or maccatalyst. Found {t:?}"),
1279        };
1280
1281            if let Some(TokenTree::Punct(p)) = iter.next() {
1282                assert_eq!(p, ',', "expect ,");
1283            };
1284        }
1285
1286        versions
1287    }
1288
1289    fn from_doc_str(str: &str) -> Self {
1290        let mut res = Self::default();
1291        for str in str.split_whitespace() {
1292            for str in str.split_terminator(',') {
1293                if str.starts_with("macos_") {
1294                    res.macos = Version::from_str(&str[6..]);
1295                } else if str.starts_with("ios_") {
1296                    res.ios = Version::from_str(&str[4..]);
1297                } else if str.starts_with("tvos_") {
1298                    res.tvos = Version::from_str(&str[5..]);
1299                } else if str.starts_with("watchos_") {
1300                    res.watchos = Version::from_str(&str[8..]);
1301                } else if str.starts_with("visionos_") {
1302                    res.visionos = Version::from_str(&str[8..]);
1303                } else if str.starts_with("maccatalyst_") {
1304                    res.maccatalyst = Version::from_str(&str[12..]);
1305                }
1306            }
1307        }
1308        res
1309    }
1310}
1311
1312#[proc_macro_attribute]
1313pub fn api_available(versions: TokenStream, body: TokenStream) -> TokenStream {
1314    let versions = Versions::from_stream(versions);
1315    let available = versions.available_cfg_ts();
1316    let available_doc = versions.available_doc_ts();
1317    let unavailable = versions.unavailable_cfg_ts();
1318    let unavailable_doc = versions.unavailable_doc_ts();
1319    if available.is_empty() {
1320        return body;
1321    }
1322
1323    let mut no_args = false;
1324    let mut no_body = false;
1325
1326    let mut available = Some(available);
1327    let mut available_doc = Some(available_doc);
1328    let mut unavailable = Some(unavailable);
1329    let mut unavailable_doc = Some(unavailable_doc);
1330    let mut res = Vec::new();
1331    let mut maybe_res: Vec<TokenTree> = Vec::new();
1332    let mut fn_index = 0usize;
1333    let mut unsafe_already = false;
1334
1335    let mut body_iter = body.into_iter();
1336
1337    while let Some(t) = body_iter.next() {
1338        if available.is_some() {
1339            res.extend(available.take().unwrap());
1340        }
1341        if unavailable.is_some() {
1342            maybe_res.extend(unavailable.take().unwrap());
1343        }
1344        if available_doc.is_some() {
1345            match t {
1346                TokenTree::Ident(ref _i) => {
1347                    let doc = available_doc.take().unwrap();
1348                    res.extend(doc);
1349                }
1350                _ => {}
1351            }
1352        }
1353        if unavailable_doc.is_some() {
1354            match t {
1355                TokenTree::Ident(ref _i) => {
1356                    let doc = unavailable_doc.take().unwrap();
1357                    maybe_res.extend(doc);
1358                }
1359                _ => {}
1360            }
1361        }
1362        match t {
1363            TokenTree::Punct(ref p) if p.as_char() == ';' => {
1364                no_body = true;
1365            }
1366            TokenTree::Ident(ref ident) => match ident.to_string().as_str() {
1367                "fn" => {
1368                    fn_index = maybe_res.len();
1369                }
1370                "unsafe" => {
1371                    unsafe_already = true;
1372                }
1373                "define_cls" => {
1374                    let token = TokenTree::Ident(Ident::new("define_weak_cls", t.span()));
1375                    maybe_res.push(token);
1376                    res.push(t.clone());
1377                    while let Some(t) = body_iter.next() {
1378                        maybe_res.push(t.clone());
1379                        res.push(t);
1380                    }
1381                    break;
1382                }
1383                "define_cls_init" => {
1384                    let token = TokenTree::Ident(Ident::new("define_weak_cls_init", t.span()));
1385                    maybe_res.push(token);
1386                    res.push(t.clone());
1387                    while let Some(t) = body_iter.next() {
1388                        maybe_res.push(t.clone());
1389                        res.push(t);
1390                    }
1391                    break;
1392                }
1393                _ => {}
1394            },
1395            _ => {}
1396        }
1397
1398        maybe_res.push(t.clone());
1399
1400        if let TokenTree::Group(ref g) = t {
1401            // function without args ()
1402            if g.delimiter() == Delimiter::Parenthesis {
1403                no_args = g.stream().is_empty();
1404                // we are in function.
1405            }
1406            // function body {}
1407            if g.delimiter() == Delimiter::Brace {
1408                if no_args && try_replace_return(&mut maybe_res) {}
1409                let mut make_result_optional = false;
1410                if try_replace_fn(&mut maybe_res, &mut make_result_optional) {
1411                    if make_result_optional {
1412                        let mut i = maybe_res.len() - 2;
1413
1414                        while i > 0 {
1415                            if let TokenTree::Punct(ref p) = maybe_res[i] {
1416                                if p.as_char() == '-' && p.spacing() == Spacing::Joint {
1417                                    i += 2;
1418                                    break;
1419                                }
1420                            }
1421                            i -= 1;
1422                        }
1423
1424                        maybe_res.insert(i, TokenTree::Punct(Punct::new('<', Spacing::Alone)));
1425                        maybe_res
1426                            .insert(i, TokenTree::Ident(Ident::new("Option", Span::call_site())));
1427                        maybe_res.insert(
1428                            maybe_res.len() - 2,
1429                            TokenTree::Punct(Punct::new('>', Spacing::Alone)),
1430                        );
1431                    }
1432                } else {
1433                    if !unsafe_already {
1434                        maybe_res.insert(
1435                            fn_index,
1436                            TokenTree::Ident(Ident::new("unsafe", Span::call_site())),
1437                        );
1438                    }
1439                }
1440            }
1441        }
1442
1443        res.push(t);
1444    }
1445    if !no_body {
1446        res.extend(maybe_res);
1447    }
1448
1449    TokenStream::from_iter(res)
1450}
1451
1452fn try_replace_fn(tokens: &mut Vec<TokenTree>, make_result_optional: &mut bool) -> bool {
1453    let Some(TokenTree::Group(ref g)) = tokens.last() else {
1454        return false;
1455    };
1456    if g.delimiter() != Delimiter::Brace {
1457        return false;
1458    }
1459    // check fn body. it should be function call.
1460    let mut body_stream = g.stream().into_iter();
1461
1462    match body_stream.next().unwrap() {
1463        TokenTree::Group(scope) => {
1464            if scope.delimiter() == Delimiter::Brace {
1465                *make_result_optional = true;
1466                return true;
1467            }
1468        }
1469        TokenTree::Ident(ident) => match ident.to_string().as_str() {
1470            "unsafe" => {
1471                let Some(TokenTree::Group(g)) = body_stream.next() else {
1472                    return false;
1473                };
1474                if g.delimiter() != Delimiter::Brace {
1475                    return false;
1476                }
1477                let mut block = g.stream().into_iter();
1478                let Some(TokenTree::Ident(ident)) = block.next() else {
1479                    return false;
1480                };
1481                let Some(TokenTree::Group(args)) = block.next() else {
1482                    return false;
1483                };
1484                if args.delimiter() != Delimiter::Parenthesis {
1485                    return false;
1486                }
1487                if block.next().is_none() {
1488                    tokens.pop();
1489                    let var = upper_case(&ident.to_string());
1490                    let stream = TokenStream::from_str(&format!(
1491                        "{{ unsafe {{ {var}.get_fn().unwrap(){} }} }}",
1492                        args.to_string()
1493                    ))
1494                    .unwrap();
1495                    tokens.extend(stream);
1496
1497                    return true;
1498                }
1499            }
1500            "Self" => {
1501                // `Self::alloc()`
1502
1503                let mut new_body = Vec::new();
1504                new_body.push(TokenTree::Ident(ident));
1505                let Some(TokenTree::Punct(p)) = body_stream.next() else {
1506                    return false;
1507                };
1508
1509                if p.as_char() != ':' {
1510                    return false;
1511                }
1512                new_body.push(TokenTree::Punct(p));
1513                let Some(TokenTree::Punct(p)) = body_stream.next() else {
1514                    return false;
1515                };
1516
1517                if p.as_char() != ':' {
1518                    return false;
1519                }
1520                new_body.push(TokenTree::Punct(p));
1521                let Some(TokenTree::Ident(ident)) = body_stream.next() else {
1522                    return false;
1523                };
1524
1525                if ident.to_string() != "alloc" {
1526                    return false;
1527                };
1528                new_body.push(TokenTree::Ident(ident));
1529
1530                let Some(TokenTree::Group(g)) = body_stream.next() else {
1531                    return false;
1532                };
1533
1534                if g.delimiter() != Delimiter::Parenthesis {
1535                    return false;
1536                }
1537                new_body.push(TokenTree::Group(g));
1538
1539                // Some(Self::alloc()?.)
1540                new_body.push(TokenTree::Punct(Punct::new('?', Spacing::Alone)));
1541                while let Some(t) = body_stream.next() {
1542                    new_body.push(t)
1543                }
1544                let s = TokenStream::from_iter(new_body.drain(..));
1545                new_body.push(TokenTree::Ident(Ident::new("Some", Span::call_site())));
1546                let g = TokenTree::Group(Group::new(Delimiter::Parenthesis, s));
1547                new_body.push(g);
1548                let s = TokenStream::from_iter(new_body.drain(..));
1549                let g = TokenTree::Group(Group::new(Delimiter::Brace, s));
1550
1551                tokens.pop();
1552                tokens.push(g);
1553
1554                *make_result_optional = true;
1555                return true;
1556                // println!("found");
1557            }
1558            _ => return false,
1559        },
1560
1561        _ => return false,
1562    };
1563    false
1564}
1565
1566fn try_replace_return(tokens: &mut Vec<TokenTree>) -> bool {
1567    let mut idx = tokens.len() - 1;
1568    let mut has_static = false;
1569    // going reverse till `&'static` or ()
1570    while idx > 0 {
1571        match &tokens[idx] {
1572            TokenTree::Group(g) if g.delimiter() == Delimiter::Parenthesis => return false,
1573            TokenTree::Ident(i) if i.to_string() == "static" => has_static = true,
1574            TokenTree::Punct(p) if p.as_char() == '\'' => break,
1575            // TokenTree::Literal(_) => todo!(),
1576            _ => {}
1577        }
1578        idx -= 1;
1579    }
1580
1581    if !has_static {
1582        return false;
1583    }
1584
1585    let mut body_stream = {
1586        let Some(TokenTree::Group(ref g)) = tokens.last() else {
1587            return false;
1588        };
1589
1590        // check fn body. it should return single var, no function call.
1591        g.stream().into_iter()
1592    };
1593
1594    // check for
1595    // unsafe { VAR }
1596    // or
1597    // unsafe { fn_call(args) }
1598
1599    let var = match body_stream.next().unwrap() {
1600        TokenTree::Ident(ident) => match ident.to_string().as_str() {
1601            "unsafe" => {
1602                let TokenTree::Group(g) = body_stream.next().unwrap() else {
1603                    return false;
1604                };
1605                if g.delimiter() != Delimiter::Brace {
1606                    return false;
1607                }
1608                let mut block = g.stream().into_iter();
1609                let TokenTree::Ident(ident) = block.next().unwrap() else {
1610                    return false;
1611                };
1612                if let Some(TokenTree::Group(_g)) = block.next() {
1613                    return false;
1614                };
1615                ident.to_string()
1616            }
1617            x => x.to_string(),
1618        },
1619
1620        _ => return false,
1621    };
1622
1623    idx -= 1; // &
1624    tokens.pop(); // {}
1625    let stream = TokenStream::from_iter(tokens.drain(idx..));
1626    let ty = stream.to_string();
1627    let stream = TokenStream::from_str(&format!("Option<{ty}>")).unwrap();
1628    tokens.extend(stream);
1629    let var = upper_case(&var.to_string());
1630    let stream = TokenStream::from_str(&format!("{{ unsafe {{ {var}.get_var() }} }}")).unwrap();
1631    tokens.extend(stream);
1632    true
1633}
1634
1635// Super simple, but stable upper_case impl
1636fn upper_case(str: &str) -> String {
1637    let len = str.len();
1638    let mut res = Vec::<u8>::with_capacity(len + 10);
1639    let bytes = str.as_bytes();
1640    let mut was_lowercase = false;
1641    for ch in bytes {
1642        let is_upper = ch.is_ascii_uppercase();
1643        if was_lowercase && is_upper {
1644            res.push(b'_');
1645        }
1646        res.push(ch.to_ascii_uppercase());
1647        was_lowercase = !is_upper;
1648    }
1649
1650    String::from_utf8(res).unwrap()
1651}
1652
1653/// Whether `sel` belongs to an Objective-C method family.
1654///
1655/// A selector is in family `F` when, after any leading underscores, it begins
1656/// with `F` and the next character is not a lowercase letter. That word
1657/// boundary is what separates `initWithFoo:`/`initFileURLWithPath:` (the `init`
1658/// family, which returns at +1) from `initialize` (which does not).
1659fn in_method_family(sel: &str, family: &str) -> bool {
1660    match sel.trim_start_matches('_').strip_prefix(family) {
1661        Some(rest) => !rest.starts_with(|c: char| c.is_ascii_lowercase()),
1662        None => false,
1663    }
1664}
1665
1666/// Whether the callee hands back an object it already owns, so the caller must
1667/// not retain it again.
1668///
1669/// These are Objective-C's returns-retained method families. `alloc` is absent
1670/// only because it yields `arc::A` rather than `arc::R`, so it never reaches
1671/// the retaining path.
1672///
1673/// A binding can also opt out of the retain by spelling its return type
1674/// `arc::Retained<T>` instead of the `arc::R<T>` alias, which is how cidre
1675/// marks a method annotated `CF_RETURNS_RETAINED` in its header. Getting that
1676/// spelling wrong on a +0 method releases an object the caller never owned, so
1677/// only use it where the header says so.
1678fn returns_retained(sel: &str) -> bool {
1679    const RETAINING_FAMILIES: [&str; 4] = ["init", "new", "mutableCopy", "copy"];
1680    // `mutableCopy` is tested before `copy` so the longer name wins.
1681    RETAINING_FAMILIES
1682        .iter()
1683        .any(|family| in_method_family(sel, family))
1684}
1685
1686fn choose_msg_send_fn(ret: &str) -> &'static str {
1687    const MSG_SEND: &'static str = "objc_msgSend";
1688    const MSG_SEND_FPRET: &'static str = "objc_msgSend_fpret";
1689    const MSG_SEND_STRET: &'static str = "objc_msgSend_stret";
1690
1691    if ret.is_empty()
1692        || ret.starts_with("-> arc :: ")
1693        || ret.starts_with("-> Option <")
1694        || ret.starts_with("-> * const ")
1695        || ret.starts_with("-> * mut ")
1696        || ret.starts_with("-> & ")
1697    {
1698        return MSG_SEND;
1699    }
1700
1701    match ret {
1702        "-> bool"
1703        | "-> i8"
1704        | "-> u8"
1705        | "-> i16"
1706        | "-> u16"
1707        | "-> f32"
1708        | "-> u32"
1709        | "-> i32"
1710        | "-> u64"
1711        | "-> i64"
1712        | "-> isize"
1713        | "-> usize"
1714        | "-> ns :: Integer"
1715        | "-> ns :: UInteger"
1716        | "-> std :: ffi :: c_int"
1717        | "-> mps :: DType"
1718        | "-> crate :: mtl :: ResId"
1719        | "-> mach :: Port"
1720        | "-> os :: Type"
1721        | "-> vn :: Confidence"
1722        | "-> vn :: AspectRatio"
1723        | "-> vn :: Degrees"
1724        | "-> sys :: Pid" => MSG_SEND,
1725
1726        // known structs
1727        "-> ns :: Rect"
1728        | "-> cg :: Rect"
1729        | "-> cm :: Time"
1730        | "-> cm :: TimeRange"
1731        | "-> cg :: AffineTransform"
1732        | "-> cg :: Size"
1733        | "-> cg :: Point"
1734        | "-> ns :: Size"
1735        | "-> ns :: Point"
1736        | "-> cm :: RotationRate"
1737        | "-> cm :: Acceleration"
1738        | "-> ca :: Transform3d"
1739        | "-> mtl :: Size" => MSG_SEND_STRET,
1740
1741        // known floats
1742        "-> f64"
1743        | "-> cg :: Float"
1744        | "-> ns :: Float"
1745        | "-> ns :: Range"
1746        | "-> cf :: TimeInterval"
1747        | "-> ns :: TimeInterval" => MSG_SEND_FPRET,
1748        _x => {
1749            // TODO: do actual structure size check
1750            // println!("unknown {x}");
1751            MSG_SEND
1752        }
1753    }
1754}
1755
1756// fn is_upper_case(str: &str) -> bool {
1757//     let bytes = str.as_bytes();
1758//     for ch in bytes {
1759//         if ch != &b'_' {
1760//             if !ch.is_ascii_uppercase() {
1761//                 return false;
1762//             }
1763//         }
1764//     }
1765//     true
1766// }