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;
50
51pub struct Lookup<'i> {
54 by_name: HashMap<&'i str, &'i Class>,
55 by_simple: HashMap<&'i str, Vec<&'i Class>>,
56}
57
58impl ClassIndex {
59 pub fn lookup(&self) -> Lookup<'_> {
60 let mut by_name = HashMap::new();
61 let mut by_simple: HashMap<&str, Vec<&Class>> = HashMap::new();
62 for c in &self.classes {
63 by_name.insert(c.name.as_str(), c);
64 by_simple.entry(simple_name(&c.name)).or_default().push(c);
65 }
66 Lookup { by_name, by_simple }
67 }
68}
69
70pub fn simple_name(internal: &str) -> &str {
73 let tail = internal.rsplit('/').next().unwrap_or(internal);
74 tail.rsplit('$').next().unwrap_or(tail)
75}
76
77impl<'i> Lookup<'i> {
78 pub fn class(&self, name: &str) -> Option<&'i Class> {
80 self.by_name.get(name).copied()
81 }
82
83 pub fn simple(&self, name: &str) -> &[&'i Class] {
85 self.by_simple
86 .get(name)
87 .map(Vec::as_slice)
88 .unwrap_or_default()
89 }
90
91 pub fn ancestry(&self, class: &'i Class) -> Vec<&'i Class> {
93 let mut out = vec![class];
94 while let Some(parent) = out
95 .last()
96 .and_then(|c| c.extends.as_deref())
97 .and_then(|p| self.class(p))
98 {
99 if out.iter().any(|c| c.name == parent.name) {
100 break;
101 }
102 out.push(parent);
103 }
104 out
105 }
106
107 pub fn methods(&self, class: &'i Class, name: &str) -> Vec<&'i Member> {
110 let mut out = Vec::new();
111 let mut seen = Vec::new();
112 self.collect_methods(class, name, &mut out, &mut seen);
113 out
114 }
115
116 fn collect_methods(
117 &self,
118 class: &'i Class,
119 name: &str,
120 out: &mut Vec<&'i Member>,
121 seen: &mut Vec<&'i str>,
122 ) {
123 if seen.contains(&class.name.as_str()) {
124 return;
125 }
126 seen.push(&class.name);
127 for m in class.methods.iter().filter(|m| m.name == name) {
128 if !out.iter().any(|o| o.desc == m.desc) {
129 out.push(m);
130 }
131 }
132 let parents = class.extends.iter().chain(&class.implements);
133 for p in parents.filter_map(|p| self.class(p)) {
134 self.collect_methods(p, name, out, seen);
135 }
136 }
137
138 pub fn field(&self, class: &'i Class, name: &str) -> Option<&'i Member> {
140 self.ancestry(class)
141 .into_iter()
142 .find_map(|c| c.fields.iter().find(|f| f.name == name))
143 }
144}