docs: private Map<Short, LocalVariableDefinition> readSingleFieldValue(MethodDefinition read)
      {
          LocalVariableDefinition protocol = read.getLocalVariable("reader");
  
          // declare and init local variables here
          Map<Short, LocalVariableDefinition> unionData = new TreeMap<>();
          for (ThriftFieldMetadata field : metadata.getFields(THRIFT_FIELD)) {
              LocalVariableDefinition variable = read.addInitializedLocalVariable(
                      toParameterizedType(field.getThriftType()),
                      "f_" + field.getName()
              );
              unionData.put(field.getId(), variable);
          }
  
          // protocol.readStructBegin();
          read.loadVariable(protocol).invokeVirtual(
                  TProtocolReader.class,
                  "readStructBegin",
                  void.class
          );
  
          // while (protocol.nextField())
          read.visitLabel("while-begin");
          read.loadVariable(protocol).invokeVirtual(TProtocolReader.class, "nextField", boolean.class);
          read.ifZeroGoto("while-end");
  
          // fieldId = protocol.getFieldId()
          read.loadVariable(protocol).invokeVirtual(TProtocolReader.class, "getFieldId", short.class);
          read.storeVariable("fieldId");
  
          // switch (fieldId)
          read.loadVariable("fieldId");
  
          List<CaseStatement> cases = new ArrayList<>();
          for (ThriftFieldMetadata field : metadata.getFields(THRIFT_FIELD)) {
              cases.add(caseStatement(field.getId(), field.getName() + "-field"));
          }
          read.switchStatement("default", cases);
  
          for (ThriftFieldMetadata field : metadata.getFields(THRIFT_FIELD)) {
              // case field.id:
              read.visitLabel(field.getName() + "-field");
  
              // push protocol
              read.loadVariable(protocol);
  
              // push ThriftTypeCodec for this field
              FieldDefinition codecField = codecFields.get(field.getId());
              if (codecField != null) {
                  read.loadThis().getField(codecType, codecField);
              }
  
              // read value
              Method readMethod = getReadMethod(field.getThriftType());
              if (readMethod == null) {
                  throw new IllegalArgumentException("Unsupported field type " + field.getThriftType().getProtocolType());
              }
              read.invokeVirtual(readMethod);
  
              // todo this cast should be based on readMethod return type and fieldType (or coercion type)
              // add cast if necessary
              if (needsCastAfterRead(field, readMethod)) {
                  read.checkCast(toParameterizedType(field.getThriftType()));
              }
  
              // coerce the type
              if (field.getCoercion().isPresent()) {
                  read.invokeStatic(field.getCoercion().get().getFromThrift());
              }
  
              // store protocol value
              read.storeVariable(unionData.get(field.getId()));
  
              // go back to top of loop
              read.gotoLabel("while-begin");
          }
  
          // default case
          read.visitLabel("default")
                  .loadVariable(protocol)
                  .invokeVirtual(TProtocolReader.class, "skipFieldData", void.class)
                  .gotoLabel("while-begin");
  
          // end of while loop
          read.visitLabel("while-end");
  
          // protocol.readStructEnd();
          read.loadVariable(protocol)
                  .invokeVirtual(TProtocolReader.class, "readStructEnd", void.class);
  
          return unionData;
      }