use crate::store::MirrorPosition;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Served {
pub at: u64,
pub address: String,
pub bytes: u64,
}
pub fn parse(line: &str) -> Option<Served> {
let (timestamp, line) = line.split_once(' ')?;
let at = crate::time::parse_ns(timestamp)?;
let (address, rest) = line.split_once(" - - [")?;
let (_, rest) = rest.split_once("] \"")?;
let (_request, rest) = rest.split_once("\" ")?;
let mut fields = rest.split(' ');
let _status: u16 = fields.next()?.parse().ok()?;
let bytes = match fields.next()? {
"-" => 0,
bytes => bytes.parse().ok()?,
};
if address.parse::<std::net::IpAddr>().is_err() {
return None;
}
Some(Served {
at,
address: address.to_owned(),
bytes,
})
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Lease {
address: String,
runner: String,
from: u64,
until: Option<u64>,
}
#[derive(Debug, Default)]
pub struct Leases {
leases: Vec<Lease>,
}
impl Leases {
pub fn start(&mut self, address: &str, runner: &str, from: u64) {
self.leases.push(Lease {
address: address.to_owned(),
runner: runner.to_owned(),
from,
until: None,
});
}
pub fn end(&mut self, runner: &str, until: u64) {
for lease in &mut self.leases {
if lease.runner == runner && lease.until.is_none() {
lease.until = Some(until);
}
}
}
pub fn forget_before(&mut self, before: u64) {
self.leases
.retain(|lease| lease.until.is_none_or(|until| until >= before));
}
pub fn holder(&self, address: &str, at: u64) -> Option<&str> {
self.leases
.iter()
.find(|lease| {
lease.address == address
&& lease.from <= at
&& lease.until.is_none_or(|until| at < until)
})
.map(|lease| lease.runner.as_str())
}
pub fn attribute(
&self,
log: &str,
after: MirrorPosition,
) -> (Vec<(String, u64)>, MirrorPosition) {
let mut totals: Vec<(String, u64)> = Vec::new();
let mut until = after;
let mut at_after = 0;
for served in log.lines().filter_map(parse) {
if served.at < after.at {
continue;
}
if served.at == after.at {
at_after += 1;
if at_after <= after.seen {
continue;
}
}
if served.at == until.at {
until.seen += 1;
} else {
until = MirrorPosition {
at: served.at,
seen: 1,
};
}
let Some(runner) = self.holder(&served.address, served.at / 1_000_000) else {
continue;
};
match totals.iter_mut().find(|(name, _)| name == runner) {
Some((_, total)) => *total += served.bytes,
None => totals.push((runner.to_owned(), served.bytes)),
}
}
(totals, until)
}
}