use super::*;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(crate) struct LineageFields {
pub(crate) session_kind: Option<String>,
pub(crate) continued_in_session_id: Option<String>,
pub(crate) slug: Option<String>,
pub(crate) timestamp: Option<String>,
}
impl LineageFields {
pub(crate) fn no_session_lineage(&self) -> bool {
self.session_kind.is_none() && self.continued_in_session_id.is_none()
}
}
pub(crate) fn line_has_lineage_key(line: &[u8]) -> bool {
static KIND: std::sync::LazyLock<memchr::memmem::Finder<'static>> =
std::sync::LazyLock::new(|| memchr::memmem::Finder::new(b"\"sessionKind\""));
static CHILD: std::sync::LazyLock<memchr::memmem::Finder<'static>> =
std::sync::LazyLock::new(|| memchr::memmem::Finder::new(b"\"continuedInSessionId\""));
KIND.find(line).is_some() || CHILD.find(line).is_some()
}
pub(crate) fn line_has_slug_key(line: &[u8]) -> bool {
static SLUG: std::sync::LazyLock<memchr::memmem::Finder<'static>> =
std::sync::LazyLock::new(|| memchr::memmem::Finder::new(b"\"slug\""));
SLUG.find(line).is_some()
}
pub(crate) fn lineage_fields(line: &[u8]) -> Option<LineageFields> {
let payload = line_payload(line)?;
let mut i = skip_ws(payload, 0);
if payload.get(i) != Some(&b'{') {
return None;
}
i += 1;
let mut out = LineageFields::default();
loop {
i = skip_ws(payload, i);
match payload.get(i) {
Some(b'}') => break,
Some(b',') => {
i += 1;
continue;
}
Some(b'"') => {}
_ => return None,
}
let (key, after_key) = read_string_span(payload, i)?;
i = skip_ws(payload, after_key);
if payload.get(i) != Some(&b':') {
return None;
}
i = skip_ws(payload, i + 1);
let start = i;
i = skip_value(payload, i)?;
let raw = payload[start..i].trim_ascii_end();
match key {
b"sessionKind" => out.session_kind = str_value(raw),
b"continuedInSessionId" => out.continued_in_session_id = str_value(raw),
b"slug" => out.slug = str_value(raw).filter(|s| !s.is_empty()),
b"timestamp" => out.timestamp = str_value(raw),
_ => {}
}
}
Some(out)
}