1use std::sync::Arc;
9
10use arrow::array::{
11 Array, ArrayRef, BooleanArray, ListArray, PrimitiveArray, StringArray, StructArray,
12 new_empty_array, new_null_array,
13};
14use arrow::buffer::OffsetBuffer;
15use arrow::compute::concat;
16use arrow::datatypes::{
17 ArrowPrimitiveType, DataType, Field, FieldRef, Fields, Float32Type, Float64Type, Int8Type,
18 Int16Type, Int32Type, Int64Type, UInt8Type, UInt16Type, UInt32Type, UInt64Type,
19};
20
21use crate::error::{Result, nd_err};
22
23#[derive(Debug, Clone, Default)]
25pub struct Attributes {
26 entries: Vec<(Arc<str>, ArrayRef)>,
27}
28
29impl Attributes {
30 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn len(&self) -> usize {
37 self.entries.len()
38 }
39
40 pub fn is_empty(&self) -> bool {
42 self.entries.is_empty()
43 }
44
45 pub fn get(&self, name: &str) -> Option<&ArrayRef> {
47 self.entries
48 .iter()
49 .find(|(key, _)| key.as_ref() == name)
50 .map(|(_, value)| value)
51 }
52
53 pub fn contains(&self, name: &str) -> bool {
55 self.get(name).is_some()
56 }
57
58 pub fn get_str(&self, name: &str) -> Option<&str> {
60 let value = self.get(name)?.as_any().downcast_ref::<StringArray>()?;
61 value.is_valid(0).then(|| value.value(0))
62 }
63
64 pub fn get_primitive<T: ArrowPrimitiveType>(&self, name: &str) -> Option<T::Native> {
66 let value = self
67 .get(name)?
68 .as_any()
69 .downcast_ref::<PrimitiveArray<T>>()?;
70 value.is_valid(0).then(|| value.value(0))
71 }
72
73 pub fn insert(&mut self, name: impl Into<Arc<str>>, value: impl IntoScalar) {
75 self.insert_unchecked(name.into(), value.into_scalar());
76 }
77
78 pub fn try_insert_array(&mut self, name: impl Into<Arc<str>>, value: ArrayRef) -> Result<()> {
80 let name = name.into();
81 if value.len() != 1 {
82 return Err(nd_err!(
83 InvalidAttribute,
84 "attribute '{name}' must have length 1, got {}",
85 value.len()
86 ));
87 }
88 self.insert_unchecked(name, value);
89 Ok(())
90 }
91
92 pub fn with(mut self, name: impl Into<Arc<str>>, value: impl IntoScalar) -> Self {
94 self.insert(name, value);
95 self
96 }
97
98 pub fn remove(&mut self, name: &str) -> Option<ArrayRef> {
100 let index = self
101 .entries
102 .iter()
103 .position(|(key, _)| key.as_ref() == name)?;
104 Some(self.entries.remove(index).1)
105 }
106
107 pub fn iter(&self) -> impl Iterator<Item = (&str, &ArrayRef)> {
109 self.entries
110 .iter()
111 .map(|(key, value)| (key.as_ref(), value))
112 }
113
114 pub fn fields(&self) -> Fields {
116 self.entries
117 .iter()
118 .map(|(key, value)| attribute_field(key, value.data_type()))
119 .collect()
120 }
121
122 pub fn from_struct_row(attrs: &StructArray, row: usize) -> Self {
124 let entries = attrs
125 .fields()
126 .iter()
127 .zip(attrs.columns())
128 .filter(|(_, column)| column.is_valid(row))
129 .map(|(field, column)| (Arc::from(field.name().as_str()), column.slice(row, 1)))
130 .collect();
131 Self { entries }
132 }
133
134 pub fn union_fields<'a>(all: impl IntoIterator<Item = &'a Attributes>) -> Result<Fields> {
137 let mut fields: Vec<FieldRef> = Vec::new();
138 for attrs in all {
139 for (name, value) in attrs.iter() {
140 match fields.iter().find(|f| f.name() == name) {
141 Some(existing) if existing.data_type() != value.data_type() => {
142 return Err(nd_err!(
143 InvalidAttribute,
144 "attribute '{name}' has types {} and {}",
145 existing.data_type(),
146 value.data_type()
147 ));
148 }
149 Some(_) => {}
150 None => fields.push(attribute_field(name, value.data_type())),
151 }
152 }
153 }
154 Ok(fields.into())
155 }
156
157 pub fn encode_rows(rows: &[Option<&Attributes>], fields: &Fields) -> Result<StructArray> {
160 let columns = fields
161 .iter()
162 .map(|field| {
163 let null = new_null_array(field.data_type(), 1);
164 let parts: Vec<&dyn Array> = rows
165 .iter()
166 .map(|row| {
167 row.and_then(|a| a.get(field.name()))
168 .map_or(null.as_ref(), |v| v.as_ref())
169 })
170 .collect();
171 Ok(if parts.is_empty() {
172 new_empty_array(field.data_type())
173 } else {
174 concat(&parts)?
175 })
176 })
177 .collect::<Result<Vec<_>>>()?;
178 if fields.is_empty() {
179 return Ok(StructArray::new_empty_fields(rows.len(), None));
180 }
181 Ok(StructArray::try_new(fields.clone(), columns, None)?)
182 }
183
184 fn insert_unchecked(&mut self, name: Arc<str>, value: ArrayRef) {
185 match self.entries.iter_mut().find(|(key, _)| *key == name) {
186 Some(entry) => entry.1 = value,
187 None => self.entries.push((name, value)),
188 }
189 }
190}
191
192impl PartialEq for Attributes {
193 fn eq(&self, other: &Self) -> bool {
195 self.len() == other.len()
196 && self
197 .iter()
198 .all(|(key, value)| other.get(key).is_some_and(|v| v == value))
199 }
200}
201
202pub(crate) fn attribute_field(name: &str, data_type: &DataType) -> FieldRef {
203 Arc::new(Field::new(name, data_type.clone(), true))
204}
205
206pub trait IntoScalar {
208 fn into_scalar(self) -> ArrayRef;
210}
211
212impl IntoScalar for &str {
213 fn into_scalar(self) -> ArrayRef {
214 Arc::new(StringArray::from(vec![self]))
215 }
216}
217
218impl IntoScalar for String {
219 fn into_scalar(self) -> ArrayRef {
220 self.as_str().into_scalar()
221 }
222}
223
224impl IntoScalar for bool {
225 fn into_scalar(self) -> ArrayRef {
226 Arc::new(BooleanArray::from(vec![self]))
227 }
228}
229
230impl IntoScalar for Vec<&str> {
231 fn into_scalar(self) -> ArrayRef {
232 list_of(Arc::new(StringArray::from(self)))
233 }
234}
235
236impl IntoScalar for Vec<String> {
237 fn into_scalar(self) -> ArrayRef {
238 list_of(Arc::new(StringArray::from(self)))
239 }
240}
241
242macro_rules! primitive_attribute {
243 ($($native:ty => $arrow:ty),* $(,)?) => {$(
244 impl IntoScalar for $native {
245 fn into_scalar(self) -> ArrayRef {
246 Arc::new(PrimitiveArray::<$arrow>::from(vec![self]))
247 }
248 }
249
250 impl IntoScalar for Vec<$native> {
251 fn into_scalar(self) -> ArrayRef {
252 list_of(Arc::new(PrimitiveArray::<$arrow>::from(self)))
253 }
254 }
255 )*};
256}
257
258primitive_attribute!(
259 i8 => Int8Type,
260 i16 => Int16Type,
261 i32 => Int32Type,
262 i64 => Int64Type,
263 u8 => UInt8Type,
264 u16 => UInt16Type,
265 u32 => UInt32Type,
266 u64 => UInt64Type,
267 f32 => Float32Type,
268 f64 => Float64Type,
269);
270
271fn list_of(values: ArrayRef) -> ArrayRef {
272 let field = Arc::new(Field::new("item", values.data_type().clone(), true));
273 Arc::new(ListArray::new(
274 field,
275 OffsetBuffer::from_lengths([values.len()]),
276 values,
277 None,
278 ))
279}
280
281#[cfg(test)]
282mod tests {
283 use arrow::array::AsArray;
284
285 use super::*;
286
287 #[test]
288 fn insert_replaces_and_keeps_order() {
289 let mut attrs = Attributes::new()
290 .with("units", "degC")
291 .with("scale_factor", 0.5f64)
292 .with("valid_range", vec![-5.0f32, 40.0]);
293 attrs.insert("units", "K");
294
295 assert_eq!(attrs.len(), 3);
296 assert_eq!(attrs.get_str("units"), Some("K"));
297 assert_eq!(
298 attrs.get_primitive::<Float64Type>("scale_factor"),
299 Some(0.5)
300 );
301 let names: Vec<&str> = attrs.iter().map(|(k, _)| k).collect();
302 assert_eq!(names, vec!["units", "scale_factor", "valid_range"]);
303
304 let range = attrs.get("valid_range").unwrap().as_list::<i32>().value(0);
305 assert_eq!(range.as_primitive::<Float32Type>().values(), &[-5.0, 40.0]);
306 }
307
308 #[test]
309 fn array_attribute_must_have_length_one() {
310 let mut attrs = Attributes::new();
311 let two: ArrayRef = Arc::new(StringArray::from(vec!["a", "b"]));
312 assert!(attrs.try_insert_array("bad", two).is_err());
313 }
314
315 #[test]
316 fn equality_ignores_order() {
317 let a = Attributes::new().with("a", 1i32).with("b", "x");
318 let b = Attributes::new().with("b", "x").with("a", 1i32);
319 assert_eq!(a, b);
320 assert_ne!(a, Attributes::new().with("a", 2i32).with("b", "x"));
321 }
322}