use super::*;
pub(crate) fn spine_type_wanted(raw: &[u8]) -> bool {
matches!(
raw,
br#""user""#
| br#""assistant""#
| br#""attachment""#
| br#""system""#
| br#""last-prompt""#
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpineKind {
User,
Assistant,
Attachment,
System,
LastPrompt,
}
impl SpineKind {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
SpineKind::User => "user",
SpineKind::Assistant => "assistant",
SpineKind::Attachment => "attachment",
SpineKind::System => "system",
SpineKind::LastPrompt => "last-prompt",
}
}
pub(crate) fn from_raw(raw: &[u8]) -> Option<SpineKind> {
match raw {
br#""user""# => Some(SpineKind::User),
br#""assistant""# => Some(SpineKind::Assistant),
br#""attachment""# => Some(SpineKind::Attachment),
br#""system""# => Some(SpineKind::System),
br#""last-prompt""# => Some(SpineKind::LastPrompt),
_ => None,
}
}
pub(crate) fn from_type(t: Option<&str>) -> Option<SpineKind> {
match t? {
"user" => Some(SpineKind::User),
"assistant" => Some(SpineKind::Assistant),
"attachment" => Some(SpineKind::Attachment),
"system" => Some(SpineKind::System),
"last-prompt" => Some(SpineKind::LastPrompt),
_ => None,
}
}
}
#[derive(Debug, Default)]
pub struct SpineExtra {
pub(crate) subtype: Option<Box<str>>,
pub(crate) logical_parent_uuid: Option<Box<str>>,
pub(crate) leaf_uuid: Option<Box<str>>,
pub(crate) explicit: Option<bool>,
pub(crate) rewound: Option<bool>,
pub(crate) compact_metadata: Option<serde_json::Value>,
}
impl SpineExtra {
fn is_empty(&self) -> bool {
self.subtype.is_none()
&& self.logical_parent_uuid.is_none()
&& self.leaf_uuid.is_none()
&& self.explicit.is_none()
&& self.rewound.is_none()
&& self.compact_metadata.is_none()
}
}
#[derive(Debug)]
pub struct SpineRow {
pub(crate) line: usize,
pub(crate) kind: SpineKind,
pub(crate) is_sidechain: Option<bool>,
pub(crate) uuid: Option<Box<str>>,
pub(crate) parent_uuid: Option<Box<str>>,
pub(crate) timestamp: Option<Box<str>>,
pub(crate) extra: Option<Box<SpineExtra>>,
}
impl SpineRow {
#[must_use]
pub fn kind_str(&self) -> &'static str {
self.kind.as_str()
}
#[must_use]
pub fn line(&self) -> usize {
self.line
}
fn extra(&self) -> Option<&SpineExtra> {
self.extra.as_deref()
}
pub(crate) fn subtype(&self) -> Option<&str> {
self.extra()?.subtype.as_deref()
}
pub(crate) fn logical_parent_uuid(&self) -> Option<&str> {
self.extra()?.logical_parent_uuid.as_deref()
}
pub(crate) fn leaf_uuid(&self) -> Option<&str> {
self.extra()?.leaf_uuid.as_deref()
}
pub(crate) fn explicit(&self) -> Option<bool> {
self.extra()?.explicit
}
#[allow(dead_code)]
pub(crate) fn rewound(&self) -> Option<bool> {
self.extra()?.rewound
}
pub(crate) fn compact_metadata(&self) -> Option<&serde_json::Value> {
self.extra()?.compact_metadata.as_ref()
}
}
pub(crate) fn spine_record(line_no: usize, line: &[u8]) -> Option<SpineRow> {
spine_record_from(line_no, &spine_fields(line)?)
}
pub(crate) fn spine_record_from(line_no: usize, f: &SpineFields<'_>) -> Option<SpineRow> {
let kind = SpineKind::from_raw(f.r#type)?;
let extra = SpineExtra {
subtype: f.subtype.and_then(boxed_str),
logical_parent_uuid: f.logical_parent_uuid.and_then(boxed_str),
leaf_uuid: f.leaf_uuid.and_then(boxed_str),
explicit: f.explicit.and_then(bool_value),
rewound: f.rewound.and_then(bool_value),
compact_metadata: f
.compact_metadata
.and_then(|raw| serde_json::from_slice(raw).ok()),
};
Some(SpineRow {
line: line_no,
kind,
is_sidechain: f.is_sidechain.and_then(bool_value),
uuid: f.uuid.and_then(boxed_str),
parent_uuid: f.parent_uuid.and_then(boxed_str),
timestamp: f.timestamp.and_then(boxed_str),
extra: (!extra.is_empty()).then(|| Box::new(extra)),
})
}
pub(crate) fn spine_from_record(line_no: usize, rec: &Record) -> SpineRow {
let extra = SpineExtra {
subtype: rec.subtype.as_deref().map(Box::from),
logical_parent_uuid: rec.logical_parent_uuid.as_deref().map(Box::from),
leaf_uuid: rec.leaf_uuid.as_deref().map(Box::from),
explicit: rec.explicit,
rewound: rec.rewound,
compact_metadata: rec.compact_metadata.clone(),
};
SpineRow {
line: line_no,
kind: SpineKind::from_type(rec.r#type.as_deref()).unwrap_or(SpineKind::Attachment),
is_sidechain: rec.is_sidechain,
uuid: rec.uuid.as_deref().map(Box::from),
parent_uuid: rec.parent_uuid.as_deref().map(Box::from),
timestamp: rec.timestamp.as_deref().map(Box::from),
extra: (!extra.is_empty()).then(|| Box::new(extra)),
}
}
#[derive(Debug, Default)]
pub(crate) struct SpineFields<'a> {
pub(crate) r#type: &'a [u8],
pub(crate) subtype: Option<&'a [u8]>,
pub(crate) uuid: Option<&'a [u8]>,
pub(crate) parent_uuid: Option<&'a [u8]>,
pub(crate) logical_parent_uuid: Option<&'a [u8]>,
pub(crate) leaf_uuid: Option<&'a [u8]>,
pub(crate) timestamp: Option<&'a [u8]>,
pub(crate) is_sidechain: Option<&'a [u8]>,
pub(crate) explicit: Option<&'a [u8]>,
pub(crate) rewound: Option<&'a [u8]>,
pub(crate) compact_metadata: Option<&'a [u8]>,
}
pub(crate) fn spine_fields(line: &[u8]) -> Option<SpineFields<'_>> {
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 = SpineFields::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"type" => {
if !spine_type_wanted(raw) {
return None;
}
out.r#type = raw;
}
b"subtype" => out.subtype = Some(raw),
b"uuid" => out.uuid = Some(raw),
b"parentUuid" => out.parent_uuid = Some(raw),
b"logicalParentUuid" => out.logical_parent_uuid = Some(raw),
b"leafUuid" => out.leaf_uuid = Some(raw),
b"timestamp" => out.timestamp = Some(raw),
b"isSidechain" => out.is_sidechain = Some(raw),
b"explicit" => out.explicit = Some(raw),
b"rewound" => out.rewound = Some(raw),
b"compactMetadata" => out.compact_metadata = Some(raw),
_ => {}
}
}
(!out.r#type.is_empty()).then_some(out)
}
pub(crate) fn line_type_and_spine(
line_no: usize,
line: &[u8],
) -> std::result::Result<Option<(String, Option<SpineRow>)>, ()> {
if line.iter().all(u8::is_ascii_whitespace) {
return Ok(None);
}
#[derive(serde::Deserialize)]
struct Probe<'a> {
#[serde(rename = "type", borrow, default)]
r#type: Option<&'a serde_json::value::RawValue>,
#[serde(borrow, default)]
subtype: Option<&'a serde_json::value::RawValue>,
#[serde(borrow, default)]
uuid: Option<&'a serde_json::value::RawValue>,
#[serde(rename = "parentUuid", borrow, default)]
parent_uuid: Option<&'a serde_json::value::RawValue>,
#[serde(rename = "logicalParentUuid", borrow, default)]
logical_parent_uuid: Option<&'a serde_json::value::RawValue>,
#[serde(rename = "leafUuid", borrow, default)]
leaf_uuid: Option<&'a serde_json::value::RawValue>,
#[serde(borrow, default)]
timestamp: Option<&'a serde_json::value::RawValue>,
#[serde(rename = "isSidechain", borrow, default)]
is_sidechain: Option<&'a serde_json::value::RawValue>,
#[serde(borrow, default)]
explicit: Option<&'a serde_json::value::RawValue>,
#[serde(borrow, default)]
rewound: Option<&'a serde_json::value::RawValue>,
#[serde(rename = "compactMetadata", borrow, default)]
compact_metadata: Option<&'a serde_json::value::RawValue>,
}
let p: Probe = serde_json::from_slice(line).map_err(|_| ())?;
fn raw(v: Option<&serde_json::value::RawValue>) -> Option<&[u8]> {
v.map(|r| r.get().as_bytes())
}
let census = raw(p.r#type)
.and_then(str_value)
.unwrap_or_else(|| "(untyped)".to_string());
let Some(ty) = raw(p.r#type).filter(|t| spine_type_wanted(t)) else {
return Ok(Some((census, None)));
};
let fields = SpineFields {
r#type: ty,
subtype: raw(p.subtype),
uuid: raw(p.uuid),
parent_uuid: raw(p.parent_uuid),
logical_parent_uuid: raw(p.logical_parent_uuid),
leaf_uuid: raw(p.leaf_uuid),
timestamp: raw(p.timestamp),
is_sidechain: raw(p.is_sidechain),
explicit: raw(p.explicit),
rewound: raw(p.rewound),
compact_metadata: raw(p.compact_metadata),
};
Ok(Some((census, spine_record_from(line_no, &fields))))
}
pub(crate) fn skip_ws(b: &[u8], mut i: usize) -> usize {
while matches!(b.get(i), Some(c) if c.is_ascii_whitespace()) {
i += 1;
}
i
}
pub(crate) fn read_string_span(b: &[u8], i: usize) -> Option<(&[u8], usize)> {
let mut j = i + 1;
while j < b.len() {
match b[j] {
b'\\' => j += 2,
b'"' => return Some((&b[i + 1..j], j + 1)),
_ => j += 1,
}
}
None
}
pub(crate) fn skip_value(b: &[u8], i: usize) -> Option<usize> {
let mut j = i;
let mut depth = 0usize;
let mut in_str = false;
while j < b.len() {
if in_str {
j += memchr::memchr2(b'\\', b'"', &b[j..])?;
if b[j] == b'\\' {
j += 2;
continue;
}
in_str = false;
j += 1;
if depth == 0 {
return Some(j);
}
continue;
}
let c = b[j];
match c {
b'"' => {
in_str = true;
j += 1;
}
b'{' | b'[' => {
depth += 1;
j += 1;
}
b'}' | b']' => {
if depth == 0 {
return Some(j);
}
depth -= 1;
j += 1;
if depth == 0 {
return Some(j);
}
}
b',' if depth == 0 => return Some(j),
_ => j += 1,
}
}
(depth == 0 && !in_str).then_some(j)
}
pub(crate) fn str_value(raw: &[u8]) -> Option<String> {
if raw.first() != Some(&b'"') || raw.len() < 2 || raw.last() != Some(&b'"') {
return None;
}
let inner = &raw[1..raw.len() - 1];
if !inner.contains(&b'\\') {
return std::str::from_utf8(inner).ok().map(str::to_string);
}
serde_json::from_slice(raw).ok()
}
fn boxed_str(raw: &[u8]) -> Option<Box<str>> {
str_value(raw).map(String::into_boxed_str)
}
fn bool_value(raw: &[u8]) -> Option<bool> {
match raw {
b"true" => Some(true),
b"false" => Some(false),
_ => None,
}
}