yog_script_analyze/
index.rs1use std::collections::HashMap;
8
9use yog_data::Schema;
10
11#[derive(Schema, Debug, Clone, Copy, Default, PartialEq, Eq)]
14pub enum Side {
15 #[default]
16 Shared,
17 Client,
18 Server,
19}
20
21#[derive(Schema, Debug, Clone, Default, PartialEq, Eq)]
24pub struct Member {
25 pub name: String,
26 pub desc: String,
27 pub access: u16,
28}
29
30#[derive(Schema, Debug, Clone, Default, PartialEq, Eq)]
32pub struct Class {
33 pub name: String,
34 pub side: Side,
35 pub access: u16,
36 pub extends: Option<String>,
37 pub implements: Vec<String>,
38 pub fields: Vec<Member>,
39 pub methods: Vec<Member>,
40}
41
42#[derive(Schema, Debug, Clone, Default, PartialEq, Eq)]
44pub struct ClassIndex {
45 pub mc: String,
46 pub classes: Vec<Class>,
47}
48
49pub mod access;
50pub mod namespace;
51
52pub struct Lookup<'i> {
55 by_name: HashMap<&'i str, &'i Class>,
56 by_simple: HashMap<&'i str, Vec<&'i Class>>,
57}
58
59impl ClassIndex {
60 pub fn lookup(&self) -> Lookup<'_> {
61 let mut by_name = HashMap::new();
62 let mut by_simple: HashMap<&str, Vec<&Class>> = HashMap::new();
63 for c in &self.classes {
64 by_name.insert(c.name.as_str(), c);
65 by_simple.entry(simple_name(&c.name)).or_default().push(c);
66 }
67 Lookup { by_name, by_simple }
68 }
69}
70
71pub const NESTED: char = '$';
73
74pub fn simple_name(internal: &str) -> &str {
77 let tail = internal.rsplit('/').next().unwrap_or(internal);
78 tail.rsplit(NESTED).next().unwrap_or(tail)
79}
80
81impl<'i> Lookup<'i> {
82 pub fn class(&self, name: &str) -> Option<&'i Class> {
84 self.by_name.get(name).copied()
85 }
86
87 pub fn simple(&self, name: &str) -> &[&'i Class] {
89 self.by_simple
90 .get(name)
91 .map(Vec::as_slice)
92 .unwrap_or_default()
93 }
94
95 pub fn ancestry(&self, class: &'i Class) -> Vec<&'i Class> {
97 let mut out = vec![class];
98 while let Some(parent) = out
99 .last()
100 .and_then(|c| c.extends.as_deref())
101 .and_then(|p| self.class(p))
102 {
103 if out.iter().any(|c| c.name == parent.name) {
104 break;
105 }
106 out.push(parent);
107 }
108 out
109 }
110
111 pub fn methods(&self, class: &'i Class, name: &str) -> Vec<&'i Member> {
114 let mut out = Vec::new();
115 let mut seen = Vec::new();
116 self.collect_methods(class, name, &mut out, &mut seen);
117 out
118 }
119
120 fn collect_methods(
121 &self,
122 class: &'i Class,
123 name: &str,
124 out: &mut Vec<&'i Member>,
125 seen: &mut Vec<&'i str>,
126 ) {
127 if seen.contains(&class.name.as_str()) {
128 return;
129 }
130 seen.push(&class.name);
131 for m in class.methods.iter().filter(|m| m.name == name) {
132 if !out.iter().any(|o| o.desc == m.desc) {
133 out.push(m);
134 }
135 }
136 let parents = class.extends.iter().chain(&class.implements);
137 for p in parents.filter_map(|p| self.class(p)) {
138 self.collect_methods(p, name, out, seen);
139 }
140 }
141
142 pub fn field(&self, class: &'i Class, name: &str) -> Option<&'i Member> {
144 self.ancestry(class)
145 .into_iter()
146 .find_map(|c| c.fields.iter().find(|f| f.name == name))
147 }
148}