axum-folder-router/src/parse.rs

98 lines
2.6 KiB
Rust

use std::{
fs,
path::{Path, PathBuf},
};
use syn::{
parse::{Parse, ParseStream},
parse_file,
Ident,
Item,
LitStr,
Result,
Token,
Visibility,
};
#[derive(Debug)]
pub struct FolderRouterArgs {
pub path: String,
pub state_type: Ident,
}
impl Parse for FolderRouterArgs {
fn parse(input: ParseStream) -> Result<Self> {
let path_lit = input.parse::<LitStr>()?;
input.parse::<Token![,]>()?;
let state_type = input.parse::<Ident>()?;
Ok(FolderRouterArgs {
path: path_lit.value(),
state_type,
})
}
}
/// Parses the file at the specified location and returns HTTP verb functions
pub fn methods_for_route(route_path: &PathBuf) -> Vec<&'static str> {
// Read the file content
let Ok(file_content) = fs::read_to_string(route_path) else {
return Vec::new();
};
// Parse the file content into a syn syntax tree
let Ok(file) = parse_file(&file_content) else {
return Vec::new();
};
// Define HTTP methods we're looking for
let allowed_methods = [
"any", "get", "post", "put", "delete", "patch", "head", "options", "trace", "connect",
];
let mut found_methods = Vec::new();
// Collect all pub & async fn's
for item in &file.items {
if let Item::Fn(fn_item) = item {
let fn_name = fn_item.sig.ident.to_string();
let is_public = matches!(fn_item.vis, Visibility::Public(_));
let is_async = fn_item.sig.asyncness.is_some();
if is_public && is_async {
found_methods.push(fn_name);
}
}
}
// Iterate through methods to ensure consistent order
allowed_methods
.into_iter()
.filter(|elem| {
found_methods
.clone()
.into_iter()
.any(|method| method == *elem)
})
.collect()
}
// Collect route.rs files recursively
pub fn collect_route_files(base_dir: &Path, dir: &Path) -> Vec<(PathBuf, PathBuf)> {
let mut routes = Vec::new();
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.filter_map(std::result::Result::ok) {
let path = entry.path();
if path.is_dir() {
let mut nested_routes = collect_route_files(base_dir, &path);
routes.append(&mut nested_routes);
} else if path.file_name().unwrap_or_default() == "route.rs" {
if let Ok(rel_dir) = path.strip_prefix(base_dir) {
routes.push((path.clone(), rel_dir.to_path_buf()));
}
}
}
}
routes.sort();
routes
}