mago_reflection/class_like/
inheritance.rs

1use ahash::HashMap;
2use ahash::HashSet;
3
4use mago_interner::StringIdentifier;
5use mago_interner::ThreadedInterner;
6use serde::Deserialize;
7use serde::Serialize;
8
9use crate::identifier::ClassLikeName;
10use crate::identifier::Name;
11
12use super::ClassLikeReflection;
13
14/// Represents the inheritance details of a class-like entity, including implemented interfaces,
15/// extended classes or interfaces, and any required inheritance constraints.
16#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
17pub struct InheritanceReflection {
18    /// Interfaces directly implemented by the current class or enum.
19    pub direct_implemented_interfaces: HashSet<Name>,
20
21    /// All interfaces implemented by the current class or any of its ancestors,
22    /// including `direct_implemented_interfaces`.
23    pub all_implemented_interfaces: HashSet<Name>,
24
25    /// The class directly extended by the current class, if applicable.
26    pub direct_extended_class: Option<Name>,
27
28    /// All classes extended by the current class or any of its ancestors,
29    /// including `direct_extended_class`.
30    pub all_extended_classes: HashSet<Name>,
31
32    /// Interfaces directly extended by the current interface, if applicable.
33    pub direct_extended_interfaces: HashSet<Name>,
34
35    /// All interfaces extended by the current interface or any of its ancestors,
36    /// including `direct_extended_interfaces`.
37    pub all_extended_interfaces: HashSet<Name>,
38
39    /// Interfaces that the current class-like entity requires any inheriting entity to implement,
40    /// as specified by the `@require-implements` tag.
41    pub require_implementations: HashSet<StringIdentifier>,
42
43    /// Classes or interfaces that the current class-like entity requires any inheriting entity to extend,
44    /// as specified by the `@require-extends` tag.
45    pub require_extensions: HashSet<StringIdentifier>,
46
47    /// Identifiers of class-like entities that directly extend or implement the current class-like entity.
48    pub children: HashSet<ClassLikeName>,
49
50    /// A lookup map of string identifiers to class-like names.
51    pub names: HashMap<StringIdentifier, Name>,
52}
53
54impl InheritanceReflection {
55    #[inline]
56    pub fn implements_interfaces(&self) -> bool {
57        !self.all_implemented_interfaces.is_empty()
58    }
59
60    #[inline]
61    pub fn extends_classes(&self) -> bool {
62        !self.all_extended_classes.is_empty()
63    }
64
65    #[inline]
66    pub fn has_children(&self) -> bool {
67        !self.children.is_empty()
68    }
69
70    #[inline]
71    pub fn is_instance_of(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
72        let Some(other_name) = other.name.inner() else {
73            return false;
74        };
75
76        let other_identifier = interner.lowered(&other_name.value);
77        if let Some(this_name) = self.direct_extended_class {
78            let this_identifier = interner.lowered(&this_name.value);
79            if this_identifier == other_identifier {
80                return true;
81            }
82        }
83
84        let Some(other_fqcn) = self.names.get(&other_identifier) else {
85            return false;
86        };
87
88        self.all_extended_classes.contains(other_fqcn)
89            || self.all_implemented_interfaces.contains(other_fqcn)
90            || self.all_extended_interfaces.contains(other_fqcn)
91    }
92
93    #[inline]
94    pub fn extends_class(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
95        let Some(name) = other.name.inner() else {
96            return false; // we can't extend a class that does not have a name, i.e. anonymous class
97        };
98
99        self.extends_class_with_name(interner, &name.value)
100    }
101
102    #[inline]
103    pub fn extends_class_with_name(&self, interner: &ThreadedInterner, other: &StringIdentifier) -> bool {
104        let identifier = interner.lowered(other);
105        let Some(other) = self.names.get(&identifier) else {
106            return false;
107        };
108
109        self.all_extended_classes.contains(other)
110    }
111
112    #[inline]
113    pub fn extends_interface(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
114        let Some(name) = other.name.inner() else {
115            return false;
116        };
117
118        self.extends_interface_with_name(interner, &name.value)
119    }
120
121    #[inline]
122    pub fn extends_interface_with_name(&self, interner: &ThreadedInterner, other: &StringIdentifier) -> bool {
123        let identifier = interner.lowered(other);
124        let Some(other) = self.names.get(&identifier) else {
125            return false;
126        };
127
128        self.all_extended_interfaces.contains(other)
129    }
130
131    #[inline]
132    pub fn implements_interface(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
133        let Some(name) = other.name.inner() else {
134            return false;
135        };
136
137        self.implements_interface_with_name(interner, &name.value)
138    }
139
140    #[inline]
141    pub fn implements_interface_with_name(&self, interner: &ThreadedInterner, other: &StringIdentifier) -> bool {
142        let identifier = interner.lowered(other);
143        let Some(other) = self.names.get(&identifier) else {
144            return false;
145        };
146
147        self.all_implemented_interfaces.contains(other)
148    }
149}