1use compact_str::CompactString;
4use serde::Serialize;
5
6#[derive(Debug)]
8pub struct CopyRow {
9 buf: Vec<u8>,
10 row_start: bool,
11}
12
13impl CopyRow {
14 #[must_use]
16 pub fn new() -> Self {
17 Self {
18 buf: Vec::new(),
19 row_start: true,
20 }
21 }
22
23 pub fn push_bind(&mut self, value: impl CopyField) -> &mut Self {
25 if !self.row_start {
26 self.buf.push(b'\t');
27 }
28
29 self.row_start = false;
30 value.encode(&mut self.buf);
31
32 self
33 }
34
35 pub fn finish_row(&mut self) {
37 self.buf.push(b'\n');
38 self.row_start = true;
39 }
40
41 #[must_use]
43 pub fn len(&self) -> usize {
44 self.buf.len()
45 }
46
47 #[must_use]
49 pub fn is_empty(&self) -> bool {
50 self.buf.is_empty()
51 }
52
53 pub fn take(&mut self) -> Vec<u8> {
55 std::mem::take(&mut self.buf)
56 }
57}
58
59impl Default for CopyRow {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65pub trait CopyField {
67 fn encode(&self, buf: &mut Vec<u8>);
69}
70
71fn encode_text(s: &str, buf: &mut Vec<u8>) {
72 for &b in s.as_bytes() {
73 match b {
74 b'\\' => buf.extend_from_slice(b"\\\\"),
75 b'\t' => buf.extend_from_slice(b"\\t"),
76 b'\n' => buf.extend_from_slice(b"\\n"),
77 b'\r' => buf.extend_from_slice(b"\\r"),
78 _ => buf.push(b),
79 }
80 }
81}
82
83#[expect(
84 clippy::expect_used,
85 reason = "std::io::Write for Vec<u8> is infallible because extending a vector reports allocation failure by aborting"
86)]
87fn encode_display(value: impl std::fmt::Display, buf: &mut Vec<u8>) {
88 use std::io::Write;
89
90 write!(buf, "{value}").expect("writing to Vec<u8> is infallible");
91}
92
93impl<T> CopyField for &T
94where
95 T: CopyField + ?Sized,
96{
97 fn encode(&self, buf: &mut Vec<u8>) {
98 (**self).encode(buf);
99 }
100}
101
102impl<T> CopyField for Option<T>
103where
104 T: CopyField,
105{
106 fn encode(&self, buf: &mut Vec<u8>) {
107 match self {
108 Some(v) => v.encode(buf),
109 None => buf.extend_from_slice(b"\\N"),
110 }
111 }
112}
113
114impl CopyField for bool {
115 fn encode(&self, buf: &mut Vec<u8>) {
116 buf.push(if *self { b't' } else { b'f' });
117 }
118}
119
120macro_rules! copy_field_int {
121 ($($ty:ty),+) => {$(
122 impl CopyField for $ty {
123 fn encode(&self, buf: &mut Vec<u8>) {
124 encode_display(self, buf);
125 }
126 }
127 )+};
128}
129
130copy_field_int!(i16, i32, i64);
131
132macro_rules! copy_field_float {
133 ($($ty:ty),+) => {$(
134 impl CopyField for $ty {
135 fn encode(&self, buf: &mut Vec<u8>) {
136 if self.is_nan() {
137 buf.extend_from_slice(b"NaN");
138 } else if self.is_infinite() {
139 buf.extend_from_slice(if self.is_sign_positive() {
140 b"Infinity"
141 } else {
142 b"-Infinity"
143 });
144 } else {
145 encode_display(self, buf);
146 }
147 }
148 }
149 )+};
150}
151
152copy_field_float!(f32, f64);
153
154impl CopyField for str {
155 fn encode(&self, buf: &mut Vec<u8>) {
156 encode_text(self, buf);
157 }
158}
159
160impl CopyField for String {
161 fn encode(&self, buf: &mut Vec<u8>) {
162 encode_text(self, buf);
163 }
164}
165
166impl CopyField for CompactString {
167 fn encode(&self, buf: &mut Vec<u8>) {
168 encode_text(self.as_str(), buf);
169 }
170}
171
172impl CopyField for serde_json::Value {
173 fn encode(&self, buf: &mut Vec<u8>) {
174 encode_text(&self.to_string(), buf);
175 }
176}
177
178impl CopyField for [i32] {
179 fn encode(&self, buf: &mut Vec<u8>) {
180 buf.push(b'{');
181
182 for (i, v) in self.iter().enumerate() {
183 if i > 0 {
184 buf.push(b',');
185 }
186
187 encode_display(v, buf);
188 }
189
190 buf.push(b'}');
191 }
192}
193
194impl CopyField for Vec<i32> {
195 fn encode(&self, buf: &mut Vec<u8>) {
196 self.as_slice().encode(buf);
197 }
198}
199
200pub fn to_json<T>(value: &T) -> serde_json::Value
203where
204 T: Serialize,
205{
206 serde_json::to_value(value).unwrap_or(serde_json::Value::Null)
207}
208
209pub trait CopyInsert {
211 const COLUMNS: &'static [&'static str];
213 fn copy_row(&self, w: &mut CopyRow, patch: &str);
215}
216
217#[cfg(test)]
218mod tests {
219 use googletest::prelude::*;
220 use rstest::rstest;
221
222 use super::*;
223
224 fn encoded(field: impl CopyField) -> Result<String> {
225 let mut buf = Vec::new();
226
227 field.encode(&mut buf);
228
229 String::from_utf8(buf).or_fail()
230 }
231
232 #[gtest]
233 #[rstest]
234 #[case::plain("abc", "abc")]
235 #[case::backslash("a\\b", "a\\\\b")]
236 #[case::tab("a\tb", "a\\tb")]
237 #[case::newline("a\nb", "a\\nb")]
238 #[case::carriage("a\rb", "a\\rb")]
239 #[case::all("\\\t\n\r", "\\\\\\t\\n\\r")]
240 #[case::empty("", "")]
241 fn str_encode_cases(#[case] input: &str, #[case] expected: &str) -> Result<()> {
242 verify_that!(encoded(input)?, eq(expected))
243 }
244
245 #[gtest]
246 fn bool_encode() -> Result<()> {
247 verify_that!(encoded(true)?, eq("t"))?;
248
249 verify_that!(encoded(false)?, eq("f"))
250 }
251
252 #[gtest]
253 #[rstest]
254 #[case::normal(1.5, "1.5")]
255 #[case::nan(f64::NAN, "NaN")]
256 #[case::pos_inf(f64::INFINITY, "Infinity")]
257 #[case::neg_inf(f64::NEG_INFINITY, "-Infinity")]
258 fn f64_encode_cases(#[case] input: f64, #[case] expected: &str) -> Result<()> {
259 verify_that!(encoded(input)?, eq(expected))
260 }
261
262 #[gtest]
263 fn f64_encode_neg_zero() -> Result<()> {
264 verify_that!(encoded(-0.0f64)?, eq(&format!("{}", -0.0f64)))
265 }
266
267 #[gtest]
268 #[rstest]
269 #[case::normal(1.5, "1.5")]
270 #[case::nan(f32::NAN, "NaN")]
271 #[case::pos_inf(f32::INFINITY, "Infinity")]
272 #[case::neg_inf(f32::NEG_INFINITY, "-Infinity")]
273 fn f32_encode_cases(#[case] input: f32, #[case] expected: &str) -> Result<()> {
274 verify_that!(encoded(input)?, eq(expected))
275 }
276
277 #[gtest]
278 fn option_encode() -> Result<()> {
279 verify_that!(encoded(Some(5i32))?, eq("5"))?;
280
281 verify_that!(encoded(None::<i32>)?, eq("\\N"))
282 }
283
284 #[gtest]
285 #[rstest]
286 #[case::empty(vec![], "{}")]
287 #[case::single(vec![7], "{7}")]
288 #[case::many(vec![1, -2, 3], "{1,-2,3}")]
289 fn vec_i32_encode_cases(#[case] input: Vec<i32>, #[case] expected: &str) -> Result<()> {
290 verify_that!(encoded(input.as_slice())?, eq(expected))?;
291
292 verify_that!(encoded(input)?, eq(expected))
293 }
294
295 #[gtest]
296 fn copy_row_assembly() -> Result<()> {
297 let mut row = CopyRow::new();
298
299 row.push_bind(1i32)
300 .push_bind("a")
301 .push_bind(Option::<i32>::None);
302 row.finish_row();
303 let bytes = row.take();
304
305 verify_that!(String::from_utf8(bytes).or_fail()?, eq("1\ta\t\\N\n"))?;
306 verify_that!(row.is_empty(), eq(true))?;
307
308 verify_that!(row.len(), eq(0))
309 }
310
311 #[gtest]
312 fn copy_row_two_rows() -> Result<()> {
313 let mut row = CopyRow::new();
314
315 row.push_bind(1i32).push_bind("a");
316 row.finish_row();
317 row.push_bind(2i32).push_bind("b");
318 row.finish_row();
319 let out = String::from_utf8(row.take()).or_fail()?;
320
321 verify_that!(out, eq("1\ta\n2\tb\n"))
322 }
323}