cargo_up/
version.rs

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}