Small refactoring to make clippy happy

This commit is contained in:
Armin Ronacher 2025-06-16 21:47:49 +02:00
parent 71f49142ea
commit 1423902539
2 changed files with 129 additions and 139 deletions

View file

@ -4,8 +4,6 @@ use jiff::Timestamp;
use notify::{Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use notify::{Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json;
use serde_urlencoded;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
@ -307,7 +305,7 @@ pub fn start_server(
} }
// Check for static file serving first // Check for static file serving first
if method == &Method::GET && !path.starts_with("/api/") { if method == Method::GET && !path.starts_with("/api/") {
if let Some(ref static_dir) = static_path { if let Some(ref static_dir) = static_path {
let static_dir_path = Path::new(static_dir); let static_dir_path = Path::new(static_dir);
println!( println!(
@ -342,25 +340,25 @@ pub fn start_server(
if path.starts_with("/api/sessions/") && path.ends_with("/stream") => if path.starts_with("/api/sessions/") && path.ends_with("/stream") =>
{ {
// Handle streaming differently - bypass normal response handling // Handle streaming differently - bypass normal response handling
return handle_session_stream_direct(&control_path, &path, &mut req); return handle_session_stream_direct(&control_path, path, &mut req);
} }
(&Method::GET, path) (&Method::GET, path)
if path.starts_with("/api/sessions/") && path.ends_with("/snapshot") => if path.starts_with("/api/sessions/") && path.ends_with("/snapshot") =>
{ {
handle_session_snapshot(&control_path, &path) handle_session_snapshot(&control_path, path)
} }
(&Method::POST, path) (&Method::POST, path)
if path.starts_with("/api/sessions/") && path.ends_with("/input") => if path.starts_with("/api/sessions/") && path.ends_with("/input") =>
{ {
handle_session_input(&control_path, &path, &mut req) handle_session_input(&control_path, path, &mut req)
} }
(&Method::DELETE, path) (&Method::DELETE, path)
if path.starts_with("/api/sessions/") && path.ends_with("/cleanup") => if path.starts_with("/api/sessions/") && path.ends_with("/cleanup") =>
{ {
handle_session_cleanup(&control_path, &path) handle_session_cleanup(&control_path, path)
} }
(&Method::DELETE, path) if path.starts_with("/api/sessions/") => { (&Method::DELETE, path) if path.starts_with("/api/sessions/") => {
handle_session_kill(&control_path, &path) handle_session_kill(&control_path, path)
} }
_ => { _ => {
let error = ApiResponse { let error = ApiResponse {
@ -407,7 +405,7 @@ fn handle_health() -> Response<String> {
json_response(StatusCode::OK, &response) json_response(StatusCode::OK, &response)
} }
fn handle_list_sessions(control_path: &PathBuf) -> Response<String> { fn handle_list_sessions(control_path: &Path) -> Response<String> {
match sessions::list_sessions(control_path) { match sessions::list_sessions(control_path) {
Ok(sessions) => { Ok(sessions) => {
let mut session_responses = Vec::new(); let mut session_responses = Vec::new();
@ -450,7 +448,7 @@ fn handle_list_sessions(control_path: &PathBuf) -> Response<String> {
} }
fn handle_create_session( fn handle_create_session(
control_path: &PathBuf, control_path: &Path,
req: &mut crate::http_server::HttpRequest, req: &mut crate::http_server::HttpRequest,
) -> Response<String> { ) -> Response<String> {
// Read the request body // Read the request body
@ -501,16 +499,16 @@ fn handle_create_session(
let cmdline: Vec<std::ffi::OsString> = create_request let cmdline: Vec<std::ffi::OsString> = create_request
.command .command
.iter() .iter()
.map(|s| std::ffi::OsString::from(s)) .map(std::ffi::OsString::from)
.collect(); .collect();
// Set working directory if specified, with tilde expansion // Set working directory if specified, with tilde expansion
let current_dir = if let Some(ref working_dir) = create_request.working_dir { let current_dir = if let Some(ref working_dir) = create_request.working_dir {
// Expand ~ to home directory if needed // Expand ~ to home directory if needed
let expanded_dir = if working_dir.starts_with('~') { let expanded_dir = if let Some(remaining_path) = working_dir.strip_prefix('~') {
if let Some(home_dir) = std::env::var_os("HOME") { if let Some(home_dir) = std::env::var_os("HOME") {
let home_path = std::path::Path::new(&home_dir); let home_path = std::path::Path::new(&home_dir);
let remaining_path = &working_dir[1..]; // Remove the ~ character // Remove the ~ character
if remaining_path.is_empty() { if remaining_path.is_empty() {
home_path.to_path_buf() home_path.to_path_buf()
} else { } else {
@ -545,7 +543,7 @@ fn handle_create_session(
}; };
// Spawn the process in a detached manner using a separate thread // Spawn the process in a detached manner using a separate thread
let control_path_clone = control_path.clone(); let control_path_clone = control_path.to_path_buf();
let session_id_clone = session_id.clone(); let session_id_clone = session_id.clone();
let cmdline_clone = cmdline.clone(); let cmdline_clone = cmdline.clone();
let working_dir_clone = current_dir.clone(); let working_dir_clone = current_dir.clone();
@ -599,7 +597,7 @@ fn handle_create_session(
let session_name = cmdline_clone let session_name = cmdline_clone
.first() .first()
.and_then(|cmd| cmd.to_str()) .and_then(|cmd| cmd.to_str())
.map(|s| s.split('/').last().unwrap_or(s)) .map(|s| s.split('/').next_back().unwrap_or(s))
.unwrap_or("unknown") .unwrap_or("unknown")
.to_string(); .to_string();
tty_spawn.session_name(session_name); tty_spawn.session_name(session_name);
@ -640,7 +638,7 @@ fn handle_create_session(
json_response(StatusCode::OK, &response) json_response(StatusCode::OK, &response)
} }
fn handle_cleanup_exited(control_path: &PathBuf) -> Response<String> { fn handle_cleanup_exited(control_path: &Path) -> Response<String> {
match sessions::cleanup_sessions(control_path, None) { match sessions::cleanup_sessions(control_path, None) {
Ok(_) => { Ok(_) => {
let response = ApiResponse { let response = ApiResponse {
@ -663,7 +661,7 @@ fn handle_cleanup_exited(control_path: &PathBuf) -> Response<String> {
} }
} }
fn handle_session_snapshot(control_path: &PathBuf, path: &str) -> Response<String> { fn handle_session_snapshot(control_path: &Path, path: &str) -> Response<String> {
if let Some(session_id) = extract_session_id(path) { if let Some(session_id) = extract_session_id(path) {
let stream_path = control_path.join(&session_id).join("stream-out"); let stream_path = control_path.join(&session_id).join("stream-out");
@ -695,7 +693,7 @@ fn handle_session_snapshot(control_path: &PathBuf, path: &str) -> Response<Strin
} }
fn handle_session_input( fn handle_session_input(
control_path: &PathBuf, control_path: &Path,
path: &str, path: &str,
req: &mut crate::http_server::HttpRequest, req: &mut crate::http_server::HttpRequest,
) -> Response<String> { ) -> Response<String> {
@ -833,94 +831,87 @@ fn handle_session_input(
} }
} }
fn handle_session_kill(control_path: &PathBuf, path: &str) -> Response<String> { fn handle_session_kill(control_path: &Path, path: &str) -> Response<String> {
if let Some(session_id) = extract_session_id(path) { let session_id = match extract_session_id(path) {
// First check if session exists by listing sessions Some(id) => id,
match sessions::list_sessions(control_path) { None => {
Ok(sessions) => { let response = ApiResponse {
if let Some(session_entry) = sessions.get(&session_id) { success: None,
// Session exists, try to kill it message: None,
if let Some(_) = session_entry.session_info.pid { error: Some("Invalid session ID".to_string()),
// First try SIGTERM, then SIGKILL if needed (like Node.js version) session_id: None,
match sessions::send_signal_to_session(control_path, &session_id, 15) { };
Ok(_) => { return json_response(StatusCode::BAD_REQUEST, &response);
// Successfully sent SIGTERM
let response = ApiResponse {
success: Some(true),
message: Some("Session killed (SIGTERM)".to_string()),
error: None,
session_id: None,
};
json_response(StatusCode::OK, &response)
}
Err(_) => {
// SIGTERM failed, try SIGKILL
match sessions::send_signal_to_session(control_path, &session_id, 9)
{
Ok(_) => {
let response = ApiResponse {
success: Some(true),
message: Some("Session killed (SIGKILL)".to_string()),
error: None,
session_id: None,
};
json_response(StatusCode::OK, &response)
}
Err(e) => {
let error = ApiResponse {
success: None,
message: None,
error: Some(format!("Failed to kill session: {}", e)),
session_id: None,
};
json_response(StatusCode::GONE, &error)
}
}
}
}
} else {
// Session has no PID, consider it already dead
let response = ApiResponse {
success: Some(true),
message: Some("Session killed".to_string()),
error: None,
session_id: None,
};
json_response(StatusCode::OK, &response)
}
} else {
// Session not found
let error = ApiResponse {
success: None,
message: None,
error: Some("Session not found".to_string()),
session_id: None,
};
json_response(StatusCode::NOT_FOUND, &error)
}
}
Err(e) => {
let error = ApiResponse {
success: None,
message: None,
error: Some(format!("Failed to list sessions: {}", e)),
session_id: None,
};
json_response(StatusCode::INTERNAL_SERVER_ERROR, &error)
}
} }
} else { };
let error = ApiResponse {
success: None, let sessions = match sessions::list_sessions(control_path) {
message: None, Ok(sessions) => sessions,
error: Some("Invalid session ID".to_string()), Err(e) => {
let response = ApiResponse {
success: None,
message: None,
error: Some(format!("Failed to list sessions: {}", e)),
session_id: None,
};
return json_response(StatusCode::INTERNAL_SERVER_ERROR, &response);
}
};
let session_entry = match sessions.get(&session_id) {
Some(entry) => entry,
None => {
let response = ApiResponse {
success: None,
message: None,
error: Some("Session not found".to_string()),
session_id: None,
};
return json_response(StatusCode::NOT_FOUND, &response);
}
};
// If session has no PID, consider it already dead
if session_entry.session_info.pid.is_none() {
let response = ApiResponse {
success: Some(true),
message: Some("Session killed".to_string()),
error: None,
session_id: None, session_id: None,
}; };
json_response(StatusCode::BAD_REQUEST, &error) return json_response(StatusCode::OK, &response);
} };
// Try SIGTERM first, then SIGKILL if needed
let (status, message) = match sessions::send_signal_to_session(control_path, &session_id, 15) {
Ok(_) => (StatusCode::OK, "Session killed (SIGTERM)"),
Err(_) => {
// SIGTERM failed, try SIGKILL
match sessions::send_signal_to_session(control_path, &session_id, 9) {
Ok(_) => (StatusCode::OK, "Session killed (SIGKILL)"),
Err(e) => {
let response = ApiResponse {
success: None,
message: None,
error: Some(format!("Failed to kill session: {}", e)),
session_id: None,
};
return json_response(StatusCode::GONE, &response);
}
}
}
};
let response = ApiResponse {
success: Some(true),
message: Some(message.to_string()),
error: None,
session_id: None,
};
json_response(status, &response)
} }
fn handle_session_cleanup(control_path: &PathBuf, path: &str) -> Response<String> { fn handle_session_cleanup(control_path: &Path, path: &str) -> Response<String> {
if let Some(session_id) = extract_session_id(path) { if let Some(session_id) = extract_session_id(path) {
match sessions::cleanup_sessions(control_path, Some(&session_id)) { match sessions::cleanup_sessions(control_path, Some(&session_id)) {
Ok(_) => { Ok(_) => {
@ -965,7 +956,7 @@ fn get_last_modified(file_path: &str) -> Option<String> {
.ok() .ok()
} }
fn handle_session_stream_direct(control_path: &PathBuf, path: &str, req: &mut HttpRequest) { fn handle_session_stream_direct(control_path: &Path, path: &str, req: &mut HttpRequest) {
let session_id = match extract_session_id(path) { let session_id = match extract_session_id(path) {
Some(id) => id, Some(id) => id,
None => { None => {
@ -1141,7 +1132,7 @@ fn handle_session_stream_direct(control_path: &PathBuf, path: &str, req: &mut Ht
let stream_path_clone = stream_out_path.clone(); let stream_path_clone = stream_out_path.clone();
match Command::new("tail") match Command::new("tail")
.args(&["-f", &stream_path_clone]) .args(["-f", &stream_path_clone])
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
{ {
@ -1240,7 +1231,7 @@ fn handle_session_stream_direct(control_path: &PathBuf, path: &str, req: &mut Ht
println!("Ended streaming SSE for session {}", session_id); println!("Ended streaming SSE for session {}", session_id);
} }
fn handle_stream_all_sessions(control_path: &PathBuf, req: &mut HttpRequest) { fn handle_stream_all_sessions(control_path: &Path, req: &mut HttpRequest) {
println!("Starting streaming SSE for all sessions"); println!("Starting streaming SSE for all sessions");
// Send SSE headers // Send SSE headers
@ -1327,7 +1318,7 @@ fn handle_stream_all_sessions(control_path: &PathBuf, req: &mut HttpRequest) {
} }
// Set up filesystem watcher for new sessions // Set up filesystem watcher for new sessions
let control_path_clone = control_path.clone(); let control_path_clone = control_path.to_path_buf();
let tx_watcher = tx.clone(); let tx_watcher = tx.clone();
let sessions_watcher = tracked_sessions.clone(); let sessions_watcher = tracked_sessions.clone();
@ -1457,7 +1448,7 @@ fn stream_session_continuously(stream_path: &str, session_id: &str, tx: mpsc::Se
// Use tail -f to stream new content // Use tail -f to stream new content
match Command::new("tail") match Command::new("tail")
.args(&["-f", stream_path]) .args(["-f", stream_path])
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
{ {
@ -1629,39 +1620,37 @@ fn handle_browse(req: &mut crate::http_server::HttpRequest) -> Response<String>
}; };
let mut files = Vec::new(); let mut files = Vec::new();
for entry in entries { for entry in entries.flatten() {
if let Ok(entry) = entry { if let Ok(file_metadata) = entry.metadata() {
if let Ok(file_metadata) = entry.metadata() { let name = entry.file_name().to_string_lossy().to_string();
let name = entry.file_name().to_string_lossy().to_string();
fn system_time_to_iso_string(time: SystemTime) -> String { fn system_time_to_iso_string(time: SystemTime) -> String {
let duration = time let duration = time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default(); .unwrap_or_default();
let timestamp = Timestamp::from_second(duration.as_secs() as i64) let timestamp = Timestamp::from_second(duration.as_secs() as i64)
.unwrap_or_else(|_| Timestamp::UNIX_EPOCH); .unwrap_or(Timestamp::UNIX_EPOCH);
timestamp.to_string() timestamp.to_string()
}
let created = file_metadata
.created()
.or_else(|_| file_metadata.modified())
.map(system_time_to_iso_string)
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string());
let last_modified = file_metadata
.modified()
.map(system_time_to_iso_string)
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string());
files.push(FileInfo {
name,
created,
last_modified,
size: file_metadata.len(),
is_dir: file_metadata.is_dir(),
});
} }
let created = file_metadata
.created()
.or_else(|_| file_metadata.modified())
.map(system_time_to_iso_string)
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string());
let last_modified = file_metadata
.modified()
.map(system_time_to_iso_string)
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string());
files.push(FileInfo {
name,
created,
last_modified,
size: file_metadata.len(),
is_dir: file_metadata.is_dir(),
});
} }
} }

View file

@ -497,6 +497,7 @@ fn spawn(mut opts: SpawnOptions) -> Result<i32, Errno> {
match execvp(&args[0], &args)? {} match execvp(&args[0], &args)? {}
} }
#[allow(clippy::too_many_arguments)]
fn communication_loop( fn communication_loop(
master: OwnedFd, master: OwnedFd,
child: Pid, child: Pid,
@ -572,7 +573,7 @@ fn communication_loop(
}), }),
}; };
if let Err(_) = notification_writer.write_notification(event) { if notification_writer.write_notification(event).is_err() {
// Ignore notification write errors to not interrupt the main flow // Ignore notification write errors to not interrupt the main flow
} }
input_notification_sent = true; input_notification_sent = true;
@ -829,7 +830,7 @@ fn monitor_detached_session(
}), }),
}; };
if let Err(_) = notification_writer.write_notification(event) { if notification_writer.write_notification(event).is_err() {
// Ignore notification write errors to not interrupt the main flow // Ignore notification write errors to not interrupt the main flow
} }
input_notification_sent = true; input_notification_sent = true;