fix: add --all to cargo fmt in script/test and autoformat codebase

cargo fmt without --all fails with "Failed to find targets" in
workspace repos. This was blocking every story's gates. Also ran
cargo fmt --all to fix all existing formatting issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dave
2026-04-13 14:07:08 +00:00
parent ed2526ce41
commit 845b85e7a7
128 changed files with 3566 additions and 2395 deletions
+112 -34
View File
@@ -13,7 +13,7 @@ use poem::web::Data;
use poem::{Body, Request, Response};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use serde_json::{Value, json};
use std::collections::BTreeMap;
use std::path::Path;
use std::sync::Arc;
@@ -41,8 +41,7 @@ impl GatewayConfig {
pub fn load(path: &Path) -> Result<Self, String> {
let contents = std::fs::read_to_string(path)
.map_err(|e| format!("cannot read {}: {e}", path.display()))?;
toml::from_str(&contents)
.map_err(|e| format!("invalid projects.toml: {e}"))
toml::from_str(&contents).map_err(|e| format!("invalid projects.toml: {e}"))
}
}
@@ -117,11 +116,21 @@ struct JsonRpcError {
impl JsonRpcResponse {
fn success(id: Option<Value>, result: Value) -> Self {
Self { jsonrpc: "2.0", id, result: Some(result), error: None }
Self {
jsonrpc: "2.0",
id,
result: Some(result),
error: None,
}
}
fn error(id: Option<Value>, code: i64, message: String) -> Self {
Self { jsonrpc: "2.0", id, result: None, error: Some(JsonRpcError { code, message }) }
Self {
jsonrpc: "2.0",
id,
result: None,
error: Some(JsonRpcError { code, message }),
}
}
}
@@ -147,22 +156,32 @@ pub async fn gateway_mcp_post_handler(
let content_type = req.header("content-type").unwrap_or("");
if !content_type.is_empty() && !content_type.contains("application/json") {
return to_json_response(JsonRpcResponse::error(
None, -32700, "Unsupported Content-Type; expected application/json".into(),
None,
-32700,
"Unsupported Content-Type; expected application/json".into(),
));
}
let bytes = match body.into_bytes().await {
Ok(b) => b,
Err(_) => return to_json_response(JsonRpcResponse::error(None, -32700, "Parse error".into())),
Err(_) => {
return to_json_response(JsonRpcResponse::error(None, -32700, "Parse error".into()));
}
};
let rpc: JsonRpcRequest = match serde_json::from_slice(&bytes) {
Ok(r) => r,
Err(_) => return to_json_response(JsonRpcResponse::error(None, -32700, "Parse error".into())),
Err(_) => {
return to_json_response(JsonRpcResponse::error(None, -32700, "Parse error".into()));
}
};
if rpc.jsonrpc != "2.0" {
return to_json_response(JsonRpcResponse::error(rpc.id, -32600, "Invalid JSON-RPC version".into()));
return to_json_response(JsonRpcResponse::error(
rpc.id,
-32600,
"Invalid JSON-RPC version".into(),
));
}
// Accept notifications silently.
@@ -185,7 +204,8 @@ pub async fn gateway_mcp_post_handler(
}
}
"tools/call" => {
let tool_name = rpc.params
let tool_name = rpc
.params
.get("name")
.and_then(|v| v.as_str())
.unwrap_or("");
@@ -200,7 +220,9 @@ pub async fn gateway_mcp_post_handler(
.header("Content-Type", "application/json")
.body(Body::from(resp_body)),
Err(e) => to_json_response(JsonRpcResponse::error(
rpc.id, -32603, format!("proxy error: {e}"),
rpc.id,
-32603,
format!("proxy error: {e}"),
)),
}
}
@@ -213,7 +235,9 @@ pub async fn gateway_mcp_post_handler(
.header("Content-Type", "application/json")
.body(Body::from(resp_body)),
Err(e) => to_json_response(JsonRpcResponse::error(
rpc.id, -32603, format!("proxy error: {e}"),
rpc.id,
-32603,
format!("proxy error: {e}"),
)),
}
}
@@ -295,14 +319,17 @@ async fn merge_tools_list(
"params": {}
});
let resp = state.client
let resp = state
.client
.post(&mcp_url)
.json(&rpc_body)
.send()
.await
.map_err(|e| format!("failed to reach {mcp_url}: {e}"))?;
let resp_json: Value = resp.json().await
let resp_json: Value = resp
.json()
.await
.map_err(|e| format!("invalid JSON from upstream: {e}"))?;
let mut tools: Vec<Value> = resp_json
@@ -320,14 +347,12 @@ async fn merge_tools_list(
}
/// Proxy a raw MCP request body to the active project's container.
async fn proxy_mcp_call(
state: &GatewayState,
request_bytes: &[u8],
) -> Result<Vec<u8>, String> {
async fn proxy_mcp_call(state: &GatewayState, request_bytes: &[u8]) -> Result<Vec<u8>, String> {
let url = state.active_url().await?;
let mcp_url = format!("{}/mcp", url.trim_end_matches('/'));
let resp = state.client
let resp = state
.client
.post(&mcp_url)
.header("Content-Type", "application/json")
.body(request_bytes.to_vec())
@@ -374,8 +399,12 @@ async fn handle_switch_project(params: &Value, state: &GatewayState) -> JsonRpcR
if !state.config.projects.contains_key(project) {
let available: Vec<&str> = state.config.projects.keys().map(|s| s.as_str()).collect();
return JsonRpcResponse::error(
None, -32602,
format!("unknown project '{project}'. Available: {}", available.join(", ")),
None,
-32602,
format!(
"unknown project '{project}'. Available: {}",
available.join(", ")
),
);
}
@@ -431,7 +460,9 @@ async fn handle_gateway_status(state: &GatewayState) -> JsonRpcResponse {
}),
)
}
Err(e) => JsonRpcResponse::error(None, -32603, format!("invalid upstream response: {e}")),
Err(e) => {
JsonRpcResponse::error(None, -32603, format!("invalid upstream response: {e}"))
}
}
}
Err(e) => JsonRpcResponse::error(None, -32603, format!("failed to reach {mcp_url}: {e}")),
@@ -500,7 +531,11 @@ pub async fn gateway_health_handler(state: Data<&Arc<GatewayState>>) -> Response
"projects": statuses,
});
let status = if all_healthy { StatusCode::OK } else { StatusCode::SERVICE_UNAVAILABLE };
let status = if all_healthy {
StatusCode::OK
} else {
StatusCode::SERVICE_UNAVAILABLE
};
Response::builder()
.status(status)
.header("Content-Type", "application/json")
@@ -519,7 +554,13 @@ pub async fn run(config_path: &Path, port: u16) -> Result<(), std::io::Error> {
crate::slog!("[gateway] Starting gateway on port {port}, active project: {active}");
crate::slog!(
"[gateway] Registered projects: {}",
state_arc.config.projects.keys().cloned().collect::<Vec<_>>().join(", ")
state_arc
.config
.projects
.keys()
.cloned()
.collect::<Vec<_>>()
.join(", ")
);
let route = poem::Route::new()
@@ -569,15 +610,27 @@ url = "http://localhost:3002"
#[test]
fn gateway_state_rejects_empty_config() {
let config = GatewayConfig { projects: BTreeMap::new() };
let config = GatewayConfig {
projects: BTreeMap::new(),
};
assert!(GatewayState::new(config).is_err());
}
#[test]
fn gateway_state_sets_first_project_active() {
let mut projects = BTreeMap::new();
projects.insert("alpha".into(), ProjectEntry { url: "http://a:3001".into() });
projects.insert("beta".into(), ProjectEntry { url: "http://b:3002".into() });
projects.insert(
"alpha".into(),
ProjectEntry {
url: "http://a:3001".into(),
},
);
projects.insert(
"beta".into(),
ProjectEntry {
url: "http://b:3002".into(),
},
);
let config = GatewayConfig { projects };
let state = GatewayState::new(config).unwrap();
let active = state.active_project.blocking_read().clone();
@@ -587,7 +640,8 @@ url = "http://localhost:3002"
#[test]
fn gateway_tool_definitions_has_expected_tools() {
let defs = gateway_tool_definitions();
let names: Vec<&str> = defs.iter()
let names: Vec<&str> = defs
.iter()
.filter_map(|d| d.get("name").and_then(|n| n.as_str()))
.collect();
assert!(names.contains(&"switch_project"));
@@ -598,8 +652,18 @@ url = "http://localhost:3002"
#[tokio::test]
async fn switch_project_to_known_project() {
let mut projects = BTreeMap::new();
projects.insert("alpha".into(), ProjectEntry { url: "http://a:3001".into() });
projects.insert("beta".into(), ProjectEntry { url: "http://b:3002".into() });
projects.insert(
"alpha".into(),
ProjectEntry {
url: "http://a:3001".into(),
},
);
projects.insert(
"beta".into(),
ProjectEntry {
url: "http://b:3002".into(),
},
);
let config = GatewayConfig { projects };
let state = GatewayState::new(config).unwrap();
@@ -614,7 +678,12 @@ url = "http://localhost:3002"
#[tokio::test]
async fn switch_project_to_unknown_project_fails() {
let mut projects = BTreeMap::new();
projects.insert("alpha".into(), ProjectEntry { url: "http://a:3001".into() });
projects.insert(
"alpha".into(),
ProjectEntry {
url: "http://a:3001".into(),
},
);
let config = GatewayConfig { projects };
let state = GatewayState::new(config).unwrap();
@@ -626,7 +695,12 @@ url = "http://localhost:3002"
#[tokio::test]
async fn active_url_returns_correct_url() {
let mut projects = BTreeMap::new();
projects.insert("myproj".into(), ProjectEntry { url: "http://my:3001".into() });
projects.insert(
"myproj".into(),
ProjectEntry {
url: "http://my:3001".into(),
},
);
let config = GatewayConfig { projects };
let state = GatewayState::new(config).unwrap();
@@ -654,10 +728,14 @@ url = "http://localhost:3002"
fn load_config_from_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("projects.toml");
std::fs::write(&path, r#"
std::fs::write(
&path,
r#"
[projects.test]
url = "http://localhost:9999"
"#).unwrap();
"#,
)
.unwrap();
let config = GatewayConfig::load(&path).unwrap();
assert_eq!(config.projects.len(), 1);