use serde_json::{Map, Value};
pub const MAX_PAYLOAD_TEXT_BYTES: usize = 4096;
pub const MAX_ACTIVITY_DETAIL_CHARS: usize = 160;
pub const TRUNCATED_KEY: &str = "truncated";
#[must_use]
pub fn bound_text(text: &str) -> (String, bool) {
if text.len() <= MAX_PAYLOAD_TEXT_BYTES {
return (text.to_owned(), false);
}
let mut start = text.len() - MAX_PAYLOAD_TEXT_BYTES;
while start < text.len() && !text.is_char_boundary(start) {
start += 1;
}
(text[start..].to_owned(), true)
}
#[must_use]
pub fn bound_payload(payload: Map<String, Value>) -> Map<String, Value> {
let mut bounded = Map::new();
let mut truncated = false;
for (key, value) in payload {
match value {
Value::String(text) if text.len() > MAX_PAYLOAD_TEXT_BYTES => {
truncated = true;
let cut = floor_char_boundary(&text, MAX_PAYLOAD_TEXT_BYTES);
bounded.insert(key, Value::String(text[..cut].to_owned()));
}
other => {
bounded.insert(key, other);
}
}
}
if truncated {
bounded.insert(TRUNCATED_KEY.to_owned(), Value::Bool(true));
}
bounded
}
#[must_use]
pub fn bound_detail(detail: &str) -> (String, bool) {
let collapsed: String = detail.split_whitespace().collect::<Vec<_>>().join(" ");
if collapsed.chars().count() <= MAX_ACTIVITY_DETAIL_CHARS {
return (collapsed, false);
}
(
collapsed.chars().take(MAX_ACTIVITY_DETAIL_CHARS).collect(),
true,
)
}
fn floor_char_boundary(value: &str, at: usize) -> usize {
let mut index = at.min(value.len());
while index > 0 && !value.is_char_boundary(index) {
index -= 1;
}
index
}