Files
endorphin-tessera-gui/src/shell/activity.rs
T
2026-10-25 21:25:43 +08:00

91 lines
2.6 KiB
Rust

//! Bounded observation of this process's local HTTP API, never external traffic.
use std::{
collections::VecDeque,
sync::{Arc, Mutex},
time::{Instant, SystemTime, UNIX_EPOCH},
};
pub type Activity = Arc<Mutex<ActivityLog>>;
pub const BODY_PREVIEW: usize = 32 * 1024;
const MAX_RECORDS: usize = 80;
#[derive(Clone, Debug)]
pub struct Exchange {
pub id: u64,
pub method: String,
pub path: String,
pub origin: String,
pub timestamp: String,
pub request: String,
pub response: String,
pub status: Option<u16>,
pub elapsed_ms: u128,
pub request_headers: String,
pub response_headers: String,
started: Instant,
}
#[derive(Debug, Default)]
pub struct ActivityLog {
pub records: VecDeque<Exchange>,
next_id: u64,
}
impl ActivityLog {
pub fn begin(&mut self, method: String, path: String, origin: String, headers: String) -> u64 {
self.next_id += 1;
let id = self.next_id;
let seconds = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
% 86400;
self.records.push_back(Exchange {
id,
method,
path: preview(&path),
origin,
timestamp: format!(
"{:02}:{:02}:{:02} UTC",
seconds / 3600,
seconds / 60 % 60,
seconds % 60
),
request: String::new(),
response: String::new(),
status: None,
elapsed_ms: 0,
request_headers: preview(&headers),
response_headers: String::new(),
started: Instant::now(),
});
while self.records.len() > MAX_RECORDS {
self.records.pop_front();
}
id
}
pub fn body(&mut self, id: u64, body: &str) {
if let Some(record) = self.records.iter_mut().find(|r| r.id == id) {
record.request = preview(body);
}
}
pub fn finish(&mut self, id: u64, status: u16, body: &str, headers: String) {
if let Some(record) = self.records.iter_mut().find(|r| r.id == id) {
record.status = Some(status);
record.response = preview(body);
record.elapsed_ms = record.started.elapsed().as_millis();
record.response_headers = preview(&headers);
}
}
}
pub fn preview(value: &str) -> String {
if value.len() <= BODY_PREVIEW {
return value.to_owned();
}
let mut end = BODY_PREVIEW;
while !value.is_char_boundary(end) {
end -= 1;
}
format!("{}\n[preview truncated at 32 KiB]", &value[..end])
}