Skip to content

Commit 0e9d670

Browse files
dfa1claude
andcommitted
feat(core): DType static factories + asNullable() shortcut (ADR 0009 part 1)
Add static factory methods on DType (i8/i16/i32/i64, u8/u16/u32/u64, f16/f32/f64, bool_, utf8, binary, null_, variant, decimal) returning non-nullable instances, plus an asNullable() default method as fluent sugar over withNullable(true). ```java // before new DType.Primitive(PType.I64, false) new DType.Utf8(true) // after DType.i64() DType.utf8().asNullable() ``` Why asNullable() and not nullable(): nullable() already exists as the boolean accessor on every record (DType.Primitive::nullable returns boolean). Adding a same-named default method on the interface clashed with the record component getters. asNullable() reads as a fluent adjective without shadowing the accessor. Pure additive — records, withNullable, pattern matching, proto serialization, and every existing call site are unchanged. ADR 0009 remains Proposed; parts 2 (structBuilder), 3 (typed Chunk), 4 (MemorySegment overload) follow separately. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5ba2ae3 commit 0e9d670

2 files changed

Lines changed: 187 additions & 0 deletions

File tree

core/src/main/java/io/github/dfa1/vortex/core/DType.java

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ public sealed interface DType
2222
/// @return {@code true} if null values are permitted
2323
boolean nullable();
2424

25+
/// Returns a copy of this type marked nullable. Sugar over
26+
/// {@link #withNullable(boolean)} so call sites read as a fluent adjective:
27+
/// {@code DType.i64().asNullable()}.
28+
///
29+
/// @return a new {@link DType} identical to this one but with {@code nullable = true}
30+
default DType asNullable() {
31+
return withNullable(true);
32+
}
33+
2534
/// Returns a copy of this type with the given nullability.
2635
///
2736
/// @param nullable the desired nullability for the returned type
@@ -42,6 +51,101 @@ default DType withNullable(boolean nullable) {
4251
};
4352
}
4453

