-
Notifications
You must be signed in to change notification settings - Fork 931
Introduce literal enum codec. #2296
Comments
Oh that's interesting. Technically you can use Thanks for all your codec feedback btw, it's much appreciated. |
Right - to bypass the export enum Currency {
sol,
like,
usdc,
}
export const CURRENCY_CODEC = mapCodec(
getScalarEnumCodec(Currency),
(v: "sol" | "like" | "usdc") => {
switch (v) {
case "sol":
return Currency.sol;
case "like":
return Currency.like;
case "usdc":
return Currency.usdc;
}
},
(v: ScalarEnumFrom<typeof Currency>) => {
switch (v) {
case 0:
case Currency.sol:
return "sol";
case 1:
case Currency.like:
return "like";
case 2:
case Currency.usdc:
return "usdc";
}
throw new Error("Invalid currency");
}
); |
Oh no I meant that you can use a scalar enum with string variants like so: export enum Currency {
sol = 'sol',
like = 'like',
usdc = 'usdc',
}
const codec = getScalarEnumCodec(Currency);
codec.encode('sol'); // 0x00
codec.decode(new Uint8Array([1])); // 'like' |
Ah got it. Thought the enum had to map to numbers as there are some cases where we can't rely on the enum's key ordering to figure out an enum's byte representation/value (if, say, we wanted to define a scalar enum codec schema in JSON). Based on the way the types are defined for const codec = getScalarEnumCodec({
sol: "sol",
like: "like",
usdc: "usdc"
}); |
Knowing that, do you still think there is a need for |
Hmm I still think it would be a nice-to-have - Zod for example has separate I think it would be better for |
Do you mean this? export enum Currency {
sol,
like,
usdc,
}
const codec = getScalarEnumCodec(Currency);
codec.encode('sol'); // 0x00
codec.encode('like'); // 0x01
codec.encode('usdc'); // 0x02 Because that's already possible. It's just that when you decode, you'll get the values of the enum. codec.decode(new Uint8Array([0])); // Currency.sol (same as 0)
codec.decode(new Uint8Array([1])); // Currency.like (same as 1)
codec.decode(new Uint8Array([2])); // Currency.usdc (same as 2) |
I mean like this: import { getScalarEnumCodec } from "@solana/web3.js";
export enum Currency {
sol = 3,
like = 5,
usdc = 9,
}
const codec = getScalarEnumCodec(Currency);
console.log(codec.encode('sol')); // Should be 0x03
console.log(codec.encode('like')); // Should be 0x05
console.log(codec.encode('usdc')); // Should be 0x09 |
Oh wait is that not the case?! If so that's a bug then. Do you want to raise a separate issue for it? |
Yep, will file it. For the case of this issue then, what do you think about having |
I think it'd be cleaner both internally and for the end-user to split them up into two separate codecs. I think something like |
This function could support other types of literals (hence |
That's a good point! I think with the current naming convention But all your feedback has gotten me thinking a bit more about it and I think it may be confusing for us to use the term Therefore, I've been thinking about the following refactoring. // Current API.
getScalarEnumCodec();
getDataEnumCodec();
getLiteralEnumCodec(); // Soon.
// Proposed API.
getEnumCodec(); // Because that's technically the only one that uses enums.
getStructUnionCodec(); // With a new option that enables you to configure the discriminant.
getLiteralUnionCodec();
getUnionCodec(); // (optionally) a more generic helper that the previous two can use under the hood. |
If we are to reference how types are named in C or Zig, the naming convention would be the following: getEnumCodec(); // Enums are available in C and Zig. Should support both JavaScript unions and an array of literals.
getDiscriminatedUnionCodec(); // Discriminated unions are available in C and Zig. |
Since the encoded/decoded types are actually represented as TypeScript types, I think it makes more sense to follow a naming convention that's idiomatic with the JavaScript/TypeScript language instead of C, Rust or Zig. It is up to the designer of these codecs to decide how they want to represent these byte arrays in something that fits in TypeScript land. Related: I'm also thinking of renaming |
Hmm if we are to follow TypeScript's naming conventions, what about these? getRecordCodec(); // Represents a Record<string, any>.
getLiteralEnumCodec(); // Represents a literal enum expression.
getDiscriminatedUnionCodec(); // Represents a union that is discriminated by an expression. |
) Now that "Data Enum" is called "Discriminated Union", "Scalar Enum" can also be appropriately renamed as "Enum". See #2296.
…ta-structures (#2394) This PR adds a new `getLiteralUnionCodec` that functions similarly to `getEnumCodec` but uses TypeScript unions to describe all possible values. ```ts const codec = getLiteralUnionCodec(['left', 'right', 'up', 'down']); // ^? FixedSizeCodec<"left" | "right" | "up" | "down"> const bytes = codec.encode('left'); // 0x00 const value = codec.decode(bytes); // 'left' ``` Fixes #2296
In the end, I went for: getEnumCodec();
getLiteralEnumCodec();
getDiscriminatedUnionCodec(); As you suggested initially. |
Because there has been no activity on this issue for 7 days since it was closed, it has been automatically locked. Please open a new issue if it requires a follow up. |
As of right now, we have both
getScalarEnumCodec
andgetDataEnumCodec
for specifying enums.It would be useful to be able to specify enum codecs with string/number literals as its input/output, similar to Zod's
z.enum([])
.Example:
The text was updated successfully, but these errors were encountered: