use super::plan::TransportDialect;
use super::row::{FIELD_SEPARATOR, encode_like_tmux};
use super::{FormatPlan, ListProfile};
use crate::snapshot::{
hydrate_client_infos_from_stdout, hydrate_pane_projections_from_stdout,
hydrate_session_infos_from_stdout, hydrate_window_projections_from_stdout,
pane_projection_plan, window_projection_plan,
};
use crate::{ServerIdentity, TmuxVersion};
const Q_ESCAPED_BEFORE_3_8: &[u8] = b"|&;<>()$`\\\"'*?[# =%";
const Q_ESCAPED_FROM_3_8: &[u8] = b"|&;<>(){}$`\\\"'*?[# =%\n\t";
const RELEASES: [(&[u8], &[u8]); 4] = [
(b"tmux 3.2a\n", Q_ESCAPED_BEFORE_3_8),
(b"tmux 3.5\n", Q_ESCAPED_BEFORE_3_8),
(b"tmux 3.7d\n", Q_ESCAPED_BEFORE_3_8),
(b"tmux next-3.9\n", Q_ESCAPED_FROM_3_8),
];
#[doc(hidden)]
pub fn __fuzz_format_rows(data: &[u8]) {
let Some((&selector, rest)) = data.split_first() else {
return;
};
let (release, escaped) = RELEASES[usize::from(selector & 0b11)];
let Ok(version) = TmuxVersion::parse_output(release) else {
return;
};
let identity = ServerIdentity::from_socket_path("/fuzz".into());
let plan = match (selector >> 2) & 0b11 {
0 => {
let plan = FormatPlan::for_profile(ListProfile::Sessions, &version);
let _ = hydrate_session_infos_from_stdout(&plan, rest);
plan
}
1 => {
let plan = FormatPlan::for_profile(ListProfile::Clients, &version);
let _ = hydrate_client_infos_from_stdout(&plan, rest);
plan
}
2 => {
let Ok(plan) = window_projection_plan(&version) else {
return;
};
let _ = hydrate_window_projections_from_stdout(&identity, &plan, rest);
plan
}
_ => {
let Ok(plan) = pane_projection_plan(&version) else {
return;
};
let _ = hydrate_pane_projections_from_stdout(&identity, &plan, rest);
plan
}
};
round_trip(&plan, rest, escaped);
}
fn round_trip(plan: &FormatPlan, values: &[u8], escaped: &[u8]) {
let fields = plan.descriptors.len();
let mut expected: Vec<&[u8]> = values.split(|&byte| byte == 0).collect();
expected.resize(expected.len().next_multiple_of(fields), b"");
let mut wire = Vec::new();
for (field, value) in expected.iter().enumerate() {
encode_like_tmux(value, escaped, plan.dialect, &mut wire);
wire.push(FIELD_SEPARATOR);
if field % fields == fields - 1 {
wire.push(b'\n');
}
}
let rows = plan.parse_rows(&wire);
let dialect = plan.dialect == TransportDialect::Vis;
assert!(
rows.is_ok(),
"{:?} decoding {wire:?} (vis: {dialect})",
rows.as_ref().err()
);
let decoded: Vec<Vec<u8>> = rows
.iter()
.flatten()
.flat_map(|row| row.slots().map(|slot| slot.as_bytes().to_vec()))
.collect();
assert_eq!(decoded, expected, "{wire:?} (vis: {dialect})");
}