1use tree_sitter::Node;
5
6use super::{named_children, text, RawContract};
7use crate::facts::{Args, ValueExpr};
8
9const MAPPINGS: &[(&str, &str)] = &[
10 ("GetMapping", "GET"),
11 ("PostMapping", "POST"),
12 ("PutMapping", "PUT"),
13 ("PatchMapping", "PATCH"),
14 ("DeleteMapping", "DELETE"),
15];
16
17pub(crate) const REST_TEMPLATE: &[(&str, &str)] = &[
19 ("getForObject", "GET"),
20 ("getForEntity", "GET"),
21 ("postForObject", "POST"),
22 ("postForEntity", "POST"),
23 ("postForLocation", "POST"),
24 ("patchForObject", "PATCH"),
25 ("exchange", ""),
26];
27
28pub(crate) const WEBCLIENT: &str = "<webclient>";
30const VERBS: &[&str] = &["get", "post", "put", "patch", "delete"];
31
32pub(crate) fn webclient_verb(inner_method: &str) -> Option<String> {
34 VERBS
35 .contains(&inner_method)
36 .then(|| inner_method.to_uppercase())
37}
38
39struct Annotation<'t> {
40 name: String,
41 args: Vec<(Option<String>, Node<'t>)>,
42}
43
44pub(crate) fn route(def: Node, src: &[u8]) -> Option<RawContract> {
46 if !matches!(def.kind(), "method_declaration" | "function_declaration") {
47 return None;
48 }
49 let anns = annotations(def, src);
50 let (verb, path) = anns.iter().find_map(|a| {
51 if let Some((_, v)) = MAPPINGS.iter().find(|(n, _)| *n == a.name) {
52 return Some((v.to_string(), mapping_path(a, src)));
53 }
54 if a.name == "RequestMapping" {
55 let verb = arg(a, "method", usize::MAX)
56 .and_then(|n| value(n, src))
57 .and_then(|v| match v {
58 ValueExpr::Name(p) => p.last().cloned(),
59 _ => None,
60 })
61 .unwrap_or_else(|| "ANY".into());
62 return Some((verb, mapping_path(a, src)));
63 }
64 None
65 })?;
66 let class = def
67 .parent()
68 .filter(|b| matches!(b.kind(), "class_body" | "interface_body"))
69 .and_then(|b| b.parent())?;
70 let class_anns = annotations(class, src);
71 let client = class_anns.iter().any(|a| a.name == "FeignClient");
72 let prefix = class_anns.iter().find_map(|a| match a.name.as_str() {
73 "RequestMapping" => Some(mapping_path(a, src)),
74 "FeignClient" => arg(a, "path", usize::MAX).and_then(|n| value(n, src)),
75 _ => None,
76 });
77 let handler = text(
78 def.child_by_field_name("name").or_else(|| name_of(def))?,
79 src,
80 );
81 Some(RawContract::Route {
82 handler: handler.to_string(),
83 receiver: Vec::new(),
84 method: verb.to_uppercase(),
85 path: ValueExpr::Join {
86 sep: String::new(),
87 parts: vec![prefix.unwrap_or(ValueExpr::Lit(String::new())), path],
88 },
89 client,
90 })
91}
92
93fn name_of(def: Node) -> Option<Node> {
94 named_children(def)
95 .into_iter()
96 .find(|c| matches!(c.kind(), "identifier" | "simple_identifier"))
97}
98
99fn mapping_path(a: &Annotation<'_>, src: &[u8]) -> ValueExpr {
100 arg(a, "value", 0)
101 .or_else(|| arg(a, "path", usize::MAX))
102 .and_then(|n| value(n, src))
103 .unwrap_or(ValueExpr::Lit(String::new()))
104}
105
106fn arg<'t>(a: &Annotation<'t>, key: &str, pos: usize) -> Option<Node<'t>> {
107 a.args
108 .iter()
109 .find(|(k, _)| k.as_deref() == Some(key))
110 .or_else(|| a.args.iter().filter(|(k, _)| k.is_none()).nth(pos))
111 .map(|(_, n)| *n)
112}
113
114fn annotations<'t>(def: Node<'t>, src: &[u8]) -> Vec<Annotation<'t>> {
115 let mut out = Vec::new();
116 for m in named_children(def)
117 .into_iter()
118 .filter(|c| c.kind() == "modifiers")
119 {
120 for a in named_children(m) {
121 if let Some(ann) = annotation(a, src) {
122 out.push(ann);
123 }
124 }
125 }
126 out
127}
128
129fn annotation<'t>(a: Node<'t>, src: &[u8]) -> Option<Annotation<'t>> {
130 match a.kind() {
131 "marker_annotation" | "annotation" if a.child_by_field_name("name").is_some() => {
133 let name = last(text(a.child_by_field_name("name")?, src));
134 let mut args = Vec::new();
135 if let Some(list) = a.child_by_field_name("arguments") {
136 for c in named_children(list) {
137 if c.kind() == "element_value_pair" {
138 let k = c
139 .child_by_field_name("key")
140 .map(|k| text(k, src).to_string());
141 if let Some(v) = c.child_by_field_name("value") {
142 args.push((k, v));
143 }
144 } else {
145 args.push((None, c));
146 }
147 }
148 }
149 Some(Annotation { name, args })
150 }
151 "annotation" => {
153 let inner = named_children(a).into_iter().next()?;
154 match inner.kind() {
155 "user_type" => Some(Annotation {
156 name: last(text(inner, src)),
157 args: Vec::new(),
158 }),
159 "constructor_invocation" => {
160 let kids = named_children(inner);
161 let name = last(text(*kids.first()?, src));
162 let mut args = Vec::new();
163 if let Some(list) = kids.iter().find(|k| k.kind() == "value_arguments") {
164 for va in named_children(*list) {
165 let parts = named_children(va);
166 match parts.as_slice() {
167 [k, v] if k.kind() == "identifier" => {
168 args.push((Some(text(*k, src).to_string()), *v))
169 }
170 [v] => args.push((None, *v)),
171 _ => {}
172 }
173 }
174 }
175 Some(Annotation { name, args })
176 }
177 _ => None,
178 }
179 }
180 _ => None,
181 }
182}
183
184fn last(s: &str) -> String {
185 s.rsplit('.').next().unwrap_or(s).trim().to_string()
186}
187
188pub(crate) fn value(node: Node, src: &[u8]) -> Option<ValueExpr> {
191 match node.kind() {
192 "string_literal" => {
193 let kids = named_children(node);
194 if kids
195 .iter()
196 .all(|k| matches!(k.kind(), "string_fragment" | "string_content"))
197 {
198 return Some(ValueExpr::Lit(
199 kids.iter().map(|k| text(*k, src)).collect::<String>(),
200 ));
201 }
202 Some(ValueExpr::Join {
203 sep: String::new(),
204 parts: kids
205 .iter()
206 .map(|k| match k.kind() {
207 "string_fragment" | "string_content" => {
208 ValueExpr::Lit(text(*k, src).to_string())
209 }
210 _ => named_children(*k)
211 .first()
212 .and_then(|e| value(*e, src))
213 .unwrap_or(ValueExpr::Name(Vec::new())),
214 })
215 .collect(),
216 })
217 }
218 "identifier" | "simple_identifier" => Some(ValueExpr::Name(vec![text(node, src).into()])),
219 "field_access" | "navigation_expression" => Some(ValueExpr::Name(
220 text(node, src)
221 .split('.')
222 .map(|s| s.trim().to_string())
223 .collect(),
224 )),
225 "binary_expression" | "additive_expression" => {
226 let kids = named_children(node);
227 if !text(node, src).contains('+') || kids.len() != 2 {
228 return None;
229 }
230 Some(ValueExpr::Join {
231 sep: String::new(),
232 parts: kids
233 .iter()
234 .map(|k| value(*k, src).unwrap_or(ValueExpr::Name(Vec::new())))
235 .collect(),
236 })
237 }
238 "element_value_array_initializer" | "collection_literal" | "array_initializer" => {
239 value(*named_children(node).first()?, src)
240 }
241 "parenthesized_expression" => value(*named_children(node).first()?, src),
242 _ => None,
243 }
244}
245
246pub(crate) fn args(list: Node, src: &[u8]) -> Args {
248 let mut out = Args::default();
249 for a in named_children(list) {
250 let v = if a.kind() == "value_argument" {
251 named_children(a).last().copied()
252 } else {
253 Some(a)
254 };
255 out.positional.push(v.and_then(|v| value(v, src)));
256 }
257 out
258}