simd_csv/select/
headers.rs1use std::collections::BTreeMap;
2use std::fmt;
3use std::ops::Index;
4
5use crate::debug;
6use crate::records::ByteRecord;
7
8#[derive(Debug, PartialEq, Clone)]
9pub enum ColumIndexationBy<'b> {
10 Name(&'b [u8]),
11 NameAndNth(&'b [u8], isize),
12 Pos(isize),
13}
14
15impl ColumIndexationBy<'_> {
16 pub fn has_name(&self) -> bool {
17 matches!(self, Self::Name(_) | Self::NameAndNth(_, _))
18 }
19}
20
21pub struct ByteHeadersIndex {
24 inner: ByteRecord,
25 map: Option<BTreeMap<Vec<u8>, Vec<usize>>>,
26}
27
28impl fmt::Debug for ByteHeadersIndex {
29 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30 write!(f, "ByteHeadersIndex(")?;
31 f.debug_list()
32 .entries(self.inner.iter().map(debug::Bytes))
33 .finish()?;
34 write!(f, ")")?;
35 Ok(())
36 }
37}
38
39impl ByteHeadersIndex {
40 pub fn new(record: ByteRecord, has_names: bool) -> Self {
41 let map = if !has_names {
42 None
43 } else {
44 let mut map = BTreeMap::new();
45
46 for (i, name) in record.iter().enumerate() {
47 let indices = map.entry(name.to_vec()).or_insert_with(Vec::new);
48 indices.push(i);
49 }
50
51 Some(map)
52 };
53
54 Self { inner: record, map }
55 }
56
57 #[inline]
58 pub fn len(&self) -> usize {
59 self.inner.len()
60 }
61
62 #[inline]
63 pub fn is_empty(&self) -> bool {
64 self.inner.is_empty()
65 }
66
67 #[inline]
68 pub fn has_names(&self) -> bool {
69 self.map.is_some()
70 }
71
72 pub fn first_column_index_by_name(&self, name: impl AsRef<[u8]>) -> Option<usize> {
73 self.map
74 .as_ref()?
75 .get(name.as_ref())
76 .map(|indices| indices[0])
77 }
78
79 pub fn find_column_index(&self, indexation: ColumIndexationBy) -> Option<usize> {
80 match indexation {
81 ColumIndexationBy::Name(name) => self
82 .map
83 .as_ref()?
84 .get(name)
85 .and_then(|positions| positions.first())
86 .copied(),
87 ColumIndexationBy::Pos(pos) => {
88 let len = self.inner.len();
89
90 if pos < 0 {
91 let pos = pos.unsigned_abs();
93
94 if pos > len {
95 None
96 } else {
97 Some(len - pos)
98 }
99 } else {
100 let pos = pos as usize;
101
102 if pos >= len {
103 None
104 } else {
105 Some(pos)
106 }
107 }
108 }
109 ColumIndexationBy::NameAndNth(name, pos) => self
110 .map
111 .as_ref()?
112 .get(name)
113 .and_then(|positions| {
114 if pos < 0 {
115 let pos = pos.unsigned_abs();
116 let len = positions.len();
117
118 if pos > len {
119 None
120 } else {
121 positions.get(len - pos)
122 }
123 } else {
124 positions.get(pos as usize)
125 }
126 })
127 .copied(),
128 }
129 }
130}
131
132impl AsRef<ByteRecord> for ByteHeadersIndex {
133 fn as_ref(&self) -> &ByteRecord {
134 &self.inner
135 }
136}
137
138impl Index<usize> for ByteHeadersIndex {
139 type Output = [u8];
140
141 #[inline(always)]
142 fn index(&self, index: usize) -> &Self::Output {
143 &self.inner[index]
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_byte_headers_index() {
153 let headers = brec!["name", "surname", "age", "name"];
154 let index = ByteHeadersIndex::new(headers.clone(), true);
155
156 assert_eq!(&headers, index.as_ref());
157 assert_eq!(index.has_names(), true);
158 assert_eq!(index.len(), 4);
159 assert_eq!(index.is_empty(), false);
160 assert_eq!(index.first_column_index_by_name("surname"), Some(1));
161 }
162}