rapid_cli/commands/
routes.rs

1use super::RapidCommand;
2use crate::{cli::Config, tui::{logo, chevrons, clean_console}, rapid_config::config::{find_rapid_config, ServerConfig}};
3use clap::{ArgMatches, Command};
4use walkdir::WalkDir;
5use std::fs::File;
6use std::io::Read;
7use colorful::{Color, Colorful};
8use regex::Regex;
9
10pub const REMIX_ROUTE_PATH: &'static str = "app/api/routes";
11pub const NEXTJS_ROUTE_PATH: &'static str = "pages/api/routes";
12
13// `rapid routes` command for showing all the route rust files and they map to in url form
14pub struct Routes {}
15
16impl RapidCommand for Routes {
17	fn cmd() -> clap::Command {
18		Command::new("routes").about("Show all the routes in your rapid project!")
19	}
20
21	fn execute(_: &Config, _args: &ArgMatches) -> Result<(), crate::cli::CliError<'static>> {
22		println!("{}", logo());
23		// Iterate over all the routes in the project and output a map of route file paths to actual http urls:
24		let config = find_rapid_config();
25		let routes_dir = match config.app_type.as_str() {
26			"server" => get_routes_dir(config.server.as_ref()),
27			"remix" => REMIX_ROUTE_PATH.to_owned(),
28			_ => NEXTJS_ROUTE_PATH.to_owned(),
29		};
30		generate_routes(&routes_dir);
31		Ok(())
32	}
33}
34
35pub fn generate_routes(routes_dir: &str) {
36	let mut routes = vec![];
37	for route_file in WalkDir::new(routes_dir.clone()) {
38		let entry = match route_file {
39			Ok(val) => val,
40			Err(e) => panic!("An error occurred what attempting to parse directory: {}", e),
41		};
42
43		// We only want to handle route files and no directories (the walkDir crate auto iterates through nested dirs)
44		if entry.path().is_dir() {
45			continue;
46		}
47
48		// Create a reference to the current route file and grab its contents as a string
49		let mut file = File::open(&entry.path()).unwrap();
50		let mut route_file_contents = String::new();
51		file.read_to_string(&mut route_file_contents).unwrap();
52
53		let file_name = entry.file_name();
54
55		// Make sure we ignore middleware and mod files from route generation
56		if file_name == "_middleware.rs" || file_name == "mod.rs" {
57			continue;
58		}
59
60		let parsed_route_dir = entry
61			.path()
62			.to_str()
63			.unwrap_or("/")
64			.to_string()
65			.replace(routes_dir, "");
66
67		routes.push(parsed_route_dir);
68	}
69
70	let total_routes_count = routes.len();
71
72	// Clean the console before printing the routes...
73	clean_console();
74	println!();
75
76	for route in routes {
77		let route_url = remove_last_occurrence(&route.replace(".rs", ""), "index");
78		let route_path = format!("{}{}", routes_dir, route);
79		let dynamic_route_regex = Regex::new(r"_(.*?)_").unwrap();
80		println!("{} {} {}\n", route_path, "➜".color(Color::LightCyan).bold(), dynamic_route_regex.replace_all(&route_url, "{$1}").bold());
81	}
82
83	println!("{} Found {} routes in your project\n", chevrons(), total_routes_count.to_string().color(Color::Blue).bold());
84}
85
86pub fn remove_last_occurrence(s: &str, sub: &str) -> String {
87	let mut split = s.rsplitn(2, sub);
88	let back = split.next().unwrap_or("");
89	let front = split.next().unwrap_or("").to_owned();
90	front + back
91}
92
93pub fn get_routes_dir(rapid_server_config: Option<&ServerConfig>) -> String {
94	match rapid_server_config {
95		Some(server) => match server.routes_directory.clone() {
96			Some(dir) => match dir == "/" {
97				true => panic!("The 'routes_directory' variable cannot be set to a base path. Please use something nested!"),
98				false => dir,
99			},
100			None => panic!("Error: the 'routes_directory' variable must be set in your rapid config file!"),
101		},
102		None => panic!("You must have a valid rapid config file in the base project directory!"),
103	}
104}