Skip to content

Commit ab49460

Browse files
committed
!
1 parent bc87808 commit ab49460

File tree

7 files changed

+145
-147
lines changed

7 files changed

+145
-147
lines changed

crates/cargo-rustapi/src/commands/doctor.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,7 @@ fn build_project_checks(workspace_root: &Path) -> Result<Vec<DoctorCheck>> {
325325
});
326326

327327
checks.push(
328-
if (signals.production_defaults || signals.request_id)
329-
&& (signals.production_defaults || signals.tracing)
330-
{
328+
if signals.production_defaults || (signals.request_id && signals.tracing) {
331329
DoctorCheck::pass(
332330
"Request IDs and tracing",
333331
"Request ID and tracing signals detected",

crates/cargo-rustapi/src/commands/new.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ pub async fn new_project(mut args: NewArgs) -> Result<()> {
8080
.interact()?;
8181

8282
match selection {
83-
1 => Some(ProjectPreset::ProdApi),
84-
2 => Some(ProjectPreset::AiApi),
85-
3 => Some(ProjectPreset::RealtimeApi),
83+
1 => Some(ProjectPreset::Production),
84+
2 => Some(ProjectPreset::Ai),
85+
3 => Some(ProjectPreset::Realtime),
8686
_ => None,
8787
}
8888
};

crates/cargo-rustapi/src/templates/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ pub enum ProjectTemplate {
2626
pub enum ProjectPreset {
2727
/// Production-oriented HTTP API defaults.
2828
#[value(name = "prod-api")]
29-
ProdApi,
29+
Production,
3030
/// AI-friendly API defaults with TOON support.
3131
#[value(name = "ai-api")]
32-
AiApi,
32+
Ai,
3333
/// Realtime API defaults with WebSocket support.
3434
#[value(name = "realtime-api")]
35-
RealtimeApi,
35+
Realtime,
3636
}
3737

3838
impl ProjectPreset {
@@ -44,21 +44,21 @@ impl ProjectPreset {
4444
/// Recommended features that should be enabled for this preset.
4545
pub fn recommended_features(self) -> Vec<String> {
4646
match self {
47-
ProjectPreset::ProdApi => vec![
47+
ProjectPreset::Production => vec![
4848
"extras-config",
4949
"extras-cors",
5050
"extras-rate-limit",
5151
"extras-security-headers",
5252
"extras-structured-logging",
5353
"extras-timeout",
5454
],
55-
ProjectPreset::AiApi => vec![
55+
ProjectPreset::Ai => vec![
5656
"extras-config",
5757
"extras-structured-logging",
5858
"extras-timeout",
5959
"protocol-toon",
6060
],
61-
ProjectPreset::RealtimeApi => vec![
61+
ProjectPreset::Realtime => vec![
6262
"extras-cors",
6363
"extras-structured-logging",
6464
"extras-timeout",

crates/rustapi-core/src/app.rs

Lines changed: 132 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,138 @@ impl Default for RustApi {
16781678
}
16791679
}
16801680

1681+
/// Check Basic Auth header against expected credentials
1682+
#[cfg(feature = "swagger-ui")]
1683+
fn check_basic_auth(req: &crate::Request, expected: &str) -> bool {
1684+
req.headers()
1685+
.get(http::header::AUTHORIZATION)
1686+
.and_then(|v| v.to_str().ok())
1687+
.map(|auth| auth == expected)
1688+
.unwrap_or(false)
1689+
}
1690+
1691+
/// Create 401 Unauthorized response with WWW-Authenticate header
1692+
#[cfg(feature = "swagger-ui")]
1693+
fn unauthorized_response() -> crate::Response {
1694+
http::Response::builder()
1695+
.status(http::StatusCode::UNAUTHORIZED)
1696+
.header(
1697+
http::header::WWW_AUTHENTICATE,
1698+
"Basic realm=\"API Documentation\"",
1699+
)
1700+
.header(http::header::CONTENT_TYPE, "text/plain")
1701+
.body(crate::response::Body::from("Unauthorized"))
1702+
.unwrap()
1703+
}
1704+
1705+
/// Configuration builder for RustAPI with auto-routes
1706+
pub struct RustApiConfig {
1707+
docs_path: Option<String>,
1708+
docs_enabled: bool,
1709+
api_title: String,
1710+
api_version: String,
1711+
api_description: Option<String>,
1712+
body_limit: Option<usize>,
1713+
layers: LayerStack,
1714+
}
1715+
1716+
impl Default for RustApiConfig {
1717+
fn default() -> Self {
1718+
Self::new()
1719+
}
1720+
}
1721+
1722+
impl RustApiConfig {
1723+
pub fn new() -> Self {
1724+
Self {
1725+
docs_path: Some("/docs".to_string()),
1726+
docs_enabled: true,
1727+
api_title: "RustAPI".to_string(),
1728+
api_version: "1.0.0".to_string(),
1729+
api_description: None,
1730+
body_limit: None,
1731+
layers: LayerStack::new(),
1732+
}
1733+
}
1734+
1735+
/// Set the docs path (default: "/docs")
1736+
pub fn docs_path(mut self, path: impl Into<String>) -> Self {
1737+
self.docs_path = Some(path.into());
1738+
self
1739+
}
1740+
1741+
/// Enable or disable docs (default: true)
1742+
pub fn docs_enabled(mut self, enabled: bool) -> Self {
1743+
self.docs_enabled = enabled;
1744+
self
1745+
}
1746+
1747+
/// Set OpenAPI info
1748+
pub fn openapi_info(
1749+
mut self,
1750+
title: impl Into<String>,
1751+
version: impl Into<String>,
1752+
description: Option<impl Into<String>>,
1753+
) -> Self {
1754+
self.api_title = title.into();
1755+
self.api_version = version.into();
1756+
self.api_description = description.map(|d| d.into());
1757+
self
1758+
}
1759+
1760+
/// Set body size limit
1761+
pub fn body_limit(mut self, limit: usize) -> Self {
1762+
self.body_limit = Some(limit);
1763+
self
1764+
}
1765+
1766+
/// Add a middleware layer
1767+
pub fn layer<L>(mut self, layer: L) -> Self
1768+
where
1769+
L: MiddlewareLayer,
1770+
{
1771+
self.layers.push(Box::new(layer));
1772+
self
1773+
}
1774+
1775+
/// Build the RustApi instance
1776+
pub fn build(self) -> RustApi {
1777+
let mut app = RustApi::new().mount_auto_routes_grouped();
1778+
1779+
// Apply configuration
1780+
if let Some(limit) = self.body_limit {
1781+
app = app.body_limit(limit);
1782+
}
1783+
1784+
app = app.openapi_info(
1785+
&self.api_title,
1786+
&self.api_version,
1787+
self.api_description.as_deref(),
1788+
);
1789+
1790+
#[cfg(feature = "swagger-ui")]
1791+
if self.docs_enabled {
1792+
if let Some(path) = self.docs_path {
1793+
app = app.docs(&path);
1794+
}
1795+
}
1796+
1797+
// Apply layers
1798+
// Note: layers are applied in reverse order in RustApi::layer logic (pushing to vec)
1799+
app.layers.extend(self.layers);
1800+
1801+
app
1802+
}
1803+
1804+
/// Build and run the server
1805+
pub async fn run(
1806+
self,
1807+
addr: impl AsRef<str>,
1808+
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
1809+
self.build().run(addr.as_ref()).await
1810+
}
1811+
}
1812+
16811813
#[cfg(test)]
16821814
mod tests {
16831815
use super::RustApi;
@@ -2462,134 +2594,3 @@ mod tests {
24622594
}
24632595
}
24642596

2465-
/// Check Basic Auth header against expected credentials
2466-
#[cfg(feature = "swagger-ui")]
2467-
fn check_basic_auth(req: &crate::Request, expected: &str) -> bool {
2468-
req.headers()
2469-
.get(http::header::AUTHORIZATION)
2470-
.and_then(|v| v.to_str().ok())
2471-
.map(|auth| auth == expected)
2472-
.unwrap_or(false)
2473-
}
2474-
2475-
/// Create 401 Unauthorized response with WWW-Authenticate header
2476-
#[cfg(feature = "swagger-ui")]
2477-
fn unauthorized_response() -> crate::Response {
2478-
http::Response::builder()
2479-
.status(http::StatusCode::UNAUTHORIZED)
2480-
.header(
2481-
http::header::WWW_AUTHENTICATE,
2482-
"Basic realm=\"API Documentation\"",
2483-
)
2484-
.header(http::header::CONTENT_TYPE, "text/plain")
2485-
.body(crate::response::Body::from("Unauthorized"))
2486-
.unwrap()
2487-
}
2488-
2489-
/// Configuration builder for RustAPI with auto-routes
2490-
pub struct RustApiConfig {
2491-
docs_path: Option<String>,
2492-
docs_enabled: bool,
2493-
api_title: String,
2494-
api_version: String,
2495-
api_description: Option<String>,
2496-
body_limit: Option<usize>,
2497-
layers: LayerStack,
2498-
}
2499-
2500-
impl Default for RustApiConfig {
2501-
fn default() -> Self {
2502-
Self::new()
2503-
}
2504-
}
2505-
2506-
impl RustApiConfig {
2507-
pub fn new() -> Self {
2508-
Self {
2509-
docs_path: Some("/docs".to_string()),
2510-
docs_enabled: true,
2511-
api_title: "RustAPI".to_string(),
2512-
api_version: "1.0.0".to_string(),
2513-
api_description: None,
2514-
body_limit: None,
2515-
layers: LayerStack::new(),
2516-
}
2517-
}
2518-
2519-
/// Set the docs path (default: "/docs")
2520-
pub fn docs_path(mut self, path: impl Into<String>) -> Self {
2521-
self.docs_path = Some(path.into());
2522-
self
2523-
}
2524-
2525-
/// Enable or disable docs (default: true)
2526-
pub fn docs_enabled(mut self, enabled: bool) -> Self {
2527-
self.docs_enabled = enabled;
2528-
self
2529-
}
2530-
2531-
/// Set OpenAPI info
2532-
pub fn openapi_info(
2533-
mut self,
2534-
title: impl Into<String>,
2535-
version: impl Into<String>,
2536-
description: Option<impl Into<String>>,
2537-
) -> Self {
2538-
self.api_title = title.into();
2539-
self.api_version = version.into();
2540-
self.api_description = description.map(|d| d.into());
2541-
self
2542-
}
2543-
2544-
/// Set body size limit
2545-
pub fn body_limit(mut self, limit: usize) -> Self {
2546-
self.body_limit = Some(limit);
2547-
self
2548-
}
2549-
2550-
/// Add a middleware layer
2551-
pub fn layer<L>(mut self, layer: L) -> Self
2552-
where
2553-
L: MiddlewareLayer,
2554-
{
2555-
self.layers.push(Box::new(layer));
2556-
self
2557-
}
2558-
2559-
/// Build the RustApi instance
2560-
pub fn build(self) -> RustApi {
2561-
let mut app = RustApi::new().mount_auto_routes_grouped();
2562-
2563-
// Apply configuration
2564-
if let Some(limit) = self.body_limit {
2565-
app = app.body_limit(limit);
2566-
}
2567-
2568-
app = app.openapi_info(
2569-
&self.api_title,
2570-
&self.api_version,
2571-
self.api_description.as_deref(),
2572-
);
2573-
2574-
#[cfg(feature = "swagger-ui")]
2575-
if self.docs_enabled {
2576-
if let Some(path) = self.docs_path {
2577-
app = app.docs(&path);
2578-
}
2579-
}
2580-
2581-
// Apply layers
2582-
// Note: layers are applied in reverse order in RustApi::layer logic (pushing to vec)
2583-
app.layers.extend(self.layers);
2584-
2585-
app
2586-
}
2587-
2588-
/// Build and run the server
2589-
pub async fn run(
2590-
self,
2591-
addr: impl AsRef<str>,
2592-
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
2593-
self.build().run(addr.as_ref()).await
2594-
}
2595-
}

crates/rustapi-core/src/multipart.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,7 @@ mod tests {
814814
#[test]
815815
fn test_parse_simple_multipart() {
816816
let boundary = "----WebKitFormBoundary";
817-
let body = format!(
818-
"------WebKitFormBoundary\r\n\
817+
let body = "------WebKitFormBoundary\r\n\
819818
Content-Disposition: form-data; name=\"field1\"\r\n\
820819
\r\n\
821820
value1\r\n\
@@ -825,7 +824,7 @@ mod tests {
825824
\r\n\
826825
file content\r\n\
827826
------WebKitFormBoundary--\r\n"
828-
);
827+
.to_string();
829828

830829
let fields = parse_multipart(&Bytes::from(body), boundary).unwrap();
831830
assert_eq!(fields.len(), 2);

crates/rustapi-core/src/router.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,7 @@ mod property_tests {
16771677
fn prop_empty_or_slashes_normalize_to_root(
16781678
num_slashes in 0..10usize,
16791679
) {
1680-
let prefix: String = std::iter::repeat('/').take(num_slashes).collect();
1680+
let prefix = "/".repeat(num_slashes);
16811681

16821682
let normalized = normalize_prefix(&prefix);
16831683

tmp/rustapi-core-clippy.txt

7.96 KB
Binary file not shown.

0 commit comments

Comments
 (0)