1use std::fmt::{self, Display, Write};
2use std::mem::size_of;
3
4use anyhow::{anyhow, Result};
5use bytemuck::{NoUninit, Pod as PodTrait};
6use bytemuck_derive::{Pod, Zeroable};
7
8#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroable, Pod)]
9#[repr(C)]
10pub struct U32Pair(pub u32, pub u32);
11
12pub fn clone_vec_as_bytes<T: NoUninit>(input: &[T]) -> Vec<u8> {
13 bytemuck::cast_slice(input).to_vec()
14}
15
16pub fn vec_from_bytes<T: PodTrait>(bytes: &[u8]) -> Vec<T> {
17 if bytes.len() % size_of::<T>() != 0 {
18 panic!(
19 "vecT: got {} bytes, needed multiple of {}",
20 bytes.len(),
21 size_of::<T>()
22 );
23 }
24 bytemuck::cast_slice(bytes).to_vec()
25}
26
27pub fn limit_str(s: &str, max_len: usize) -> String {
28 limit_bytes(s.as_bytes(), max_len)
29}
30
31pub fn limit_bytes(s: &[u8], max_len: usize) -> String {
32 if s.len() > max_len {
33 format!("{}...", String::from_utf8_lossy(&s[0..max_len]))
34 } else {
35 String::from_utf8_lossy(s).to_string()
36 }
37}
38
39pub fn to_hex_string(bytes: &[u8]) -> String {
40 bytes
41 .iter()
42 .map(|b| format!("{:02x}", b))
43 .collect::<Vec<_>>()
44 .join("")
45}
46
47pub fn from_hex_string(s: &str) -> Result<Vec<u8>> {
48 let mut result = Vec::with_capacity(s.len() / 2);
49 let mut iter = s.chars();
50 while let Some(c1) = iter.next() {
51 let c2 = iter
52 .next()
53 .ok_or_else(|| anyhow!("expecting even number of chars"))?;
54 let byte = u8::from_str_radix(&format!("{}{}", c1, c2), 16)?;
55 result.push(byte);
56 }
57 Ok(result)
58}
59
60struct LimitedWriter<'a> {
61 buf: &'a mut Vec<u8>,
62 max_len: usize,
63}
64
65impl<'a> LimitedWriter<'a> {
66 fn new(buf: &'a mut Vec<u8>, max_len: usize) -> Self {
67 Self { buf, max_len }
68 }
69}
70
71impl Write for LimitedWriter<'_> {
72 fn write_str(&mut self, s: &str) -> fmt::Result {
73 let remaining = self.max_len.saturating_sub(self.buf.len());
74 if s.len() > remaining {
75 self.buf.extend_from_slice(&s.as_bytes()[..remaining]);
76 Err(fmt::Error)
77 } else {
78 self.buf.extend_from_slice(s.as_bytes());
79 Ok(())
80 }
81 }
82}
83
84pub fn limit_display(obj: impl Display, max_len: usize) -> String {
85 let mut buffer = Vec::new();
86 let mut writer = LimitedWriter::new(&mut buffer, max_len);
87
88 let r = write!(writer, "{}", obj);
89 let mut exceeded = r.is_err();
90 let mut valid_str = match String::from_utf8(buffer) {
91 Ok(s) => s,
92 Err(e) => {
93 exceeded = true;
94 let l = e.utf8_error().valid_up_to();
95 let mut buf = e.into_bytes();
96 buf.truncate(l);
97 String::from_utf8(buf).unwrap()
98 }
99 };
100
101 if exceeded {
102 valid_str.push_str("...");
103 }
104 valid_str
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_short_string() {
113 let result = limit_display("hello", 10);
114 assert_eq!(result, "hello");
115 }
116
117 #[test]
118 fn test_exact_length() {
119 let result = limit_display("1234567890", 10);
120 assert_eq!(result, "1234567890");
121 }
122
123 #[test]
124 fn test_truncate_with_ellipsis() {
125 let result = limit_display("This is a long string", 10);
126 assert_eq!(result, "This is a ...");
127 }
128
129 #[test]
130 fn test_utf8_truncation() {
131 let result = limit_display("😀😀😀😀😀", 10);
132 assert_eq!(result, "😀😀...");
133 }
134
135 #[test]
136 fn test_utf8_partial_char() {
137 let result = limit_display("😀😀😀", 7);
138 assert_eq!(result, "😀...");
139 }
140
141 #[test]
142 fn test_empty_string() {
143 let result = limit_display("", 10);
144 assert_eq!(result, "");
145 }
146
147 #[test]
148 fn test_very_small_limit() {
149 let result = limit_display("hello", 1);
150 assert_eq!(result, "h...");
151 }
152}