Skip to main content

mago_analyzer/plugin/provider/
method.rs

1//! Method return type provider trait.
2
3use mago_codex::ttype::union::TUnion;
4use mago_word::Word;
5use mago_word::ascii_lowercase_word;
6use mago_word::concat_word;
7use mago_word::starts_with_ignore_case;
8
9use crate::plugin::context::InvocationInfo;
10use crate::plugin::context::ProviderContext;
11use crate::plugin::provider::Provider;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct MethodTarget {
15    pub class: &'static [u8],
16    pub method: &'static [u8],
17}
18
19impl MethodTarget {
20    #[inline]
21    #[must_use]
22    pub const fn exact(class: &'static [u8], method: &'static [u8]) -> Self {
23        Self { class, method }
24    }
25
26    #[inline]
27    #[must_use]
28    pub const fn all_methods(class: &'static [u8]) -> Self {
29        Self { class, method: b"*" }
30    }
31
32    #[inline]
33    #[must_use]
34    pub const fn any_class(method: &'static [u8]) -> Self {
35        Self { class: b"*", method }
36    }
37
38    #[must_use]
39    pub fn matches(&self, class_name: &[u8], method_name: &[u8]) -> bool {
40        self.matches_class(class_name) && self.matches_method(method_name)
41    }
42
43    fn matches_class(&self, class_name: &[u8]) -> bool {
44        if self.class == b"*" {
45            return true;
46        }
47
48        if self.class.last() == Some(&b'*') {
49            starts_with_ignore_case(class_name, &self.class[..self.class.len() - 1])
50        } else {
51            class_name.eq_ignore_ascii_case(self.class)
52        }
53    }
54
55    fn matches_method(&self, method_name: &[u8]) -> bool {
56        if self.method == b"*" {
57            return true;
58        }
59
60        if self.method.last() == Some(&b'*') {
61            starts_with_ignore_case(method_name, &self.method[..self.method.len() - 1])
62        } else {
63            method_name.eq_ignore_ascii_case(self.method)
64        }
65    }
66
67    #[must_use]
68    pub fn is_exact(&self) -> bool {
69        !self.class.contains(&b'*') && !self.method.contains(&b'*')
70    }
71
72    #[must_use]
73    pub fn index_key(&self) -> Option<Word> {
74        if self.is_exact() {
75            Some(concat_word!(ascii_lowercase_word(self.class), b"::", ascii_lowercase_word(self.method)))
76        } else {
77            None
78        }
79    }
80}
81
82pub trait MethodReturnTypeProvider: Provider {
83    fn targets() -> &'static [MethodTarget]
84    where
85        Self: Sized;
86
87    fn get_return_type(
88        &self,
89        context: &ProviderContext<'_, '_, '_>,
90        class_name: &[u8],
91        method_name: &[u8],
92        invocation: &InvocationInfo<'_, '_, '_>,
93    ) -> Option<TUnion>;
94}