use std::collections::{BTreeMap, BTreeSet};
use crate::{NodeBodyDescriptor, TraceBodyOptions, TraceBodyState};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TraceBodyAdmission {
pub load: Vec<String>,
pub states: BTreeMap<String, TraceBodyState>,
pub admitted_record_bytes: u64,
pub selected_body_bytes: u64,
pub next_deferred: Option<(String, u64)>,
}
impl TraceBodyAdmission {
pub fn next_rerun_record_bytes(&self) -> Option<u64> {
self.next_deferred
.as_ref()
.map(|(_, bytes)| self.admitted_record_bytes + bytes)
}
pub fn next_named_record_bytes(&self) -> Option<u64> {
self.next_deferred.as_ref().map(|(_, bytes)| *bytes)
}
pub fn state(&self, node_id: &str) -> TraceBodyState {
self.states
.get(node_id)
.copied()
.unwrap_or(TraceBodyState::Missing)
}
pub fn omitted(&self) -> BTreeSet<String> {
self.states
.iter()
.filter(|(_, state)| state.is_omitted())
.map(|(id, _)| id.clone())
.collect()
}
}
pub fn admit(
manifest: &[String],
descriptors: &BTreeMap<String, NodeBodyDescriptor>,
options: &TraceBodyOptions,
reusable: &BTreeSet<String>,
) -> TraceBodyAdmission {
let mut result = TraceBodyAdmission::default();
let mut ceiling_reached = false;
for node_id in manifest {
let Some(descriptor) = descriptors.get(node_id) else {
result
.states
.insert(node_id.clone(), TraceBodyState::Missing);
continue;
};
result.selected_body_bytes += descriptor.body_bytes;
let named = options.refs.as_ref().map(|refs| refs.contains(node_id));
match named {
Some(false) => {
result.states.insert(
node_id.clone(),
state_without_body(node_id, options, reusable),
);
continue;
}
None if options.compact.is_some() => {
result.states.insert(
node_id.clone(),
state_without_body(node_id, options, reusable),
);
continue;
}
_ => {}
}
match options.max_record_bytes {
Some(ceiling)
if ceiling_reached
|| result.admitted_record_bytes + descriptor.record_bytes > ceiling =>
{
ceiling_reached = true;
result
.states
.insert(node_id.clone(), TraceBodyState::DeferredBudget);
if result.next_deferred.is_none() {
result.next_deferred = Some((node_id.clone(), descriptor.record_bytes));
}
}
_ => {
result.admitted_record_bytes += descriptor.record_bytes;
result
.states
.insert(node_id.clone(), TraceBodyState::Loaded);
result.load.push(node_id.clone());
}
}
}
result
}
fn state_without_body(
node_id: &str,
options: &TraceBodyOptions,
reusable: &BTreeSet<String>,
) -> TraceBodyState {
if options.compact.is_some() && reusable.contains(node_id) {
TraceBodyState::Compact
} else {
TraceBodyState::NotRequested
}
}
#[cfg(test)]
#[path = "trace_body_admission_tests.rs"]
mod tests;