clickhouse_format/output/
tsv_with_names.rs1use core::marker::PhantomData;
2use std::collections::HashMap;
3
4use csv::ReaderBuilder;
5use serde::de::DeserializeOwned;
6
7use crate::format_name::FormatName;
8
9use super::{tsv::TsvOutput, Output, OutputResult};
10
11pub struct TsvWithNamesOutput<T> {
12 types: Option<Vec<String>>,
13 phantom: PhantomData<T>,
14}
15impl<T> Default for TsvWithNamesOutput<T> {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20impl<T> TsvWithNamesOutput<T> {
21 pub fn new() -> Self {
22 Self {
23 types: None,
24 phantom: PhantomData,
25 }
26 }
27 pub fn with_types(types: Vec<String>) -> Self {
28 Self {
29 types: Some(types),
30 phantom: PhantomData,
31 }
32 }
33}
34
35impl<T> Output for TsvWithNamesOutput<T>
36where
37 T: DeserializeOwned,
38{
39 type Row = T;
40 type Info = Option<HashMap<String, String>>;
41
42 type Error = csv::Error;
43
44 fn format_name() -> FormatName {
45 FormatName::TsvWithNames
46 }
47
48 fn deserialize(&self, slice: &[u8]) -> OutputResult<Self::Row, Self::Info, Self::Error> {
49 let mut rdr = ReaderBuilder::new().delimiter(b'\t').from_reader(slice);
50
51 let header = rdr.headers()?;
52 let names: Vec<String> = header.iter().map(ToOwned::to_owned).collect();
53
54 let records = rdr.into_records();
55
56 TsvOutput::from_raw_parts(Some(names), self.types.to_owned())
57 .deserialize_with_records(records)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 use std::{fs, path::PathBuf};
66
67 use crate::test_helpers::{TestStringsRow, TEST_STRINGS_ROW_1};
68
69 #[test]
70 fn simple() -> Result<(), Box<dyn std::error::Error>> {
71 let file_path = PathBuf::new().join("tests/files/TSVWithNames.tsv");
72 let content = fs::read_to_string(&file_path)?;
73
74 assert_eq!(
75 TsvWithNamesOutput::<HashMap<String, String>>::format_name(),
76 file_path
77 .file_stem()
78 .unwrap()
79 .to_string_lossy()
80 .parse()
81 .unwrap()
82 );
83
84 let (rows, info) =
85 TsvWithNamesOutput::<HashMap<String, String>>::new().deserialize(content.as_bytes())?;
86 assert_eq!(rows.first().unwrap().get("tuple1").unwrap(), "(1,'a')");
87 assert_eq!(info, None);
88
89 let (rows, info) =
90 TsvWithNamesOutput::<TestStringsRow>::new().deserialize(content.as_bytes())?;
91 assert_eq!(rows.first().unwrap(), &*TEST_STRINGS_ROW_1);
92 assert_eq!(info, None);
93
94 Ok(())
95 }
96}