54+
// ── Static factories ────────────────────────────────────────────────────
55+
//
56+
// Convenience entry points returning non-nullable instances. Combine with
57+
// [#nullable()] for nullable columns. The underlying records are unchanged
58+
// and remain usable directly (pattern matching, proto serialization, tests).
59+
60+
/// @return non-nullable {@link Bool}
61+
static Bool bool_() {
62+
return new Bool(false);
63+
}
64+
65+
/// @return non-nullable {@link Utf8}
66+
static Utf8 utf8() {
67+
return new Utf8(false);
68+
}
69+
70+
/// @return non-nullable {@link Binary}
71+
static Binary binary() {
72+
return new Binary(false);
73+
}
74+
75+
/// @return non-nullable {@link Null}
76+
static Null null_() {
77+
return new Null(false);
78+
}
79+
80+
/// @return non-nullable {@link Variant}
81+
static Variant variant() {
82+
return new Variant(false);
83+
}
84+
85+
/// @return non-nullable {@link Primitive} of {@link PType#I8}
86+
static Primitive i8() {
87+
return new Primitive(PType.I8, false);
88+
}
89+
90+
/// @return non-nullable {@link Primitive} of {@link PType#I16}
91+
static Primitive i16() {
92+
return new Primitive(PType.I16, false);
93+
}
94+
95+
/// @return non-nullable {@link Primitive} of {@link PType#I32}
96+
static Primitive i32() {
97+
return new Primitive(PType.I32, false);
98+
}
99+
100+
/// @return non-nullable {@link Primitive} of {@link PType#I64}
101+
static Primitive i64() {
102+
return new Primitive(PType.I64, false);
103+
}
104+
105+
/// @return non-nullable {@link Primitive} of {@link PType#U8}
106+
static Primitive u8() {
107+
return new Primitive(PType.U8, false);
108+
}
109+
110+
/// @return non-nullable {@link Primitive} of {@link PType#U16}
111+
static Primitive u16() {
112+
return new Primitive(PType.U16, false);
113+
}
114+
115+
/// @return non-nullable {@link Primitive} of {@link PType#U32}
116+
static Primitive u32() {
117+
return new Primitive(PType.U32, false);
118+
}
119+
120+
/// @return non-nullable {@link Primitive} of {@link PType#U64}
121+
static Primitive u64() {
122+
return new Primitive(PType.U64, false);
123+
}
124+
125+
/// @return non-nullable {@link Primitive} of {@link PType#F16}
126+
static Primitive f16() {
127+
return new Primitive(PType.F16, false);
128+
}
129+
130+
/// @return non-nullable {@link Primitive} of {@link PType#F32}
131+
static Primitive f32() {
132+
return new Primitive(PType.F32, false);
133+
}
134+
135+
/// @return non-nullable {@link Primitive} of {@link PType#F64}
136+
static Primitive f64() {
137+
return new Primitive(PType.F64, false);
138+
}
139+
140+
/// @param precision total number of significant decimal digits
141+
/// @param scale number of digits to the right of the decimal point
142+
/// @return non-nullable {@link Decimal}
143+
static Decimal decimal(int precision, int scale) {
144+
return new Decimal((byte) precision, (byte) scale, false);
145+
}
146+
147+
// ── Records ─────────────────────────────────────────────────────────────
148+
45149
/// The SQL {@code NULL} type — no values, always nullable.
46150
///
47151
/// @param nullable whether null values are permitted
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package io.github.dfa1.vortex.core;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
7+
/// Static factories + {@code nullable()} shortcut (ADR 0009 part 1).
8+
class DTypeFactoriesTest {
9+
10+
@Test
11+
void primitiveFactories_areNonNullable() {
12+
// Given / When
13+
DType.Primitive sut = DType.i64();
14+
15+
// Then
16+
assertThat(sut.ptype()).isEqualTo(PType.I64);
17+
assertThat(sut.nullable()).isFalse();
18+
}
19+
20+
@Test
21+
void nullableShortcut_marksType_nullable() {
22+
// Given
23+
DType sut = DType.utf8();
24+
25+
// When
26+
DType marked = sut.asNullable();
27+
28+
// Then
29+
assertThat(marked).isInstanceOf(DType.Utf8.class);
30+
assertThat(marked.nullable()).isTrue();
31+
// Original instance must remain non-nullable
32+
assertThat(sut.nullable()).isFalse();
33+
}
34+
35+
@Test
36+
void allPrimitiveFactories_returnExpectedPType() {
37+
// Given / When / Then
38+
assertThat(DType.i8().ptype()).isEqualTo(PType.I8);
39+
assertThat(DType.i16().ptype()).isEqualTo(PType.I16);
40+
assertThat(DType.i32().ptype()).isEqualTo(PType.I32);
41+
assertThat(DType.i64().ptype()).isEqualTo(PType.I64);
42+
assertThat(DType.u8().ptype()).isEqualTo(PType.U8);
43+
assertThat(DType.u16().ptype()).isEqualTo(PType.U16);
44+
assertThat(DType.u32().ptype()).isEqualTo(PType.U32);
45+
assertThat(DType.u64().ptype()).isEqualTo(PType.U64);
46+
assertThat(DType.f16().ptype()).isEqualTo(PType.F16);
47+
assertThat(DType.f32().ptype()).isEqualTo(PType.F32);
48+
assertThat(DType.f64().ptype()).isEqualTo(PType.F64);
49+
}
50+
51+
@Test
52+
void bool_utf8_binary_null_variant_factories_areNonNullable() {
53+
// Given / When / Then
54+
assertThat(DType.bool_().nullable()).isFalse();
55+
assertThat(DType.utf8().nullable()).isFalse();
56+
assertThat(DType.binary().nullable()).isFalse();
57+
assertThat(DType.null_().nullable()).isFalse();
58+
assertThat(DType.variant().nullable()).isFalse();
59+
}
60+
61+
@Test
62+
void decimalFactory_setsPrecisionAndScale() {
63+
// Given / When
64+
DType.Decimal sut = DType.decimal(12, 4);
65+
66+
// Then
67+
assertThat(sut.precision()).isEqualTo((byte) 12);
68+
assertThat(sut.scale()).isEqualTo((byte) 4);
69+
assertThat(sut.nullable()).isFalse();
70+
}
71+
72+
@Test
73+
void nullableShortcut_equivalentTo_withNullableTrue() {
74+
// Given
75+
DType direct = DType.f64().withNullable(true);
76+
77+
// When
78+
DType viaShortcut = DType.f64().asNullable();
79+
80+
// Then
81+
assertThat(viaShortcut).isEqualTo(direct);
82+
}
83+
}

0 commit comments

Comments
 (0)