• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16276835702

14 Jul 2025 08:12PM UTC coverage: 81.509% (-0.02%) from 81.533%
16276835702

Pull #3871

github

web-flow
Merge c6fd6d749 into b0be264bf
Pull Request #3871: chore: Fewer ways to invoke display

26 of 29 new or added lines in 8 files covered. (89.66%)

14 existing lines in 3 files now uncovered.

46253 of 56746 relevant lines covered (81.51%)

3489259.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.6
/vortex-dtype/src/dtype.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::fmt::{Debug, Display, Formatter};
5
use std::hash::Hash;
6
use std::ops::Index;
7
use std::sync::Arc;
8

9
use DType::*;
10
use itertools::Itertools;
11
use static_assertions::const_assert_eq;
12
use vortex_error::vortex_panic;
13

14
use crate::decimal::DecimalDType;
15
use crate::nullability::Nullability;
16
use crate::{ExtDType, FieldDType, PType, StructFields};
17

18
/// A name for a field in a struct
19
pub type FieldName = Arc<str>;
20

21
/// An ordered list of field names in a struct
22
#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
23
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24
pub struct FieldNames(Arc<[FieldName]>);
25

26
impl FieldNames {
27
    /// Returns the number of elements.
28
    pub fn len(&self) -> usize {
913,137✔
29
        self.0.len()
913,137✔
30
    }
913,137✔
31

32
    /// Returns true if the number of elements is 0.
33
    pub fn is_empty(&self) -> bool {
×
34
        self.len() == 0
×
35
    }
×
36

37
    /// Returns a borrowed iterator over the field names.
38
    pub fn iter(&self) -> impl ExactSizeIterator<Item = &FieldName> {
107,445✔
39
        FieldNamesIter {
107,445✔
40
            inner: self,
107,445✔
41
            idx: 0,
107,445✔
42
        }
107,445✔
43
    }
107,445✔
44

45
    /// Returns a reference to a field name, or None if `index` is out of bounds.
46
    pub fn get(&self, index: usize) -> Option<&FieldName> {
532✔
47
        self.0.get(index)
532✔
48
    }
532✔
49
}
50

51
impl AsRef<[FieldName]> for FieldNames {
52
    fn as_ref(&self) -> &[FieldName] {
2,174✔
53
        &self.0
2,174✔
54
    }
2,174✔
55
}
56

57
impl Index<usize> for FieldNames {
58
    type Output = FieldName;
59

60
    fn index(&self, index: usize) -> &Self::Output {
22,581✔
61
        &self.0[index]
22,581✔
62
    }
22,581✔
63
}
64

65
/// Iterator of references to field names
66
pub struct FieldNamesIter<'a> {
67
    inner: &'a FieldNames,
68
    idx: usize,
69
}
70

71
impl<'a> Iterator for FieldNamesIter<'a> {
72
    type Item = &'a FieldName;
73

74
    fn next(&mut self) -> Option<Self::Item> {
357,887✔
75
        if self.idx >= self.inner.len() {
357,887✔
76
            return None;
28,641✔
77
        }
329,246✔
78

329,246✔
79
        let i = &self.inner.0[self.idx];
329,246✔
80
        self.idx += 1;
329,246✔
81
        Some(i)
329,246✔
82
    }
357,887✔
83

84
    fn size_hint(&self) -> (usize, Option<usize>) {
10,179✔
85
        let len = self.inner.len() - self.idx;
10,179✔
86
        (len, Some(len))
10,179✔
87
    }
10,179✔
88
}
89

90
impl ExactSizeIterator for FieldNamesIter<'_> {}
91

92
/// Owned iterator of field names.
93
pub struct FieldNamesIntoIter {
94
    inner: FieldNames,
95
    idx: usize,
96
}
97

