use std::fmt::Write;
use cranpose_services::{SaveableScope, Saver};
use super::{BackStack, Entry};
const VERSION: &str = "1";
pub(super) fn encode<R>(stack: &BackStack<R>, saver: &Saver<R>) -> String {
let mut saved = format!("{VERSION} {}", stack.next_id);
for entry in &stack.entries {
let route = saver.save(&entry.route);
let _ = write!(saved, " {} {} {route}", entry.id, route.len());
}
saved
}
pub(super) fn decode<R>(
saved: &str,
saver: &Saver<R>,
saveables: &SaveableScope,
) -> Option<BackStack<R>> {
let rest = saved.strip_prefix(VERSION)?.strip_prefix(' ')?;
let (next_id, mut rest) = rest.split_once(' ')?;
let next_id: u64 = next_id.parse().ok()?;
let mut entries: Vec<Entry<R>> = Vec::new();
while let Some((id, route, tail)) = entry(rest) {
let in_order = id < next_id && entries.last().is_none_or(|last| last.id < id);
let Some(route) = saver.restore(route).filter(|_| in_order) else {
break;
};
entries.push(Entry::new(id, route, saveables));
rest = tail;
}
(!entries.is_empty()).then(|| BackStack {
entries,
next_id,
saveables: saveables.clone(),
})
}
fn entry(saved: &str) -> Option<(u64, &str, &str)> {
let (id, rest) = saved.split_once(' ')?;
let (length, rest) = rest.split_once(' ')?;
let (route, rest) = rest.split_at_checked(length.parse().ok()?)?;
let rest = match rest.strip_prefix(' ') {
Some(rest) => rest,
None if rest.is_empty() => rest,
None => return None,
};
Some((id.parse().ok()?, route, rest))
}