From 6a1de98713de388f5c4191aabe4ca0038531f30f Mon Sep 17 00:00:00 2001
From: Julian Burner <48808497+NebelNidas@users.noreply.github.com>
Date: Tue, 7 Jan 2025 10:29:34 +0100
Subject: [PATCH] Restore the ability to read source-namespace-only mapping
 files (#121)

This isn't spec-compliant, but is used for [mod-provided Javadocs](https://github.com/FabricMC/fabric-loom/pull/627) in Loom and has some valid use cases. Once #94 is ready, this should log a warning.
---
 CHANGELOG.md                                                | 1 +
 .../net/fabricmc/mappingio/format/srg/TsrgFileReader.java   | 6 +++---
 .../net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java | 6 +++---
 .../net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java | 6 +++---
 .../mappingio/test/tests/reading/EmptyContentReadTest.java  | 6 +++---
 5 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f857ab3e..87980643 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+- Restore the ability to read source-namespace-only mapping files, even if not spec-compliant
 
 ## [0.7.0] - 2025-01-01
 - Added IntelliJ IDEA migration map reader and writer
diff --git a/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java b/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java
index b64913cb..fdad07a9 100644
--- a/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java
@@ -82,12 +82,12 @@ private static void read(ColumnFileReader reader, String sourceNs, String target
 			dstNamespaces = new ArrayList<>();
 			String dstNamespace;
 
-			while ((dstNamespace = reader.nextCol()) != null) {
-				if (dstNamespace.isEmpty()) throw new IOException("empty destination namespace in TSRG v2 header");
+			while (!reader.isAtEol()) {
+				dstNamespace = reader.nextCol();
+				if (dstNamespace == null || dstNamespace.isEmpty()) throw new IOException("empty destination namespace in TSRG v2 header");
 				dstNamespaces.add(dstNamespace);
 			}
 
-			if (dstNamespaces.isEmpty()) throw new IOException("no destination namespaces in TSRG v2 header");
 			reader.nextLine(0);
 		} else {
 			if (sourceNs == null || sourceNs.isEmpty()) throw new IllegalArgumentException("provided source namespace must not be null or empty");
diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java
index 1c740dc8..c7d61213 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java
@@ -74,13 +74,13 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws
 		List<String> dstNamespaces = new ArrayList<>();
 		String dstNamespace;
 
-		while ((dstNamespace = reader.nextCol()) != null) {
-			if (dstNamespace.isEmpty()) throw new IOException("empty destination namespace in Tiny v1 header");
+		while (!reader.isAtEol()) {
+			dstNamespace = reader.nextCol();
+			if (dstNamespace == null || dstNamespace.isEmpty()) throw new IOException("empty destination namespace in Tiny v1 header");
 			dstNamespaces.add(dstNamespace);
 		}
 
 		int dstNsCount = dstNamespaces.size();
-		if (dstNsCount == 0) throw new IOException("no destination namespaces in Tiny v1 header");
 
 		Set<MappingFlag> flags = visitor.getFlags();
 		MappingVisitor parentVisitor = null;
diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java
index d6643a32..81f4c9fb 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java
@@ -75,13 +75,13 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws
 		List<String> dstNamespaces = new ArrayList<>();
 		String dstNamespace;
 
-		while ((dstNamespace = reader.nextCol()) != null) {
-			if (dstNamespace.isEmpty()) throw new IOException("empty destination namespace in Tiny v2 header");
+		while (!reader.isAtEol()) {
+			dstNamespace = reader.nextCol();
+			if (dstNamespace == null || dstNamespace.isEmpty()) throw new IOException("empty destination namespace in Tiny v2 header");
 			dstNamespaces.add(dstNamespace);
 		}
 
 		int dstNsCount = dstNamespaces.size();
-		if (dstNsCount == 0) throw new IOException("no destination namespaces in Tiny v2 header");
 		boolean readerMarked = false;
 
 		if (visitor.getFlags().contains(MappingFlag.NEEDS_MULTIPLE_PASSES)) {
diff --git a/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java b/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java
index 98235aca..9d2c0965 100644
--- a/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java
+++ b/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java
@@ -63,7 +63,7 @@ public void emptyTinyFile() throws Exception {
 		assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header0), target));
 		assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header1), target));
 		assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header2), target));
-		assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header3), target));
+		Tiny1FileReader.read(new StringReader(header3), target);
 		assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header4), target));
 		Tiny1FileReader.read(new StringReader(header5), target);
 	}
@@ -80,7 +80,7 @@ public void emptyTinyV2File() throws Exception {
 		assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header0), target));
 		assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header1), target));
 		assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header2), target));
-		assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header3), target));
+		Tiny2FileReader.read(new StringReader(header3), target);
 		assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header4), target));
 		Tiny2FileReader.read(new StringReader(header5), target);
 	}
@@ -113,7 +113,7 @@ public void emptyTsrgFile() throws Exception {
 		instantiateTree();
 		assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header1), target));
 		assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header2), target));
-		assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header3), target));
+		TsrgFileReader.read(new StringReader(header3), target);
 		assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header4), target));
 		TsrgFileReader.read(new StringReader(header5), target);
 	}