Skip to content

Added support for returning Unions #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

osztenkurden
Copy link

@osztenkurden osztenkurden commented Aug 19, 2024

I'm making a JS parser for Counter-Strike 2 demo files, and there are a lot of different protobufs and structs that might be returned, so for ease of DX I added support of Unions for defaultWrite.

@cztomsik
Copy link
Owner

Can you explain what exactly are you trying to parse? I think we could add support for .Union but it should only work for types with tag_type != null and it should work both ways.

Regarding u0 that sounds weird and I am not sure it should be provided by default.

Also, I think you have missed this section in the README
https://github.com/cztomsik/napigen?tab=readme-ov-file#hooks

You can simply define your own type overrides, both for reading and writing, and they can be different so it should be enough for your use-case.

For example here, I am only defining napigenWrite because I want to serialize something differently, but I could do the same for reading.
https://github.com/cztomsik/ggml-js/blob/main/src/main.zig#L42

@osztenkurden
Copy link
Author

Well, regarding hooks, you are completely right, I could have add it there, but seems like a quality-of-life thing, when dealing with custom data coming from files that with a small tweak more people could use it

In my case, when decoding a protobuf from a singular data-frame, it might result in a lot of different structs, or array of structs, and for me the best way to handle them is to get them into a union, and then just receive a value on the JS side.

I'm gonna mark here that I'm doing my project as a starting point in zig, as I never used any low level language, so that might not make sense, but thought that someone might use this Union support.

In u0 case, well, I dont have strong feeling about supporting this, might as well remove that, as this will be much more trivial to add than unions

@cztomsik
Copy link
Owner

Ok, can you change the PR to point into the dev branch? I will do few changes tomorrow and merge it.

@osztenkurden osztenkurden changed the base branch from main to dev August 20, 2024 05:46
@osztenkurden osztenkurden changed the title Added support for returning Unions and u0 Added support for returning Unions Aug 20, 2024
@osztenkurden
Copy link
Author

Removed u8, comment and changed do dev branch

@cztomsik
Copy link
Owner

cztomsik commented Aug 20, 2024

Hm, I just noticed you are not using the union tag anywhere in the result. This is a problem, suppose you have struct like this:

// Just a simple wrapper, indicating result from db.query() or something like that
// Notice that you have same payload type and the meaning comes from the tag enum
const DbResult = union(enum) {
  created: u32, // id of the created row
  updated: u32, // num of rows changed
  deleted: u32, // num of rows deleted
  error: SomeErrorType,
}

and now if you try to pass it to the JS-side (so your new defaultWrite() gets called), you will only get the payload at the JS side. You will loose the "kind" of the result. So you will have no way to tell if the payload is id or number of rows updated/deleted.

I think this is a reason why I did not include the Union impl in the first place, I wanted to have some thinking about it first. What we do, must be reversible, so what I'd suggest is probably encoding the value with "prefix" field for the tag:

Zig JS
DbResult{ .created = 1 }  { created: 1 }
DbResult{ .updated = 1 }  { updated: 1 }
DbResult{ .deleted = 1 }  { deleted: 1 }
... ...

@osztenkurden
Copy link
Author

Sounds good, I'll try to implement it

@cztomsik
Copy link
Owner

cztomsik commented Aug 22, 2024

We should also support parsing, it will be trickier, so I can do it myself, but if you feel into it here's what's needed:

  • call try check(napi_get_property_names()) to get array of object's property names (this can fail for invalid data but that try check() should be enough I think
  • call self.getElement(prop_names, 0)
  • call self.readString() on that
  • do inline for (std.meta.fields(T)) |f| over all variants
  • if (std.mem.eql(u8, f.name, prop_name))
  • return @unionInit(f.name, self.readStruct(f.type))

@osztenkurden
Copy link
Author

Ill try during the weekend

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants