tink_json icon indicating copy to clipboard operation
tink_json copied to clipboard

Integer constants as enums

Open rhacking opened this issue 7 years ago • 4 comments

As far as I can see, there's no way of parsing target as an enum in the following example:

    "bufferViews": [
        {
            "buffer": 0,
            "byteOffset": 576,
            "byteLength": 72,
            "target": 34963
        },
        {
            "buffer": 0,
            "byteOffset": 0,
            "byteLength": 576,
            "byteStride": 12,
            "target": 34962
        }
    ],

The following results in [haxe] invalid use of @:json

enum BufferTarget {
    @:json(34963) ElementArrayBuffer;
    @:json(34962) ArrayBuffer;
}

Right now I'm using an enum abstract, but I think it would be more elegant if something like the above were possible.

rhacking avatar Feb 10 '18 18:02 rhacking

Well, the way it's "supposed to work" is more like:

enum Buffer {
    @:json({ target: 34963 }) ElementArrayBuffer(elementArrayBuffer:{ buffer:Int, byteOffset:Int, byteLength:Int });
    @:json({ target: 34962 }) ArrayBuffer(arrayBuffer:{ buffer:Int, byteOffset:Int, byteLength:Int, byteStride:Int });
}

The point being that the byteStride is actually only there if needed. But I'll look into making it possible this way ;)

back2dos avatar Feb 11 '18 10:02 back2dos

Hmm, I can see why it would be better to do it like this (as indeed byteStride is only there for ArrayBuffer), but then you can't access, say, the buffer property of an arbitrary buffer without first using a switch to extract it, even though any buffer will have this property.

rhacking avatar Feb 11 '18 10:02 rhacking

Well, could solve that with abstracts or static extensions, but without really knowing the use case I can hardly give qualified advise ^^

That said, I will look into making this possible per @:json. Right now you can do something like this:

//BufferTarget.hx
package whatever;

class BufferTargetParser {
  public function new(_) {}
  public function parse(value:Int):BufferTarget
    return switch value {
      case 34963: ElementArrayBuffer;
      case 34962: ArrayBuffer;
      default: throw 'Invalid buffer target $value';
    }
}
@:jsonParse(whatever.BufferTarget.BufferTargetParser)//must be fully qualified name
enum BufferTarget {
    @:json(34963) ElementArrayBuffer;
    @:json(34962) ArrayBuffer;
}

Here are the tests for custom parsing and stringifying

back2dos avatar Feb 11 '18 11:02 back2dos

Ah, I didn't know you could that. I solved it like this now:

typedef BufferViewRaw = {
    var buffer : Int;
    var byteLength : Int;
    var byteOffset : Int;
    var target : Int;
    @:optional var byteStride : Int;
}

@:jsonParse(gltf.GLTFLoader.BufferViewParser)
typedef BufferView = {
    var buffer : Int;
    var byteLength : Int;
    var byteOffset : Int;
    var target : BufferTarget;
}

class BufferViewParser {
    public function new(_) {}

    public function parse(v:BufferViewRaw):BufferView {
        return {
            buffer: v.buffer, 
            byteLength: v.byteLength, 
            byteOffset: v.byteOffset, 
            target: switch v.target {
                case 34963: ElementArrayBuffer;
                case 34962: ArrayBuffer(v.byteStride == null ? Tight : Fixed(v.byteStride));
                default: throw 'Invalid buffer target ${v.target}';
            }
        };
    }
}

enum BufferTarget {
    ElementArrayBuffer;
    ArrayBuffer(stride : Stride);
}

It's a bit bulky this way, but I think the final type it gets makes more sense. Though the :json modification would still be helpful for standalone enums that don't influence which other fields there are.

rhacking avatar Feb 11 '18 14:02 rhacking