use std::fmt::Write as _;
const DEFAULT_MAX_SYNC_CREATES: usize = 5_000;
const DEFAULT_MAX_FIELD_BYTES: usize = 1024 * 1024;
fn env_usize(key: &str, default: usize) -> usize {
std::env::var(key)
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(default)
}
pub fn max_sync_creates() -> usize {
env_usize("BALLS_PLUGIN_MAX_SYNC_CREATES", DEFAULT_MAX_SYNC_CREATES)
}
pub fn max_field_bytes() -> usize {
env_usize("BALLS_PLUGIN_MAX_SYNC_FIELD_BYTES", DEFAULT_MAX_FIELD_BYTES)
}
fn clamp_to<T>(created: &[T], cap: usize) -> (&[T], usize) {
if created.len() <= cap {
(created, 0)
} else {
(&created[..cap], created.len() - cap)
}
}
pub fn clamp_creates<T>(created: &[T]) -> (&[T], usize) {
clamp_to(created, max_sync_creates())
}
fn truncate_to(s: &mut String, cap: usize) -> Option<usize> {
if s.len() <= cap {
return None;
}
let orig = s.len();
let mut end = cap;
while !s.is_char_boundary(end) {
end -= 1;
}
s.truncate(end);
let _ = write!(
s,
" […balls truncated this field: {orig} bytes over the \
{cap}-byte sync-ingest backstop; raise \
BALLS_PLUGIN_MAX_SYNC_FIELD_BYTES if this was a real payload]"
);
Some(orig)
}
pub fn truncate_field(s: &mut String) -> Option<usize> {
truncate_to(s, max_field_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clamp_under_and_at_cap_is_passthrough() {
let v = [1, 2, 3];
assert_eq!(clamp_to(&v, 5), (&v[..], 0));
assert_eq!(clamp_to(&v, 3), (&v[..], 0));
}
#[test]
fn clamp_over_cap_keeps_prefix_and_reports_dropped() {
let v = [1, 2, 3, 4, 5];
let (kept, dropped) = clamp_to(&v, 2);
assert_eq!(kept, &[1, 2]);
assert_eq!(dropped, 3);
}
#[test]
fn truncate_within_budget_is_noop() {
let mut s = "small".to_string();
assert_eq!(truncate_to(&mut s, 1024), None);
assert_eq!(s, "small");
}
#[test]
fn truncate_on_ascii_boundary_keeps_prefix_and_marks() {
let mut s = "A".repeat(100);
let orig = truncate_to(&mut s, 10);
assert_eq!(orig, Some(100));
assert!(s.starts_with("AAAAAAAAAA"));
assert!(s.contains("balls truncated this field: 100 bytes"));
}
#[test]
fn truncate_backtracks_off_a_multibyte_char() {
let s0 = "testé more text here padding padding".to_string();
let mut s = s0.clone();
let orig = truncate_to(&mut s, 5).unwrap();
assert_eq!(orig, s0.len());
assert!(s.starts_with("test"));
assert!(!s.starts_with("testé"));
assert!(s.contains("balls truncated this field"));
}
}