1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
use syn::{punctuated::Punctuated, token::Comma, Meta, NestedMeta};

pub(crate) type PunctuatedNestedMeta = Punctuated<NestedMeta, Comma>;

thread_local! {
    static EMPTY_META_NESTED: PunctuatedNestedMeta = PunctuatedNestedMeta::new();
}

pub trait MetaExt {
    fn is_list_like(&self) -> bool;
    fn get_list_like_nested(&self) -> Option<&PunctuatedNestedMeta>;

    fn is_name_value_like(&self) -> bool;
}

fn empty_meta_nested() -> &'static PunctuatedNestedMeta {
    let ptr = EMPTY_META_NESTED.with(|nested| nested as *const _);
    unsafe {
        // Safety: Read-only thread-local always has the same value
        &*ptr
    }
}

impl MetaExt for Meta {
    fn is_list_like(&self) -> bool {
        match self {
            Meta::Path(_) => true,
            Meta::List(_) => true,
            Meta::NameValue(_) => false,
        }
    }

    fn is_name_value_like(&self) -> bool {
        match self {
            Meta::Path(_) => true,
            Meta::List(_) => false,
            Meta::NameValue(_) => true,
        }
    }

    fn get_list_like_nested(&self) -> Option<&PunctuatedNestedMeta> {
        match self {
            Meta::Path(_) => Some(empty_meta_nested()),
            Meta::List(list) => Some(&list.nested),
            Meta::NameValue(_) => None,
        }
    }
}