public static CallRoutingTable fromTsv(final Reader inputReader) throws IOException {
    try (final BufferedReader reader = new BufferedReader(inputReader)) {
      // use maps to silently dedupe CidrBlocks
      Map<CidrBlock.IpV4CidrBlock, List<String>> ipv4Map = new HashMap<>();
      Map<CidrBlock.IpV6CidrBlock, List<String>> ipv6Map = new HashMap<>();
      Map<CallRoutingTable.GeoKey, List<String>> ipGeoTable = new HashMap<>();
      String line;
      while((line = reader.readLine()) != null) {
        if(line.isBlank()) {
          continue;
        }

        List<String> splits = Arrays.stream(line.split(WHITESPACE_REGEX)).filter(s -> !s.isBlank()).toList();
        if (splits.size() < 2) {
          throw new IllegalStateException("Invalid row, expected some key and list of values");
        }

        List<String> datacenters = splits.subList(1, splits.size());
        switch (guessLineType(splits)) {
          case v4 ->  {
            CidrBlock cidrBlock = CidrBlock.parseCidrBlock(splits.getFirst());
            if(!(cidrBlock instanceof CidrBlock.IpV4CidrBlock)) {
              throw new IllegalArgumentException("Expected an ipv4 cidr block");
            }
            ipv4Map.put((CidrBlock.IpV4CidrBlock) cidrBlock, datacenters);
          }
          case v6 -> {
            CidrBlock cidrBlock = CidrBlock.parseCidrBlock(splits.getFirst());
            if(!(cidrBlock instanceof CidrBlock.IpV6CidrBlock)) {
              throw new IllegalArgumentException("Expected an ipv6 cidr block");
            }
            ipv6Map.put((CidrBlock.IpV6CidrBlock) cidrBlock, datacenters);
          }
          case Geo -> {
            String[] geo = splits.getFirst().split("-");
            if(geo.length < 3) {
              throw new IllegalStateException("Geo row key invalid, expected atleast continent, country, and protocol");
            }
            String continent = geo[0];
            String country = geo[1];
            Optional<String> subdivision = geo.length > 3 ? Optional.of(geo[2]) : Optional.empty();
            CallRoutingTable.Protocol protocol = CallRoutingTable.Protocol.valueOf(geo[geo.length - 1].toLowerCase());
            CallRoutingTable.GeoKey tableKey = new CallRoutingTable.GeoKey(
                continent,
                country,
                subdivision,
                protocol
            );
            ipGeoTable.put(tableKey, datacenters);
          }
        }
      }

      return new CallRoutingTable(
          ipv4Map,
          ipv6Map,
          ipGeoTable
      );
    }
  }
@Test
  public void testParserSuccess() throws IOException {
    var input =
        """
        192.1.12.0/24  datacenter-1 datacenter-2 datacenter-3
        193.123.123.0/24  datacenter-1  datacenter-2
        1.123.123.0/24  datacenter-1
        
        2001:db8:b0aa::/48  datacenter-1
        2001:db8:b0ab::/48  datacenter-3  datacenter-1 datacenter-2
        2001:db8:b0ac::/48  datacenter-2  datacenter-1
        
        SA-SR-v4 datacenter-3
        SA-UY-v4 datacenter-3 datacenter-1 datacenter-2
        NA-US-VA-v6 datacenter-2 datacenter-1
        """;
    var actual = CallRoutingTableParser.fromTsv(new StringReader(input));
    var expected = new CallRoutingTable(
        Map.of(
            (CidrBlock.IpV4CidrBlock) CidrBlock.parseCidrBlock("192.1.12.0/24"), List.of("datacenter-1", "datacenter-2", "datacenter-3"),
            (CidrBlock.IpV4CidrBlock) CidrBlock.parseCidrBlock("193.123.123.0/24"), List.of("datacenter-1", "datacenter-2"),
            (CidrBlock.IpV4CidrBlock) CidrBlock.parseCidrBlock("1.123.123.0/24"), List.of("datacenter-1")
        ),
        Map.of(
            (CidrBlock.IpV6CidrBlock) CidrBlock.parseCidrBlock("2001:db8:b0aa::/48"), List.of("datacenter-1"),
            (CidrBlock.IpV6CidrBlock) CidrBlock.parseCidrBlock("2001:db8:b0ab::/48"), List.of("datacenter-3", "datacenter-1", "datacenter-2"),
            (CidrBlock.IpV6CidrBlock) CidrBlock.parseCidrBlock("2001:db8:b0ac::/48"), List.of("datacenter-2", "datacenter-1")
        ),
        Map.of(
            new CallRoutingTable.GeoKey("SA", "SR", Optional.empty(), CallRoutingTable.Protocol.v4), List.of("datacenter-3"),
            new CallRoutingTable.GeoKey("SA", "UY", Optional.empty(), CallRoutingTable.Protocol.v4), List.of("datacenter-3", "datacenter-1", "datacenter-2"),
            new CallRoutingTable.GeoKey("NA", "US", Optional.of("VA"), CallRoutingTable.Protocol.v6), List.of("datacenter-2", "datacenter-1")
        )
    );

    assertThat(actual).isEqualTo(expected);
  }