1use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
11#[serde(rename_all = "PascalCase")]
12pub enum NodeKind {
13 Expr,
16 Literal,
18 Path,
20 MethodCall,
22 FunctionCall,
24 MacroCall,
26 BinaryOp,
28 UnaryOp,
30 If,
32 Match,
34 Loop,
36 Block,
38 Closure,
40 Await,
42 Try,
44 Return,
46 Index,
48
49 Function,
52 Struct,
54 Enum,
56 Trait,
58 Impl,
60 Mod,
62 Use,
64 Const,
66 Static,
68 TypeAlias,
70
71 Field,
74 Variant,
76 Param,
78 Arg,
80 GenericArg,
82 Lifetime,
84 Attribute,
86
87 LetExpr,
90 Wildcard,
92 Unit,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
98#[serde(untagged)]
99pub enum NameMatcher {
100 Exact(String),
102 Pattern(NamePattern),
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
108pub struct NamePattern {
109 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub glob: Option<String>,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub regex: Option<String>,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub starts_with: Option<String>,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub ends_with: Option<String>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub contains: Option<String>,
124}
125
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
128#[serde(untagged)]
129pub enum PatternExpr {
130 Pattern(Box<CodePattern>),
132 Name(NameMatcher),
134 Literal(serde_json::Value),
136 Wildcard,
138 Capture(String),
140}
141
142#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
144pub struct ArmPattern {
145 #[serde(default, skip_serializing_if = "Option::is_none")]
147 pub pattern_path: Option<String>,
148
149 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub body: Option<Box<CodePattern>>,
152}
153
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
156pub struct CodePattern {
157 pub node: NodeKind,
159
160 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub arm_count: Option<usize>,
163
164 #[serde(default, skip_serializing_if = "Option::is_none")]
166 pub arms: Option<Vec<ArmPattern>>,
167
168 #[serde(flatten)]
170 pub children: HashMap<String, PatternExpr>,
171
172 #[serde(default, skip_serializing_if = "Option::is_none")]
174 pub capture: Option<String>,
175
176 #[serde(default)]
178 pub ellipsis: bool,
179}
180
181impl CodePattern {
182 pub fn new(node: NodeKind) -> Self {
184 Self {
185 node,
186 arm_count: None,
187 arms: None,
188 children: HashMap::new(),
189 capture: None,
190 ellipsis: false,
191 }
192 }
193
194 pub fn with_child(mut self, name: impl Into<String>, pattern: PatternExpr) -> Self {
196 self.children.insert(name.into(), pattern);
197 self
198 }
199
200 pub fn with_capture(mut self, var: impl Into<String>) -> Self {
202 self.capture = Some(var.into());
203 self
204 }
205
206 pub fn with_ellipsis(mut self) -> Self {
208 self.ellipsis = true;
209 self
210 }
211}
212
213#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
215pub struct BodyMatch {
216 #[serde(default, skip_serializing_if = "Option::is_none")]
218 pub contains: Option<Vec<CodePattern>>,
219
220 #[serde(default, skip_serializing_if = "Option::is_none")]
222 pub not_contains: Option<Vec<CodePattern>>,
223
224 #[serde(default, skip_serializing_if = "Option::is_none")]
226 pub all_of: Option<Vec<CodePattern>>,
227}
228
229impl BodyMatch {
230 pub fn new() -> Self {
232 Self::default()
233 }
234
235 pub fn contains(mut self, pattern: CodePattern) -> Self {
237 self.contains.get_or_insert_with(Vec::new).push(pattern);
238 self
239 }
240
241 pub fn not_contains(mut self, pattern: CodePattern) -> Self {
243 self.not_contains.get_or_insert_with(Vec::new).push(pattern);
244 self
245 }
246
247 pub fn all_of(mut self, pattern: CodePattern) -> Self {
249 self.all_of.get_or_insert_with(Vec::new).push(pattern);
250 self
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_code_pattern_builder() {
260 let pattern = CodePattern::new(NodeKind::MethodCall)
261 .with_child(
262 "method",
263 PatternExpr::Name(NameMatcher::Exact("unwrap".into())),
264 )
265 .with_capture("$UNWRAP");
266
267 assert_eq!(pattern.node, NodeKind::MethodCall);
268 assert_eq!(pattern.capture, Some("$UNWRAP".to_string()));
269 assert!(pattern.children.contains_key("method"));
270 }
271
272 #[test]
273 fn test_body_match_builder() {
274 let body = BodyMatch::new()
275 .contains(CodePattern::new(NodeKind::MethodCall))
276 .not_contains(CodePattern::new(NodeKind::MacroCall));
277
278 assert!(body.contains.is_some());
279 assert!(body.not_contains.is_some());
280 assert!(body.all_of.is_none());
281 }
282
283 #[test]
284 fn test_serialize_deserialize() {
285 let pattern = CodePattern::new(NodeKind::MethodCall).with_child(
286 "method",
287 PatternExpr::Name(NameMatcher::Exact("unwrap".into())),
288 );
289
290 let json = serde_json::to_string(&pattern).unwrap();
291 let deserialized: CodePattern = serde_json::from_str(&json).unwrap();
292
293 assert_eq!(pattern.node, deserialized.node);
294 }
295}