98
impl Iterator for FieldNamesIntoIter {
99
    type Item = FieldName;
100

101
    fn next(&mut self) -> Option<Self::Item> {
3✔
102
        if self.idx >= self.inner.len() {
3✔
103
            return None;
1✔
104
        }
2✔
105

2✔
106
        let i = self.inner.0[self.idx].clone();
2✔
107
        self.idx += 1;
2✔
108
        Some(i)
2✔
109
    }
3✔
110

111
    fn size_hint(&self) -> (usize, Option<usize>) {
1✔
112
        let len = self.inner.len() - self.idx;
1✔
113
        (len, Some(len))
1✔
114
    }
1✔
115
}
116

117
impl ExactSizeIterator for FieldNamesIntoIter {}
118

119
impl IntoIterator for FieldNames {
120
    type Item = FieldName;
121

122
    type IntoIter = FieldNamesIntoIter;
123

124
    fn into_iter(self) -> Self::IntoIter {
2✔
125
        FieldNamesIntoIter {
2✔
126
            inner: self,
2✔
127
            idx: 0,
2✔
128
        }
2✔
129
    }
2✔
130
}
131

132
impl From<Vec<FieldName>> for FieldNames {
133
    fn from(value: Vec<FieldName>) -> Self {
35,519✔
134
        Self(value.into())
35,519✔
135
    }
35,519✔
136
}
137

