use std::collections::HashMap;
use serde::{Serialize, Serializer};
use crate::entry::{EntryId, IsoTimestamp, SessionEntry};
#[derive(Clone, Debug, PartialEq)]
pub struct SessionTreeNode {
pub entry: SessionEntry,
pub children: Vec<SessionTreeNode>,
pub label: Option<String>,
pub label_timestamp: Option<IsoTimestamp>,
}
impl Drop for SessionTreeNode {
fn drop(&mut self) {
let mut stack = std::mem::take(&mut self.children);
while let Some(mut node) = stack.pop() {
stack.append(&mut node.children);
}
}
}
impl Serialize for SessionTreeNode {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("entry", &self.entry)?;
map.serialize_entry("children", &self.children)?;
if let Some(label) = &self.label {
map.serialize_entry("label", label)?;
}
if let Some(timestamp) = &self.label_timestamp {
map.serialize_entry("labelTimestamp", timestamp)?;
}
map.end()
}
}
pub(super) fn build(
entries: &[SessionEntry],
labels: &HashMap<EntryId, (String, IsoTimestamp)>,
) -> Vec<SessionTreeNode> {
let position: HashMap<&EntryId, usize> = entries
.iter()
.enumerate()
.map(|(i, e)| (&e.id, i))
.collect();
let mut children: Vec<Vec<usize>> = vec![Vec::new(); entries.len()];
let mut roots = Vec::new();
for (index, entry) in entries.iter().enumerate() {
match entry
.parent_id
.as_ref()
.filter(|parent| **parent != entry.id)
.and_then(|parent| position.get(parent))
{
Some(&parent) if parent != index => children[parent].push(index),
_ => roots.push(index),
}
}
let millis: Vec<i64> = entries
.iter()
.map(|e| e.timestamp.to_ms().unwrap_or(0))
.collect();
for list in &mut children {
list.sort_by_key(|&index| millis[index]);
}
let mut order = Vec::with_capacity(entries.len());
let mut stack: Vec<usize> = roots.clone();
let mut visited = vec![false; entries.len()];
while let Some(index) = stack.pop() {
if std::mem::replace(&mut visited[index], true) {
continue;
}
order.push(index);
stack.extend(children[index].iter().copied());
}
let mut built: Vec<Option<SessionTreeNode>> = (0..entries.len()).map(|_| None).collect();
for &index in order.iter().rev() {
let entry = &entries[index];
let kids = children[index]
.iter()
.filter_map(|&child| built[child].take())
.collect();
let label = labels.get(&entry.id);
built[index] = Some(SessionTreeNode {
entry: entry.clone(),
children: kids,
label: label.map(|(text, _)| text.clone()),
label_timestamp: label.map(|(_, ts)| ts.clone()),
});
}
roots
.into_iter()
.filter_map(|index| built[index].take())
.collect()
}