1use crate::{
2 ra_ap_syntax::{
3 ast::{self, Expr, HasName, NameOrNameRef},
4 AstNode,
5 },
6 semver::{Error as SemVerError, Version as SemverVersion},
7 utils::{normalize, INTERNAL_ERR},
8 Semantics, Upgrader,
9};
10
11use anyhow::Result as AnyResult;
12use paste::paste;
13
14use std::{collections::HashMap as Map, ops::Deref};
15
16pub(crate) type Hook<T> = Box<dyn Fn(&mut Upgrader, &T, &Semantics)>;
17
18pub(crate) struct Hooks<T>(Map<String, Map<String, Vec<Hook<T>>>>);
19
20impl<T> Default for Hooks<T> {
21 fn default() -> Self {
22 Self(Map::new())
23 }
24}
25
26impl<T> Deref for Hooks<T> {
27 type Target = Map<String, Map<String, Vec<Hook<T>>>>;
28
29 fn deref(&self) -> &Self::Target {
30 &self.0
31 }
32}
33
34impl<T> Hooks<T> {
35 pub(crate) fn insert(&mut self, path: &str, name: &str, hook: Hook<T>) {
36 let path = path.to_string();
37 let name = name.to_string();
38
39 if !self.0.contains_key(&path) {
40 self.0.insert(path.clone(), Map::new());
41 }
42
43 let hook_map = self.0.get_mut(&path).expect(INTERNAL_ERR);
44
45 if !hook_map.contains_key(&name) {
46 hook_map.insert(name.clone(), vec![]);
47 }
48
49 hook_map.get_mut(&name).expect(INTERNAL_ERR).push(hook);
50 }
51}
52
53macro_rules! members {
54 ($($node:ident,)*) => {
55 paste! {
56 pub struct Version {
57 pub(crate) version: SemverVersion,
58 pub(crate) peers: Vec<String>,
59 pub(crate) init: Option<Box<dyn Fn(&mut Upgrader, &SemverVersion) -> AnyResult<()>>>,
60 $(
61 pub(crate) [<hook_ $node:snake>]: Vec<Hook<ast::$node>>,
62 pub(crate) [<hook_ $node:snake _on>]: Hooks<ast::$node>,
63 )*
64 }
65
66 impl Version {
67 pub fn new(version: &str) -> Result<Self, SemVerError> {
68 Ok(Self {
69 version: SemverVersion::parse(version)?,
70 peers: vec![],
71 init: None,
72 $(
73 [<hook_ $node:snake>]: Vec::new(),
74 [<hook_ $node:snake _on>]: Hooks::default(),
75 )*
76 })
77 }
78 }
79 }
80 };
81}
82
83macro_rules! methods {
84 ($($node:ident,)*) => {
85 paste! {
86 $(
87 pub fn [<hook_ $node:snake>]<F>(mut self, f: F) -> Self
88 where
89 F: Fn(&mut Upgrader, &ast::$node, &Semantics) + 'static,
90 {
91 self.[<hook_ $node:snake>].push(Box::new(f));
92 self
93 }
94
95 pub fn [<hook_ $node:snake _on>]<F>(mut self, path: &str, name: &str, f: F) -> Self
96 where
97 F: Fn(&mut Upgrader, &ast::$node, &Semantics) + 'static
98 {
99 self.[<hook_ $node:snake _on>].insert(path, name, Box::new(f));
100 self
101 }
102 )*
103 }
104 };
105}
106
107members!(
108 MethodCallExpr,
109 CallExpr,
110 IdentPat,
111 Path,
112 PathExpr,
113 PathPat,
114 FieldExpr,
115 RecordPat,
116 RecordExpr,
117 RecordExprField,
118 RecordPatField,
119 TupleStructPat,
120);
121
122impl Version {
123 pub fn peers(mut self, peers: &[&str]) -> Self {
124 self.peers = peers.to_vec().iter().map(|x| normalize(*x)).collect();
125 self
126 }
127
128 pub fn init<F>(mut self, init: F) -> Self
129 where
130 F: Fn(&mut Upgrader, &SemverVersion) -> AnyResult<()> + 'static,
131 {
132 self.init = Some(Box::new(init));
133 self
134 }
135
136 pub fn rename_structs(mut self, name: &str, map: &'static [[&str; 2]]) -> Self {
137 for rename in map.into_iter() {
138 self = self.hook_path_on(name, rename[0], move |u, n, _| {
139 u.replace(n.segment(), rename[1]);
140 })
141 }
142
143 self
144 }
145
146 pub fn rename_methods(mut self, name: &str, map: &'static [[&str; 2]]) -> Self {
147 for rename in map.into_iter() {
148 self = self
149 .hook_method_call_expr_on(name, rename[0], move |u, n, _| {
150 u.replace(n.name_ref(), rename[1]);
151 })
152 .hook_path_expr_on(name, rename[0], move |u, n, _| {
153 u.replace(n.path(), rename[1]);
154 });
155 }
156
157 self
158 }
159
160 pub fn rename_members(mut self, name: &str, map: &'static [[&str; 2]]) -> Self {
161 for rename in map.into_iter() {
162 self = self
163 .hook_field_expr_on(name, rename[0], move |u, n, _| {
164 u.replace(n.name_ref(), rename[1]);
165 })
166 .hook_record_pat_field_on(name, rename[0], move |u, n, _| match n.field_name() {
167 Some(NameOrNameRef::Name(_)) => {
168 u.replace(n.syntax().text_range(), format!("{}: {}", rename[1], n))
169 }
170 Some(NameOrNameRef::NameRef(name_ref)) => {
171 u.replace(name_ref.syntax().text_range(), rename[1])
172 }
173 _ => {}
174 })
175 .hook_record_expr_field_on(name, rename[0], move |u, n, _| {
176 if let Some(name_ref) = n.name_ref() {
177 u.replace(name_ref.syntax().text_range(), rename[1]);
178 } else if let Some(Expr::PathExpr(path_expr)) = n.expr() {
179 u.replace(
180 path_expr.syntax().text_range(),
181 format!("{}: {}", rename[1], rename[0]),
182 );
183 }
184 });
185 }
186
187 self
188 }
189
190 pub fn rename_variants(mut self, name: &str, map: &'static [[&str; 2]]) -> Self {
191 for rename in map.into_iter() {
192 self = self
193 .hook_path_expr_on(name, rename[0], move |u, n, _| {
194 u.replace(n.path(), rename[1]);
195 })
196 .hook_path_pat_on(name, rename[0], move |u, n, _| {
197 u.replace(n.path(), rename[1]);
198 })
199 .hook_record_pat_on(name, rename[0], move |u, n, _| {
200 u.replace(n.path(), rename[1]);
201 })
202 .hook_record_expr_on(name, rename[0], move |u, n, _| {
203 u.replace(n.path(), rename[1]);
204 })
205 .hook_tuple_struct_pat_on(name, rename[0], move |u, n, _| {
206 u.replace(n.path(), rename[1]);
207 })
208 .hook_ident_pat_on(name, rename[0], move |u, n, _| {
209 u.replace(n.name(), rename[1]);
210 });
211 }
212
213 self
214 }
215}
216
217impl Version {
218 methods!(
219 MethodCallExpr,
220 CallExpr,
221 IdentPat,
222 Path,
223 PathExpr,
224 PathPat,
225 FieldExpr,
226 RecordPat,
227 RecordExpr,
228 RecordExprField,
229 RecordPatField,
230 TupleStructPat,
231 );
232}