Skip to content

Commit 8db4193

Browse files
dfa1claude
andcommitted
refactor(extension): Optional return for tryFrom / findKnown
@nullable return types let callers silently dereference; Optional forces the caller to handle the empty case at the type level and reads as idiomatic modern Java per CLAUDE.md. - ExtensionId.tryFrom: Optional<ExtensionId> - Extension.findKnown: Optional<Extension> - Callers updated: Chunk.as, VortexWriter.writeChunk, JdbcImporter.fillExtensionCell, VortexInspectorTui.formatValue. - ExtensionIdTest assertions: isSameAs -> contains / isNull -> isEmpty. - ExtensionTestSupport.tzMeta tz arg stays @nullable (string, not a result). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 3f9a731 commit 8db4193

8 files changed

Lines changed: 24 additions & 29 deletions

File tree

cli/src/main/java/io/github/dfa1/vortex/cli/tui/VortexInspectorTui.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,8 @@ record Failed(String message) implements DataState {
680680
private static String formatValue(Array array, int i, DType declared) {
681681
if (declared instanceof DType.Extension ext
682682
&& io.github.dfa1.vortex.extension.ExtensionId.tryFrom(ext.extensionId())
683-
== io.github.dfa1.vortex.extension.ExtensionId.VORTEX_DATE) {
683+
.filter(id -> id == io.github.dfa1.vortex.extension.ExtensionId.VORTEX_DATE)
684+
.isPresent()) {
684685
try {
685686
return io.github.dfa1.vortex.extension.DateExtension.INSTANCE.decode(array, i).toString();
686687
} catch (RuntimeException e) {

core/src/main/java/io/github/dfa1/vortex/extension/Extension.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.github.dfa1.vortex.core.VortexException;
55

66
import java.util.Collection;
7+
import java.util.Optional;
78

89
/// Contract for a Vortex extension type — pairs the wire-format identity
910
/// (an [ExtensionId]) with a factory for the matching [DType.Extension]
@@ -37,23 +38,19 @@ default Object encodeAll(DType.Extension dtype, Collection<?> values) {
3738
throw new VortexException("encode not supported for " + extensionId());
3839
}
3940

40-
/// Resolves a {@link DType.Extension} to its spec-defined singleton, or
41-
/// {@code null} when the wire id isn't one of the four spec extensions.
41+
/// Resolves a {@link DType.Extension} to its spec-defined singleton.
4242
/// Closes over the closed-set spec impls; third-party extensions go
4343
/// through {@link io.github.dfa1.vortex.encoding.Registry#lookup(ExtensionId)}.
4444
///
4545
/// @param dtype declared extension dtype
46-
/// @return matching spec extension singleton, or {@code null}
47-
static @org.jspecify.annotations.Nullable Extension findKnown(DType.Extension dtype) {
48-
ExtensionId id = ExtensionId.tryFrom(dtype.extensionId());
49-
if (id == null) {
50-
return null;
51-
}
52-
return switch (id) {
46+
/// @return matching spec extension singleton, or empty when the wire id
47+
/// isn't one of the four spec extensions
48+
static Optional<Extension> findKnown(DType.Extension dtype) {
49+
return ExtensionId.tryFrom(dtype.extensionId()).map(id -> switch (id) {
5350
case VORTEX_DATE -> DateExtension.INSTANCE;
5451
case VORTEX_TIME -> TimeExtension.INSTANCE;
5552
case VORTEX_TIMESTAMP -> TimestampExtension.INSTANCE;
5653
case VORTEX_UUID -> UuidExtension.INSTANCE;
57-
};
54+
});
5855
}
5956
}

core/src/main/java/io/github/dfa1/vortex/extension/ExtensionId.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.github.dfa1.vortex.core.VortexException;
44

55
import java.util.Map;
6+
import java.util.Optional;
67
import java.util.function.Function;
78
import java.util.stream.Collectors;
89
import java.util.stream.Stream;
@@ -48,9 +49,9 @@ public static ExtensionId from(String id) {
4849
/// Non-throwing lookup for a raw extension id string.
4950
///
5051
/// @param id raw extension id string
51-
/// @return matching constant, or {@code null} if not a known spec extension
52-
public static @org.jspecify.annotations.Nullable ExtensionId tryFrom(String id) {
53-
return LOOKUP.get(id);
52+
/// @return matching constant, or empty if not a known spec extension
53+
public static Optional<ExtensionId> tryFrom(String id) {
54+
return Optional.ofNullable(LOOKUP.get(id));
5455
}
5556

5657
/// Returns the canonical wire-format id string.

core/src/test/java/io/github/dfa1/vortex/extension/ExtensionIdTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ class ExtensionIdTest {
2020
void tryFrom_knownIds_returnEnumConstant(String wire, ExtensionId expected) {
2121
// Given / When / Then — wire string round-trips to the enum constant
2222
// so the LOOKUP map stays in sync with the enum definition
23-
assertThat(ExtensionId.tryFrom(wire)).isSameAs(expected);
23+
assertThat(ExtensionId.tryFrom(wire)).contains(expected);
2424
}
2525

2626
@Test
27-
void tryFrom_unknownId_returnsNull() {
27+
void tryFrom_unknownId_returnsEmpty() {
2828
// Given — open-world extension id; library doesn't recognise it
2929
// When / Then — non-throwing miss so the registry can route to passthrough
30-
assertThat(ExtensionId.tryFrom("acme.geopoint")).isNull();
30+
assertThat(ExtensionId.tryFrom("acme.geopoint")).isEmpty();
3131
}
3232

3333
@Test

core/src/test/java/io/github/dfa1/vortex/extension/ExtensionTestSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static ByteBuffer unitByte(byte tag) {
3434
return meta;
3535
}
3636

37-
static ByteBuffer tzMeta(byte unitTag, String tz) {
37+
static ByteBuffer tzMeta(byte unitTag, @org.jspecify.annotations.Nullable String tz) {
3838
byte[] tzBytes = tz == null ? new byte[0] : tz.getBytes(StandardCharsets.UTF_8);
3939
ByteBuffer meta = ByteBuffer.allocate(3 + tzBytes.length).order(ByteOrder.LITTLE_ENDIAN);
4040
meta.put(0, unitTag);

jdbc/src/main/java/io/github/dfa1/vortex/jdbc/JdbcImporter.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,8 @@ private static boolean fillCell(Object buffer, int rowIdx, ResultSet rs, int col
216216
@SuppressWarnings("unchecked")
217217
private static void fillExtensionCell(List<Object> buffer, ResultSet rs, int colIdx,
218218
DType.Extension ext) throws SQLException {
219-
ExtensionId id = ExtensionId.tryFrom(ext.extensionId());
220-
if (id == null) {
221-
throw new UnsupportedOperationException("unsupported extension: " + ext.extensionId());
222-
}
219+
ExtensionId id = ExtensionId.tryFrom(ext.extensionId())
220+
.orElseThrow(() -> new UnsupportedOperationException("unsupported extension: " + ext.extensionId()));
223221
// SQL NULL → null in the buffer. Nullable extension columns round-trip through the
224222
// writer's ExtEncoding → MaskedEncoding → primitive layout (validity child preserved);
225223
// NOT NULL columns reject any null element with VortexException during encode.

reader/src/main/java/io/github/dfa1/vortex/scan/Chunk.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,8 @@ public <T> List<T> as(String name, Class<T> domainType) {
117117
if (!(colDtype instanceof DType.Extension ext)) {
118118
throw new VortexException("not an extension column: " + name);
119119
}
120-
ExtensionId id = ExtensionId.tryFrom(ext.extensionId());
121-
if (id == null) {
122-
throw new VortexException("not a spec extension id: " + ext.extensionId());
123-
}
120+
ExtensionId id = ExtensionId.tryFrom(ext.extensionId())
121+
.orElseThrow(() -> new VortexException("not a spec extension id: " + ext.extensionId()));
124122
Array storage = column(name);
125123
Object result = switch (id) {
126124
case VORTEX_DATE -> {

writer/src/main/java/io/github/dfa1/vortex/writer/VortexWriter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ public void writeChunk(Map<String, Object> columns) throws IOException {
310310
// ExtEncoding wraps the storage child below — matches Rust's nested layout
311311
// (ExtEncoding → PrimitiveEncoding) and lets Registry skip its unwrap path.
312312
if (colDtype instanceof DType.Extension extDtype && data instanceof java.util.Collection<?> coll) {
313-
io.github.dfa1.vortex.extension.ExtensionId extId =
314-
io.github.dfa1.vortex.extension.ExtensionId.tryFrom(extDtype.extensionId());
315313
io.github.dfa1.vortex.extension.Extension impl =
316-
extId == null ? null : defaultRegistry.lookup(extId);
314+
io.github.dfa1.vortex.extension.ExtensionId.tryFrom(extDtype.extensionId())
315+
.map(defaultRegistry::lookup)
316+
.orElse(null);
317317
if (impl != null) {
318318
data = impl.encodeAll(extDtype, coll);
319319
}

0 commit comments

Comments
 (0)