138
impl From<&[&'static str]> for FieldNames {
139
    fn from(value: &[&'static str]) -> Self {
×
140
        Self(value.iter().cloned().map(Arc::from).collect())
×
141
    }
×
142
}
143

144
impl From<&[FieldName]> for FieldNames {
145
    fn from(value: &[FieldName]) -> Self {
114✔
146
        Self(Arc::from(value))
114✔
147
    }
114✔
148
}
149

150
impl<const N: usize> From<[&'static str; N]> for FieldNames {
151
    fn from(value: [&'static str; N]) -> Self {
166,685✔
152
        Self(value.into_iter().map(Arc::from).collect())
166,685✔
153
    }
166,685✔
154
}
155

156
impl<const N: usize> From<[FieldName; N]> for FieldNames {
157
    fn from(value: [FieldName; N]) -> Self {
13✔
158
        Self(value.into())
13✔
159
    }
13✔
160
}
161

162
impl<F: Into<FieldName>> FromIterator<F> for FieldNames {
163
    fn from_iter<T: IntoIterator<Item = F>>(iter: T) -> Self {
6,840✔
164
        Self(iter.into_iter().map(|v| v.into()).collect())
12,602✔
165
    }
6,840✔
166
}
167

168
/// The logical types of elements in Vortex arrays.
169
///
170
/// Vortex arrays preserve a single logical type, while the encodings allow for multiple
171
/// physical ways to encode that type.
172
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
173
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
174
pub enum DType {
175
    /// The logical null type (only has a single value, `null`)
176
    Null,
177
    /// The logical boolean type (`true` or `false` if non-nullable; `true`, `false`, or `null` if nullable)
178
    Bool(Nullability),
179
    /// Primitive, fixed-width numeric types (e.g., `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `f32`, `f64`)
180
    Primitive(PType, Nullability),
181
    /// Real numbers with fixed exact precision and scale.
182
    Decimal(DecimalDType, Nullability),
183
    /// UTF-8 strings
184
    Utf8(Nullability),
185
    /// Binary data
186
    Binary(Nullability),
187
    /// A struct is composed of an ordered list of fields, each with a corresponding name and DType
188
    Struct(StructFields, Nullability),
189
    /// A variable-length list type, parameterized by a single element DType
190
    List(Arc<DType>, Nullability),
191
    /// User-defined extension types
192
    Extension(Arc<ExtDType>),
193
}
194

195
#[cfg(not(target_arch = "wasm32"))]
196
const_assert_eq!(size_of::<DType>(), 16);
197

198
#[cfg(target_arch = "wasm32")]
199
const_assert_eq!(size_of::<DType>(), 8);
200

201
impl DType {
202
    /// The default DType for bytes
203
    pub const BYTES: Self = Primitive(PType::U8, Nullability::NonNullable);
204

205
    /// Get the nullability of the DType
206
    pub fn nullability(&self) -> Nullability {
690,973,906✔
207
        self.is_nullable().into()
690,973,906✔
208
    }
690,973,906✔
209

210
    /// Check if the DType is nullable
211
    pub fn is_nullable(&self) -> bool {
691,239,012✔
212
        use crate::nullability::Nullability::*;
213

214
        match self {
691,239,012✔
215
            Null => true,
722✔
216
            Extension(ext_dtype) => ext_dtype.storage_dtype().is_nullable(),
22,066✔
217
            Bool(n)
82,676✔
218
            | Primitive(_, n)
690,813,524✔
219
            | Decimal(_, n)
13,988✔
220
            | Utf8(n)
238,576✔
221
            | Binary(n)
32,238✔
222
            | Struct(_, n)
20,562✔
223
            | List(_, n) => matches!(n, Nullable),
691,216,224✔
224
        }
225
    }
691,239,012✔
226

227
    /// Get a new DType with `Nullability::NonNullable` (but otherwise the same as `self`)
228
    pub fn as_nonnullable(&self) -> Self {
192✔
229
        self.with_nullability(Nullability::NonNullable)
192✔
230
    }
192✔
231

232
    /// Get a new DType with `Nullability::Nullable` (but otherwise the same as `self`)
233
    pub fn as_nullable(&self) -> Self {
441,284✔
234
        self.with_nullability(Nullability::Nullable)
441,284✔
235
    }
441,284✔
236

237
    /// Get a new DType with the given nullability (but otherwise the same as `self`)
238
    pub fn with_nullability(&self, nullability: Nullability) -> Self {
483,178✔
239
        match self {
483,178✔
240
            Null => Null,
38✔
241
            Bool(_) => Bool(nullability),
364,806✔
242
            Primitive(p, _) => Primitive(*p, nullability),
96,062✔
243
            Decimal(d, _) => Decimal(*d, nullability),
3,060✔
244
            Utf8(_) => Utf8(nullability),
15,204✔
245
            Binary(_) => Binary(nullability),
532✔
246
            Struct(st, _) => Struct(st.clone(), nullability),
1,634✔
247
            List(c, _) => List(c.clone(), nullability),
874✔
248
            Extension(ext) => Extension(Arc::new(ext.with_nullability(nullability))),
968✔
249
        }
250
    }
483,178✔
251

252
    /// Union the nullability of this dtype with the other nullability, returning a new dtype.
253
    pub fn union_nullability(&self, other: Nullability) -> Self {
15,212✔
254
        let nullability = self.nullability() | other;
15,212✔
255
        self.with_nullability(nullability)
15,212✔
256
    }
15,212✔
257

258
    /// Check if `self` and `other` are equal, ignoring nullability
259
    pub fn eq_ignore_nullability(&self, other: &Self) -> bool {
2,945,964✔
260
        match (self, other) {
2,945,964✔
261
            (Null, Null) => true,
76✔
262
            (Bool(_), Bool(_)) => true,
21,640✔
263
            (Primitive(lhs_ptype, _), Primitive(rhs_ptype, _)) => lhs_ptype == rhs_ptype,
2,843,186✔
264
            (Decimal(lhs, _), Decimal(rhs, _)) => lhs == rhs,
5,328✔
265
            (Utf8(_), Utf8(_)) => true,
37,006✔
266
            (Binary(_), Binary(_)) => true,
1,484✔
267
            (List(lhs_dtype, _), List(rhs_dtype, _)) => lhs_dtype.eq_ignore_nullability(rhs_dtype),
21,544✔
268
            (Struct(lhs_dtype, _), Struct(rhs_dtype, _)) => {
3,724✔
269
                (lhs_dtype.names() == rhs_dtype.names())
3,724✔
270
                    && (lhs_dtype
3,724✔
271
                        .fields()
3,724✔
272
                        .zip_eq(rhs_dtype.fields())
3,724✔
273
                        .all(|(l, r)| l.eq_ignore_nullability(&r)))
6,004✔
274
            }
275
            (Extension(lhs_extdtype), Extension(rhs_extdtype)) => {
11,632✔
276
                lhs_extdtype.as_ref().eq_ignore_nullability(rhs_extdtype)
11,632✔
277
            }
278
            _ => false,
344✔
279
        }
280
    }
2,945,964✔
281

282
    /// Check if `self` is a `StructDType`
283
    pub fn is_struct(&self) -> bool {
41,578✔
284
        matches!(self, Struct(_, _))
41,578✔
285
    }
41,578✔
286

287
    /// Check if `self` is a primitive tpye
288
    pub fn is_primitive(&self) -> bool {
418✔
289
        matches!(self, Primitive(_, _))
418✔
290
    }
418✔
291

292
    /// Returns this DType's `PType` if it is a primitive type, otherwise panics.
293
    pub fn as_ptype(&self) -> PType {
2,148,316,961✔
294
        match self {
2,148,316,961✔
295
            Primitive(ptype, _) => *ptype,
2,148,316,961✔
296
            _ => vortex_panic!("DType is not a primitive type"),
×
297
        }
298
    }
2,148,316,961✔
299

300
    /// Check if `self` is an unsigned integer
301
    pub fn is_unsigned_int(&self) -> bool {
24,956✔
302
        if let Primitive(ptype, _) = self {
24,956✔
303
            return ptype.is_unsigned_int();
24,956✔
304
        }
×
305
        false
×
306
    }
24,956✔
307

308
    /// Check if `self` is a signed integer
309
    pub fn is_signed_int(&self) -> bool {
4,850✔
310
        if let Primitive(ptype, _) = self {
4,850✔
311
            return ptype.is_signed_int();
4,850✔
312
        }
×
313
        false
×
314
    }
4,850✔
315

316
    /// Check if `self` is an integer (signed or unsigned)
317
    pub fn is_int(&self) -> bool {
99,782✔
318
        if let Primitive(ptype, _) = self {
99,782✔
319
            return ptype.is_int();
99,782✔
320
        }
×
321
        false
×
322
    }
99,782✔
323

324
    /// Check if `self` is a floating point number
325
    pub fn is_float(&self) -> bool {
1,824✔
326
        if let Primitive(ptype, _) = self {
1,824✔
327
            return ptype.is_float();
1,824✔
328
        }
×
329
        false
×
330
    }
1,824✔
331

332
    /// Check if `self` is a boolean
333
    pub fn is_boolean(&self) -> bool {
5,308✔
334
        matches!(self, Bool(_))
5,308✔
335
    }
5,308✔
336

337
    /// Check if `self` is a binary
338
    pub fn is_binary(&self) -> bool {
228✔
339
        matches!(self, Binary(_))
228✔
340
    }
228✔
341

342
    /// Check if `self` is a utf8
343
    pub fn is_utf8(&self) -> bool {
1,664✔
344
        matches!(self, Utf8(_))
1,664✔
345
    }
1,664✔
346

347
    /// Check if `self` is an extension type
348
    pub fn is_extension(&self) -> bool {
×
349
        matches!(self, Extension(_))
×
350
    }
×
351

352
    /// Check if `self` is a decimal type
353
    pub fn is_decimal(&self) -> bool {
×
354
        matches!(self, Decimal(..))
×
355
    }
×
356

357
    /// Check returns the inner decimal type if the dtype is a decimal
358
    pub fn as_decimal(&self) -> Option<&DecimalDType> {
5,976✔
359
        match self {
5,976✔
360
            Decimal(decimal, _) => Some(decimal),
5,976✔
361
            _ => None,
×
362
        }
363
    }
5,976✔
364

365
    /// Get the `StructDType` if `self` is a `StructDType`, otherwise `None`
366
    pub fn as_struct(&self) -> Option<&StructFields> {
306,329✔
367
        match self {
306,329✔
368
            Struct(s, _) => Some(s),
305,796✔
369
            _ => None,
533✔
370
        }
371
    }
306,329✔
372

373
    /// Get the inner dtype if `self` is a `ListDType`, otherwise `None`
374
    pub fn as_list_element(&self) -> Option<&Arc<DType>> {
456✔
375
        match self {
456✔
376
            List(s, _) => Some(s),
418✔
377
            _ => None,
38✔
378
        }
379
    }
456✔
380

381
    /// Convenience method for creating a struct dtype
382
    pub fn struct_<I: IntoIterator<Item = (impl Into<FieldName>, impl Into<FieldDType>)>>(
17✔
383
        iter: I,
17✔
384
        nullability: Nullability,
17✔
385
    ) -> Self {
17✔
386
        Struct(StructFields::from_iter(iter), nullability)
17✔
387
    }
17✔
388

389
    /// Convenience method for creating a list dtype
390
    pub fn list(dtype: impl Into<DType>, nullability: Nullability) -> Self {
2✔
391
        List(Arc::new(dtype.into()), nullability)
2✔
392
    }
2✔
393
}
394

395
impl Display for DType {
396
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1,634✔
397
        match self {
1,634✔
398
            Null => write!(f, "null"),
×
399
            Bool(n) => write!(f, "bool{n}"),
76✔
400
            Primitive(pt, n) => write!(f, "{pt}{n}"),
1,254✔
401
            Decimal(dt, n) => write!(f, "{dt}{n}"),
×
UNCOV
402
            Utf8(n) => write!(f, "utf8{n}"),
×
403
            Binary(n) => write!(f, "binary{n}"),
×
404
            Struct(sdt, n) => write!(
114✔
405
                f,
114✔
406
                "{{{}}}{}",
114✔
407
                sdt.names()
114✔
408
                    .iter()
114✔
409
                    .zip(sdt.fields())
114✔
410
                    .map(|(n, dt)| format!("{n}={dt}"))
266✔
411
                    .join(", "),
114✔
412
                n
114✔
413
            ),
114✔
414
            List(edt, n) => write!(f, "list({edt}){n}"),
38✔
415
            Extension(ext) => write!(
152✔
416
                f,
152✔
417
                "ext({}, {}{}){}",
152✔
418
                ext.id(),
152✔
419
                ext.storage_dtype()
152✔
420
                    .with_nullability(Nullability::NonNullable),
152✔
421
                ext.metadata()
152✔
422
                    .map(|m| format!(", {m:?}"))
152✔
423
                    .unwrap_or_else(|| "".to_string()),
152✔
424
                ext.storage_dtype().nullability(),
152✔
425
            ),
152✔
426
        }
427
    }
1,634✔
428
}
429

430
#[cfg(test)]
431
mod tests {
432
    use super::*;
433

434
    #[test]
435
    fn test_field_names_iter() {
1✔
436
        let names = ["a", "b"];
1✔
437
        let field_names = FieldNames::from(names);
1✔
438
        assert_eq!(field_names.iter().len(), names.len());
1✔
439
        let mut iter = field_names.iter();
1✔
440
        assert_eq!(iter.next(), Some(&"a".into()));
1✔
441
        assert_eq!(iter.next(), Some(&"b".into()));
1✔
442
        assert_eq!(iter.next(), None);
1✔
443
    }
1✔
444

445
    #[test]
446
    fn test_field_names_owned_iter() {
1✔
447
        let names = ["a", "b"];
1✔
448
        let field_names = FieldNames::from(names);
1✔
449
        assert_eq!(field_names.clone().into_iter().len(), names.len());
1✔
450
        let mut iter = field_names.into_iter();
1✔
451
        assert_eq!(iter.next(), Some("a".into()));
1✔
452
        assert_eq!(iter.next(), Some("b".into()));
1✔
453
        assert_eq!(iter.next(), None);
1✔
454
    }
1✔
455
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc