actix_admin/
builder.rs

1use crate::{prelude::*, routes::{delete_file, display_card_grid, export_csv, search}, ActixAdminMenuElement};
2use actix_web::{web, Route };
3use std::collections::HashMap;
4use std::fs;
5use crate::routes::{
6    create_get, create_post, delete, delete_many, edit_get, edit_post, index, list, not_found, show, download
7};
8
9/// Represents a builder entity which helps generating the ActixAdmin configuration
10pub struct ActixAdminBuilder {
11    pub scopes: HashMap<String, actix_web::Scope>,
12    pub custom_routes: Vec<(String, Route)>,
13    pub actix_admin: ActixAdmin,
14    pub custom_index: Option<Route>,
15}
16
17/// The trait to work with ActixAdminBuilder
18pub trait ActixAdminBuilderTrait {
19    fn new(configuration: ActixAdminConfiguration) -> Self;
20    fn add_entity<E: ActixAdminViewModelTrait + 'static>(
21        &mut self,
22        view_model: &ActixAdminViewModel,
23    );
24    fn add_entity_to_category<
25        E: ActixAdminViewModelTrait + 'static,
26    >(
27        &mut self,
28        view_model: &ActixAdminViewModel,
29        category_name: &str,
30    );
31    fn add_custom_handler(
32        &mut self,
33        menu_element_name: &str,
34        path: &str,
35        route: Route,
36        add_to_menu: bool,
37    );
38    fn add_custom_handler_to_category(
39        &mut self,
40        menu_element_name: &str,
41        path: &str,
42        route: Route,
43        add_to_menu: bool,
44        category: &str
45    );
46    fn add_card_grid(
47        &mut self,
48        menu_element_name: &str,
49        path: &str,
50        elements: Vec<Vec<String>>,
51        add_to_menu: bool,
52    );
53    fn add_card_grid_to_category(
54        &mut self,
55        menu_element_name: &str,
56        path: &str,
57        elements: Vec<Vec<String>>,
58        add_to_menu: bool,
59        category: &str
60    );
61    fn add_custom_handler_for_entity<
62        E: ActixAdminViewModelTrait + 'static,
63    >(
64        &mut self,
65        menu_element_name: &str,
66        path: &str,
67        route: Route,
68        add_to_menu: bool
69    );
70    fn add_custom_handler_for_entity_in_category<
71        E: ActixAdminViewModelTrait + 'static,
72    >(
73        &mut self,
74        menu_element_name: &str,
75        path: &str,
76        route: Route,
77        category_name: &str,
78        add_to_menu: bool,
79    );
80    fn add_custom_handler_for_index(&mut self, route: Route);
81    fn get_scope(self) -> actix_web::Scope;
82    fn get_actix_admin(&self) -> ActixAdmin;
83    fn add_support_handler(&mut self, arg: &str, support: Route);
84}
85
86impl ActixAdminBuilderTrait for ActixAdminBuilder {
87    fn new(configuration: ActixAdminConfiguration) -> Self {
88        ActixAdminBuilder {
89            actix_admin: ActixAdmin {
90                entity_names: HashMap::new(),
91                view_models: HashMap::new(),
92                card_grids: HashMap::new(),
93                configuration: configuration,
94                tera: crate::tera_templates::get_tera(),
95                support_path: None
96            },
97            custom_routes: Vec::new(),
98            scopes: HashMap::new(),
99            custom_index: None,
100        }
101    }
102
103    fn add_entity<E: ActixAdminViewModelTrait + 'static>(
104        &mut self,
105        view_model: &ActixAdminViewModel,
106    ) {
107        let _ = &self.add_entity_to_category::<E>(view_model, "");
108    }
109
110    fn add_entity_to_category<
111        E: ActixAdminViewModelTrait + 'static,
112    >(
113        &mut self,
114        view_model: &ActixAdminViewModel,
115        category_name: &str,
116    ) {
117        self.scopes.insert(
118            E::get_entity_name(),
119            web::scope(&format!("/{}", E::get_entity_name()))
120                .route("/list", web::get().to(list::<E>))
121                .route("/export_csv", web::get().to(export_csv::<E>))
122                .route("/create", web::get().to(create_get::<E>))
123                .route("/search", web::get().to(search::<E>))
124                .route("/create", web::post().to(create_post::<E>))
125                .route("/edit/{id}", web::get().to(edit_get::<E>))
126                .route("/edit/{id}", web::post().to(edit_post::<E>))
127                .route("/delete", web::delete().to(delete_many::<E>))
128                .route("/delete/{id}", web::delete().to(delete::<E>))
129                .route("/show/{id}", web::get().to(show::<E>))
130                .route("/file/{id}/{column_name}", web::get().to(download::<E>))
131                .route("/file/{id}/{column_name}", web::delete().to(delete_file::<E>))
132                .default_service(web::to(not_found))
133            );
134
135        fs::create_dir_all(format!("{}/{}", &self.actix_admin.configuration.file_upload_directory, E::get_entity_name())).unwrap();
136
137        let category = self.actix_admin.entity_names.get_mut(category_name);
138        let menu_element = ActixAdminMenuElement {
139            name: E::get_entity_name(),
140            link: E::get_entity_name(),
141            is_custom_handler: false,
142        };
143        match category {
144            Some(entity_list) => entity_list.push(menu_element),
145            None => {
146                let mut entity_list = Vec::new();
147                entity_list.push(menu_element);
148                self.actix_admin
149                    .entity_names
150                    .insert(category_name.to_string(), entity_list);
151            }
152        }
153
154        let key = E::get_entity_name();
155        self.actix_admin.view_models.insert(key, view_model.clone());
156    }
157
158    fn add_custom_handler_for_index(&mut self, route: Route) {
159        self.custom_index = Some(route);
160    }
161
162    fn add_custom_handler_to_category(
163        &mut self,
164        menu_element_name: &str,
165        path: &str,
166        route: Route,
167        add_to_menu: bool,
168        category_name: &str
169    ) {
170        self.custom_routes.push((path.to_string(), route));
171
172        if add_to_menu {
173            let menu_element = ActixAdminMenuElement {
174                name: menu_element_name.to_string(),
175                link: path.replacen("/", "", 1),
176                is_custom_handler: true,
177            };
178            let category = self.actix_admin.entity_names.get_mut(category_name);
179            match category {
180                Some(entity_list) => {
181                    if !entity_list.contains(&menu_element) {
182                        entity_list.push(menu_element);
183                    }
184                }
185                None => {
186                    let mut entity_list = Vec::new();
187                    entity_list.push(menu_element);
188                    self.actix_admin
189                        .entity_names
190                        .insert(category_name.to_string(), entity_list);
191                },
192            }
193        }
194    }
195
196    fn add_card_grid(
197        &mut self,
198        menu_element_name: &str,
199        path: &str,
200        elements: Vec<Vec<String>>,
201        add_to_menu: bool,
202    ) {
203        self.add_card_grid_to_category(menu_element_name, path, elements, add_to_menu, "");
204    }
205
206    fn add_card_grid_to_category(
207        &mut self,
208        menu_element_name: &str,
209        path: &str,
210        elements: Vec<Vec<String>>,
211        add_to_menu: bool,
212        category_name: &str
213    ) {
214        self.custom_routes.push((path.to_string(), web::get().to(display_card_grid)));
215        self.actix_admin.card_grids.insert(path.to_string().replace("/", ""), elements);
216
217        if add_to_menu {
218            let menu_element = ActixAdminMenuElement {
219                name: menu_element_name.to_string(),
220                link: path.replacen("/", "", 1),
221                is_custom_handler: true,
222            };
223            let category = self.actix_admin.entity_names.get_mut(category_name);
224            match category {
225                Some(entity_list) => {
226                    if !entity_list.contains(&menu_element) {
227                        entity_list.push(menu_element);
228                    }
229                }
230                None => {
231                    let mut entity_list = Vec::new();
232                    entity_list.push(menu_element);
233                    self.actix_admin
234                        .entity_names
235                        .insert(category_name.to_string(), entity_list);
236                },
237            }
238        }
239    }
240
241    fn add_custom_handler(
242        &mut self,
243        menu_element_name: &str,
244        path: &str,
245        route: Route,
246        add_to_menu: bool
247    ) {
248        self.add_custom_handler_to_category(menu_element_name, path, route, add_to_menu, "");
249    }
250
251    fn add_support_handler(&mut self, arg: &str, support: Route) {
252        self.custom_routes.push((arg.to_string(), support));
253        self.actix_admin.support_path = Some(arg.to_string().replace("/", ""));
254    }
255
256    fn add_custom_handler_for_entity<
257        E: ActixAdminViewModelTrait + 'static,
258    >(
259        &mut self,
260        menu_element_name: &str,
261        path: &str,
262        route: Route,
263        add_to_menu: bool,
264    ) {
265        let _ = &self.add_custom_handler_for_entity_in_category::<E>(
266            menu_element_name,
267            path,
268            route,
269            "",
270            add_to_menu,
271        );
272    }
273
274    fn add_custom_handler_for_entity_in_category<
275        E: ActixAdminViewModelTrait + 'static,
276    >(
277        &mut self,
278        menu_element_name: &str,
279        path: &str,
280        route: Route,
281        category_name: &str,
282        add_to_menu: bool,
283    ) {
284        let menu_element = ActixAdminMenuElement {
285            name: menu_element_name.to_string(),
286            link: format!("{}{}", E::get_entity_name(), path),
287            is_custom_handler: true,
288        };
289
290        let existing_scope = self.scopes.remove(&E::get_entity_name());
291
292        match existing_scope {
293            Some(scope) => {
294                let existing_scope = scope.route(path, route);
295                self.scopes
296                    .insert(E::get_entity_name(), existing_scope);
297            }
298            _ => {
299                let new_scope =
300                    web::scope(&format!("/{}", E::get_entity_name())).route(path, route);
301                self.scopes.insert(E::get_entity_name(), new_scope);
302            }
303        }
304
305        if add_to_menu {
306            let category = self.actix_admin.entity_names.get_mut(category_name);
307            match category {
308                Some(entity_list) => {
309                    if !entity_list.contains(&menu_element) {
310                        entity_list.push(menu_element);
311                    }
312                }
313                _ => (),
314            }
315        }
316    }
317
318    fn get_scope(self) -> actix_web::Scope {
319        let index_handler = match self.custom_index {
320            Some(handler) => handler,
321            _ => web::get().to(index),
322        };
323        let mut admin_scope = web::scope(&self.actix_admin.configuration.base_path)
324            .route("/", index_handler)
325            .default_service(web::to(not_found));
326
327        for (_entity, scope) in self.scopes {
328            admin_scope = admin_scope.service(scope);
329        }
330
331        for (path, route) in self.custom_routes {
332            admin_scope = admin_scope.route(&path, route);
333        }
334
335        admin_scope
336    }
337
338    fn get_actix_admin(&self) -> ActixAdmin {
339        self.actix_admin.clone()
340    }
341}