openapi_from_source/extractor/mod.rs
1//! Route extraction module for parsing web framework route definitions.
2//!
3//! This module provides a unified interface for extracting route information from different
4//! web frameworks. Each framework has its own extractor implementation that knows how to
5//! parse framework-specific route definitions.
6//!
7//! # Supported Frameworks
8//!
9//! - **Axum**: See [`axum::AxumExtractor`]
10//! - **Actix-Web**: See [`actix::ActixExtractor`]
11//!
12//! # Example
13//!
14//! ```no_run
15//! use openapi_from_source::extractor::{RouteExtractor, axum::AxumExtractor};
16//! use openapi_from_source::parser::AstParser;
17//! use std::path::Path;
18//!
19//! let parsed = AstParser::parse_file(Path::new("src/main.rs")).unwrap();
20//! let extractor = AxumExtractor;
21//! let routes = extractor.extract_routes(&[parsed]);
22//! println!("Found {} routes", routes.len());
23//! ```
24
25pub mod axum;
26pub mod actix;
27
28use crate::parser::ParsedFile;
29
30/// Trait for extracting route information from parsed Rust files.
31///
32/// Implementations of this trait know how to analyze the AST of a specific web framework
33/// and extract route definitions, including paths, HTTP methods, parameters, and type information.
34pub trait RouteExtractor {
35 /// Extracts all route information from parsed Rust files.
36 ///
37 /// # Arguments
38 ///
39 /// * `parsed_files` - All successfully parsed Rust source files in the project
40 ///
41 /// # Returns
42 ///
43 /// Returns a vector of `RouteInfo` structs, one for each discovered route across all files.
44 fn extract_routes(&self, parsed_files: &[ParsedFile]) -> Vec<RouteInfo>;
45}
46
47/// Complete information about a single API endpoint.
48///
49/// This structure contains all the metadata needed to generate an OpenAPI operation,
50/// including the path, HTTP method, parameters, and request/response types.
51#[derive(Debug, Clone)]
52pub struct RouteInfo {
53 /// The URL path pattern (e.g., "/users/:id" or "/users/{id}")
54 pub path: String,
55 /// The HTTP method for this route
56 pub method: HttpMethod,
57 /// The name of the handler function
58 pub handler_name: String,
59 /// List of parameters extracted from the handler signature
60 pub parameters: Vec<Parameter>,
61 /// Type information for the request body, if present
62 pub request_body: Option<TypeInfo>,
63 /// Type information for the response, if it can be determined
64 pub response_type: Option<TypeInfo>,
65}
66
67/// HTTP methods supported by route extractors.
68///
69/// These correspond to standard HTTP methods used in RESTful APIs.
70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
71pub enum HttpMethod {
72 /// HTTP GET method
73 Get,
74 /// HTTP POST method
75 Post,
76 /// HTTP PUT method
77 Put,
78 /// HTTP DELETE method
79 Delete,
80 /// HTTP PATCH method
81 Patch,
82 /// HTTP OPTIONS method
83 Options,
84 /// HTTP HEAD method
85 Head,
86}
87
88/// Information about a single parameter in a route handler.
89///
90/// Parameters can come from different locations (path, query string, headers)
91/// and have associated type information for schema generation.
92#[derive(Debug, Clone)]
93pub struct Parameter {
94 /// The parameter name
95 pub name: String,
96 /// Where the parameter is extracted from (path, query, header)
97 pub location: ParameterLocation,
98 /// Type information for generating the parameter schema
99 pub type_info: TypeInfo,
100 /// Whether the parameter is required (non-optional)
101 pub required: bool,
102}
103
104/// The location where a parameter value is extracted from in an HTTP request.
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub enum ParameterLocation {
107 /// Path parameter embedded in the URL (e.g., `/users/:id`)
108 Path,
109 /// Query string parameter (e.g., `?page=1&limit=10`)
110 Query,
111 /// HTTP header parameter
112 Header,
113}
114
115/// Type information extracted from Rust code for OpenAPI schema generation.
116///
117/// This structure captures the essential information about a Rust type needed to
118/// generate an appropriate OpenAPI schema, including generic arguments and wrapper types.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct TypeInfo {
121 /// The base type name (e.g., "String", "User", "i32")
122 pub name: String,
123 /// Whether this is a generic type with type parameters
124 pub is_generic: bool,
125 /// Generic type arguments (e.g., for `Vec<String>`, contains TypeInfo for String)
126 pub generic_args: Vec<TypeInfo>,
127 /// Whether this type is wrapped in `Option<T>`
128 pub is_option: bool,
129 /// Whether this type is a `Vec<T>` (array type)
130 pub is_vec: bool,
131}
132
133impl TypeInfo {
134 /// Create a new TypeInfo for a simple type
135 pub fn new(name: String) -> Self {
136 Self {
137 name,
138 is_generic: false,
139 generic_args: Vec::new(),
140 is_option: false,
141 is_vec: false,
142 }
143 }
144
145 /// Create a TypeInfo for an `Option<T>` type
146 pub fn option(inner: TypeInfo) -> Self {
147 Self {
148 name: inner.name.clone(),
149 is_generic: false,
150 generic_args: vec![inner],
151 is_option: true,
152 is_vec: false,
153 }
154 }
155
156 /// Create a TypeInfo for a `Vec<T>` type
157 pub fn vec(inner: TypeInfo) -> Self {
158 Self {
159 name: inner.name.clone(),
160 is_generic: false,
161 generic_args: vec![inner],
162 is_option: false,
163 is_vec: true,
164 }
165 }
166}
167
168impl RouteInfo {
169 /// Create a new RouteInfo with minimal required fields
170 pub fn new(path: String, method: HttpMethod, handler_name: String) -> Self {
171 Self {
172 path,
173 method,
174 handler_name,
175 parameters: Vec::new(),
176 request_body: None,
177 response_type: None,
178 }
179 }
180}
181
182impl Parameter {
183 /// Create a new Parameter
184 pub fn new(name: String, location: ParameterLocation, type_info: TypeInfo, required: bool) -> Self {
185 Self {
186 name,
187 location,
188 type_info,
189 required,
190 }
191 }
192}