mirror of
https://github.com/elicpeter/nyx.git
synced 2026-07-12 21:02:11 +02:00
[pitboss/grind] deferred session-0002 (20260522T043516Z-29b8)
This commit is contained in:
parent
945836bf88
commit
ebe3a4fca0
5 changed files with 289 additions and 13 deletions
|
|
@ -19,8 +19,8 @@ use crate::symbol::Lang;
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use super::rust_routes::{
|
use super::rust_routes::{
|
||||||
bind_rust_path_params, find_actix_route_chain, find_method_attribute, find_rust_function,
|
bind_rust_path_params, collect_rust_middleware, find_actix_route_chain, find_method_attribute,
|
||||||
rust_formal_names, source_imports_actix,
|
find_rust_function, rust_formal_names, source_imports_actix,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RustActixAdapter;
|
pub struct RustActixAdapter;
|
||||||
|
|
@ -50,13 +50,14 @@ impl FrameworkAdapter for RustActixAdapter {
|
||||||
.or_else(|| find_actix_route_chain(ast, file_bytes, &summary.name))?;
|
.or_else(|| find_actix_route_chain(ast, file_bytes, &summary.name))?;
|
||||||
let formals = rust_formal_names(func, file_bytes);
|
let formals = rust_formal_names(func, file_bytes);
|
||||||
let request_params = bind_rust_path_params(&formals, &path);
|
let request_params = bind_rust_path_params(&formals, &path);
|
||||||
|
let middleware = collect_rust_middleware(ast, file_bytes);
|
||||||
Some(FrameworkBinding {
|
Some(FrameworkBinding {
|
||||||
adapter: ADAPTER_NAME.to_owned(),
|
adapter: ADAPTER_NAME.to_owned(),
|
||||||
kind: EntryKind::HttpRoute,
|
kind: EntryKind::HttpRoute,
|
||||||
route: Some(RouteShape { method, path }),
|
route: Some(RouteShape { method, path }),
|
||||||
request_params,
|
request_params,
|
||||||
response_writer: None,
|
response_writer: None,
|
||||||
middleware: Vec::new(),
|
middleware,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -167,6 +168,18 @@ mod tests {
|
||||||
assert_eq!(route.path, "/save");
|
assert_eq!(route.path, "/save");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn populates_middleware_from_wrap_call() {
|
||||||
|
let src: &[u8] = b"use actix_web::{App, web};\n\
|
||||||
|
fn build() -> App<()> { App::new().wrap(HttpAuthentication::bearer(validator)).route(\"/u\", web::get().to(show)) }\n\
|
||||||
|
async fn show() -> String { String::new() }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let binding = RustActixAdapter
|
||||||
|
.detect(&summary("show"), tree.root_node(), src)
|
||||||
|
.expect("binding");
|
||||||
|
assert!(binding.middleware.iter().any(|m| m.name.contains("HttpAuthentication")));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chained_builder_requires_handler_match() {
|
fn chained_builder_requires_handler_match() {
|
||||||
let src: &[u8] = b"use actix_web::{App, web};\n\
|
let src: &[u8] = b"use actix_web::{App, web};\n\
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ use crate::symbol::Lang;
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use super::rust_routes::{
|
use super::rust_routes::{
|
||||||
bind_rust_path_params, find_axum_route, find_rust_function, rust_formal_names,
|
bind_rust_path_params, collect_rust_middleware, find_axum_route, find_rust_function,
|
||||||
source_imports_axum,
|
rust_formal_names, source_imports_axum,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RustAxumAdapter;
|
pub struct RustAxumAdapter;
|
||||||
|
|
@ -52,13 +52,14 @@ impl FrameworkAdapter for RustAxumAdapter {
|
||||||
bind_rust_path_params(&formals, &path)
|
bind_rust_path_params(&formals, &path)
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
let middleware = collect_rust_middleware(ast, file_bytes);
|
||||||
Some(FrameworkBinding {
|
Some(FrameworkBinding {
|
||||||
adapter: ADAPTER_NAME.to_owned(),
|
adapter: ADAPTER_NAME.to_owned(),
|
||||||
kind: EntryKind::HttpRoute,
|
kind: EntryKind::HttpRoute,
|
||||||
route: Some(RouteShape { method, path }),
|
route: Some(RouteShape { method, path }),
|
||||||
request_params,
|
request_params,
|
||||||
response_writer: None,
|
response_writer: None,
|
||||||
middleware: Vec::new(),
|
middleware,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,6 +124,17 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn populates_middleware_from_layer_calls() {
|
||||||
|
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/u/{id}\", get(show)).layer(AuthLayer) }\nfn show(id: String) -> String { id }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let binding = RustAxumAdapter
|
||||||
|
.detect(&summary("show"), tree.root_node(), src)
|
||||||
|
.expect("binding");
|
||||||
|
assert_eq!(binding.middleware.len(), 1);
|
||||||
|
assert_eq!(binding.middleware[0].name, "AuthLayer");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn skips_when_route_does_not_reference_function() {
|
fn skips_when_route_does_not_reference_function() {
|
||||||
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/u\", get(show)) }\nfn helper() {}\n";
|
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/u\", get(show)) }\nfn helper() {}\n";
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ use crate::symbol::Lang;
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use super::rust_routes::{
|
use super::rust_routes::{
|
||||||
bind_rust_path_params, find_method_attribute, find_rust_function, rust_formal_names,
|
bind_rust_path_params, collect_rust_middleware, find_method_attribute, find_rust_function,
|
||||||
source_imports_rocket,
|
rust_formal_names, source_imports_rocket,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RustRocketAdapter;
|
pub struct RustRocketAdapter;
|
||||||
|
|
@ -53,13 +53,14 @@ impl FrameworkAdapter for RustRocketAdapter {
|
||||||
let (method, path) = find_method_attribute(func, file_bytes)?;
|
let (method, path) = find_method_attribute(func, file_bytes)?;
|
||||||
let formals = rust_formal_names(func, file_bytes);
|
let formals = rust_formal_names(func, file_bytes);
|
||||||
let request_params = bind_rust_path_params(&formals, &path);
|
let request_params = bind_rust_path_params(&formals, &path);
|
||||||
|
let middleware = collect_rust_middleware(ast, file_bytes);
|
||||||
Some(FrameworkBinding {
|
Some(FrameworkBinding {
|
||||||
adapter: ADAPTER_NAME.to_owned(),
|
adapter: ADAPTER_NAME.to_owned(),
|
||||||
kind: EntryKind::HttpRoute,
|
kind: EntryKind::HttpRoute,
|
||||||
route: Some(RouteShape { method, path }),
|
route: Some(RouteShape { method, path }),
|
||||||
request_params,
|
request_params,
|
||||||
response_writer: None,
|
response_writer: None,
|
||||||
middleware: Vec::new(),
|
middleware,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -115,6 +116,18 @@ mod tests {
|
||||||
assert_eq!(binding.route.unwrap().method, HttpMethod::POST);
|
assert_eq!(binding.route.unwrap().method, HttpMethod::POST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn populates_middleware_from_attach_fairing() {
|
||||||
|
let src: &[u8] = b"use rocket::get;\n#[get(\"/u\")]\nfn show() -> &'static str { \"ok\" }\n\
|
||||||
|
#[launch]\nfn rocket() -> _ { rocket::build().attach(CsrfLayer).mount(\"/\", routes![show]) }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let binding = RustRocketAdapter
|
||||||
|
.detect(&summary("show"), tree.root_node(), src)
|
||||||
|
.expect("binding");
|
||||||
|
assert_eq!(binding.middleware.len(), 1);
|
||||||
|
assert_eq!(binding.middleware[0].name, "CsrfLayer");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn skips_when_rocket_not_imported() {
|
fn skips_when_rocket_not_imported() {
|
||||||
let src: &[u8] = b"#[get(\"/u\")]\nfn show() {}\n";
|
let src: &[u8] = b"#[get(\"/u\")]\nfn show() {}\n";
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@
|
||||||
//! paradigm; the warp adapter binds formals positionally rather
|
//! paradigm; the warp adapter binds formals positionally rather
|
||||||
//! than by name.
|
//! than by name.
|
||||||
|
|
||||||
use crate::dynamic::framework::{HttpMethod, ParamBinding, ParamSource};
|
use crate::dynamic::framework::auth_markers;
|
||||||
|
use crate::dynamic::framework::{HttpMethod, MiddlewareShape, ParamBinding, ParamSource};
|
||||||
|
use crate::symbol::Lang;
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
/// True when `bytes` carries any of the well-known axum markers.
|
/// True when `bytes` carries any of the well-known axum markers.
|
||||||
|
|
@ -287,6 +289,153 @@ pub fn verb_from_ident(ident: &str) -> Option<HttpMethod> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Walk every method-chain call in the file whose field name is one
|
||||||
|
/// of the known middleware-attach verbs and collect argument
|
||||||
|
/// expressions whose names match a known Rust middleware marker (see
|
||||||
|
/// [`crate::dynamic::framework::auth_markers::is_protective`]).
|
||||||
|
///
|
||||||
|
/// Per-framework attach verbs:
|
||||||
|
/// - axum: `.layer(...)`, `.route_layer(...)`
|
||||||
|
/// - actix: `.wrap(...)`, `.wrap_fn(...)`
|
||||||
|
/// - rocket: `.attach(...)` (fairings)
|
||||||
|
/// - warp: `.and(filter)` filter composition
|
||||||
|
///
|
||||||
|
/// Argument rendering:
|
||||||
|
/// - bare identifier (`.layer(AuthLayer)`) → `"AuthLayer"`
|
||||||
|
/// - scoped identifier (`.wrap(middleware::Logger::default())`'s
|
||||||
|
/// receiver path) — the call-form below covers it via callee text
|
||||||
|
/// - call expression (`.layer(AuthLayer::new())`) →
|
||||||
|
/// `"AuthLayer::new"` (callee text, args dropped)
|
||||||
|
/// - turbofish call expression (`.layer(Service::<T>::new())`) →
|
||||||
|
/// callee stripped of generics
|
||||||
|
///
|
||||||
|
/// De-duplicates within a single file; preserves declaration order.
|
||||||
|
/// Names the registry does not recognise are dropped silently — the
|
||||||
|
/// caller can re-walk with a wider predicate if it needs broader
|
||||||
|
/// inclusion.
|
||||||
|
pub fn collect_rust_middleware(root: Node<'_>, bytes: &[u8]) -> Vec<MiddlewareShape> {
|
||||||
|
let mut raw: Vec<String> = Vec::new();
|
||||||
|
walk_attach_calls(root, bytes, &mut raw);
|
||||||
|
let mut out: Vec<MiddlewareShape> = Vec::new();
|
||||||
|
for name in raw {
|
||||||
|
if auth_markers::is_protective(Lang::Rust, &name)
|
||||||
|
&& !out.iter().any(|m| m.name == name)
|
||||||
|
{
|
||||||
|
out.push(MiddlewareShape { name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_attach_calls(node: Node<'_>, bytes: &[u8], out: &mut Vec<String>) {
|
||||||
|
if node.kind() == "call_expression" {
|
||||||
|
try_collect_attach_call(node, bytes, out);
|
||||||
|
}
|
||||||
|
let mut cur = node.walk();
|
||||||
|
for child in node.children(&mut cur) {
|
||||||
|
walk_attach_calls(child, bytes, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_collect_attach_call(call: Node<'_>, bytes: &[u8], out: &mut Vec<String>) {
|
||||||
|
let Some(callee) = call.child_by_field_name("function") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if callee.kind() != "field_expression" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(field) = callee.child_by_field_name("field") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Ok(verb) = field.utf8_text(bytes) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !matches!(
|
||||||
|
verb,
|
||||||
|
"layer" | "route_layer" | "wrap" | "wrap_fn" | "attach" | "and"
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(args) = call.child_by_field_name("arguments") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut cur = args.walk();
|
||||||
|
for arg in args.named_children(&mut cur) {
|
||||||
|
if matches!(arg.kind(), "line_comment" | "block_comment") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
push_middleware_candidates(arg, bytes, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_middleware_candidates(node: Node<'_>, bytes: &[u8], out: &mut Vec<String>) {
|
||||||
|
let Some(primary) = middleware_arg_name(node, bytes) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
out.push(primary.clone());
|
||||||
|
// Also push the leading path segment so a scoped callee like
|
||||||
|
// `HttpAuthentication::bearer(validator)` matches the marker
|
||||||
|
// `HttpAuthentication` in the auth-markers table.
|
||||||
|
if let Some((head, _)) = primary.split_once("::") {
|
||||||
|
let head = head.trim();
|
||||||
|
if !head.is_empty() && head != primary {
|
||||||
|
out.push(head.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn middleware_arg_name(node: Node<'_>, bytes: &[u8]) -> Option<String> {
|
||||||
|
match node.kind() {
|
||||||
|
"identifier" | "scoped_identifier" => {
|
||||||
|
node.utf8_text(bytes).ok().map(|s| s.trim().to_owned())
|
||||||
|
}
|
||||||
|
"call_expression" => {
|
||||||
|
let callee = node.child_by_field_name("function")?;
|
||||||
|
let raw = callee.utf8_text(bytes).ok()?.trim().to_owned();
|
||||||
|
// Strip turbofish generics: `Service::<T>::new` → `Service::new`.
|
||||||
|
Some(strip_turbofish(&raw))
|
||||||
|
}
|
||||||
|
"generic_function" => {
|
||||||
|
let callee = node.child_by_field_name("function")?;
|
||||||
|
callee.utf8_text(bytes).ok().map(|s| s.trim().to_owned())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strip_turbofish(raw: &str) -> String {
|
||||||
|
let mut out = String::with_capacity(raw.len());
|
||||||
|
let mut depth: i32 = 0;
|
||||||
|
let bytes = raw.as_bytes();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
if depth == 0 && i + 1 < bytes.len() && bytes[i] == b':' && bytes[i + 1] == b':' {
|
||||||
|
// peek for `<`
|
||||||
|
let mut j = i + 2;
|
||||||
|
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
if j < bytes.len() && bytes[j] == b'<' {
|
||||||
|
depth += 1;
|
||||||
|
i = j + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if depth > 0 {
|
||||||
|
match bytes[i] {
|
||||||
|
b'<' => depth += 1,
|
||||||
|
b'>' => depth -= 1,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push(bytes[i] as char);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
/// Read the content of a Rust `string_literal` node, stripping the
|
/// Read the content of a Rust `string_literal` node, stripping the
|
||||||
/// surrounding `"` quotes. Returns `None` if `node` is not a string
|
/// surrounding `"` quotes. Returns `None` if `node` is not a string
|
||||||
/// literal.
|
/// literal.
|
||||||
|
|
@ -921,4 +1070,81 @@ mod tests {
|
||||||
assert!(matches!(bindings[0].source, ParamSource::Implicit));
|
assert!(matches!(bindings[0].source, ParamSource::Implicit));
|
||||||
assert!(matches!(bindings[1].source, ParamSource::PathSegment(_)));
|
assert!(matches!(bindings[1].source, ParamSource::PathSegment(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_picks_axum_layer_bare_ident() {
|
||||||
|
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/x\", get(show)).layer(AuthLayer) }\nfn show() {}\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
assert_eq!(mw.len(), 1);
|
||||||
|
assert_eq!(mw[0].name, "AuthLayer");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_picks_axum_route_layer() {
|
||||||
|
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/x\", get(show)).route_layer(CsrfLayer) }\nfn show() {}\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
assert_eq!(mw.len(), 1);
|
||||||
|
assert_eq!(mw[0].name, "CsrfLayer");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_picks_actix_wrap_call() {
|
||||||
|
let src: &[u8] = b"use actix_web::App;\nfn build() -> App<()> { App::new().wrap(HttpAuthentication::bearer(validator)) }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
assert!(mw.iter().any(|m| m.name.contains("HttpAuthentication")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_picks_rocket_attach_fairing() {
|
||||||
|
let src: &[u8] = b"use rocket::Rocket;\nfn build() { rocket::build().attach(CsrfLayer) }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
assert_eq!(mw.len(), 1);
|
||||||
|
assert_eq!(mw[0].name, "CsrfLayer");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_picks_warp_and_filter() {
|
||||||
|
let src: &[u8] = b"use warp::Filter;\nfn build() { let r = warp::path!(\"x\").and(BearerAuth).map(show); }\nfn show() {}\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
assert_eq!(mw.len(), 1);
|
||||||
|
assert_eq!(mw[0].name, "BearerAuth");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_drops_unknown_names() {
|
||||||
|
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().layer(LoggingLayer) }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
assert!(mw.is_empty(), "LoggingLayer is not a recognised marker");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_dedupes_and_preserves_order() {
|
||||||
|
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().layer(AuthLayer).route_layer(CsrfLayer).layer(AuthLayer) }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
let names: Vec<&str> = mw.iter().map(|m| m.name.as_str()).collect();
|
||||||
|
assert_eq!(names, vec!["AuthLayer", "CsrfLayer"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_rust_middleware_returns_empty_when_no_attach() {
|
||||||
|
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/x\", get(show)) }\nfn show() {}\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let mw = collect_rust_middleware(tree.root_node(), src);
|
||||||
|
assert!(mw.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn strip_turbofish_removes_generic_args() {
|
||||||
|
assert_eq!(strip_turbofish("Foo::<T>::new"), "Foo::new");
|
||||||
|
assert_eq!(strip_turbofish("Foo::new"), "Foo::new");
|
||||||
|
assert_eq!(strip_turbofish("foo"), "foo");
|
||||||
|
assert_eq!(strip_turbofish("Foo::<A, B>::bar"), "Foo::bar");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ use crate::symbol::Lang;
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use super::rust_routes::{
|
use super::rust_routes::{
|
||||||
bind_rust_path_params, find_rust_function, find_warp_route, rust_formal_names,
|
bind_rust_path_params, collect_rust_middleware, find_rust_function, find_warp_route,
|
||||||
source_imports_warp,
|
rust_formal_names, source_imports_warp,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RustWarpAdapter;
|
pub struct RustWarpAdapter;
|
||||||
|
|
@ -54,13 +54,14 @@ impl FrameworkAdapter for RustWarpAdapter {
|
||||||
bind_rust_path_params(&formals, &path)
|
bind_rust_path_params(&formals, &path)
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
let middleware = collect_rust_middleware(ast, file_bytes);
|
||||||
Some(FrameworkBinding {
|
Some(FrameworkBinding {
|
||||||
adapter: ADAPTER_NAME.to_owned(),
|
adapter: ADAPTER_NAME.to_owned(),
|
||||||
kind: EntryKind::HttpRoute,
|
kind: EntryKind::HttpRoute,
|
||||||
route: Some(RouteShape { method, path }),
|
route: Some(RouteShape { method, path }),
|
||||||
request_params,
|
request_params,
|
||||||
response_writer: None,
|
response_writer: None,
|
||||||
middleware: Vec::new(),
|
middleware,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +109,17 @@ mod tests {
|
||||||
assert!(binding.route.unwrap().path.contains("x"));
|
assert!(binding.route.unwrap().path.contains("x"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn populates_middleware_from_and_filter() {
|
||||||
|
let src: &[u8] = b"use warp::Filter;\nfn build() { let r = warp::path!(\"x\" / u32).and(BearerAuth).map(show); }\nfn show(id: u32) -> String { String::new() }\n";
|
||||||
|
let tree = parse(src);
|
||||||
|
let binding = RustWarpAdapter
|
||||||
|
.detect(&summary("show"), tree.root_node(), src)
|
||||||
|
.expect("binding");
|
||||||
|
assert_eq!(binding.middleware.len(), 1);
|
||||||
|
assert_eq!(binding.middleware[0].name, "BearerAuth");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn skips_when_warp_not_imported() {
|
fn skips_when_warp_not_imported() {
|
||||||
let src: &[u8] = b"fn show() {}\n";
|
let src: &[u8] = b"fn show() {}\n";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue