Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 74 additions & 18 deletions public/templates/macros.njk
Original file line number Diff line number Diff line change
Expand Up @@ -38,37 +38,95 @@

{# Recursive Borsh Reader, responsible for handling collections and any nested levels #}
{% macro borshReader(field) %}
{{ recursiveReader(field.nesting, field.baseType, field.isStruct, field.size) }}
{{ recursiveReader(field.nesting, field.baseType, field.isStruct, field.size, field.format) }}
{% endmacro %}

{% macro recursiveReader(depth, baseType, isStruct, size) %}
{% macro recursiveReader(depth, baseType, isStruct, size, format) %}
{% if depth > 5 %}
// ERROR: Nesting depth exceeds supported limit (5)
throw Exception('Nesting depth exceeds supported limit(5)');
{% elif depth == 0 %}
{% if isStruct %}
{{ baseType }}Borsh.fromBorsh(reader)
{% else %}
{{ baseTypeReader(baseType, false, size) }}
{{ baseTypeReader(baseType, false, size, format) }}
{% endif %}
{% else %}
reader.readArray(() {
{% if isStruct %}
// item is a struct, call fromBorsh per item
return {{ baseType }}Borsh.fromBorsh(reader);
{% else %}
return {{ recursiveReader(depth - 1, baseType, false, size) }};
return {{ recursiveReader(depth - 1, baseType, false, size, format) }};
{% endif %}
})
{% endif %}
{% endmacro %}

{% macro intFormatUtil(format, isWrite, varName='value') %}
{% if format == 'u8' %}
{% if isWrite %}
writer.writeU8({{ varName }})
{% else %}
reader.readU8()
{% endif %}
{% elif format == 'i8' %}
{% if isWrite %}
writer.writeI8({{ varName }})
{% else %}
reader.readI8()
{% endif %}
{% elif format == 'u16' %}
{% if isWrite %}
writer.writeU16({{ varName }})
{% else %}
reader.readU16()
{% endif %}
{% elif format == 'i16' %}
{% if isWrite %}
writer.writeI16({{ varName }})
{% else %}
reader.readI16()
{% endif %}
{% elif format == 'u32' %}
{% if isWrite %}
writer.writeU32({{ varName }})
{% else %}
reader.readU32()
{% endif %}
{% elif format == 'i32' %}
{% if isWrite %}
writer.writeI32({{ varName }})
{% else %}
reader.readI32()
{% endif %}
{% elif format == 'u64' %}
{% if isWrite %}
writer.writeU64({{ varName }})
{% else %}
reader.readU64()
{% endif %}
{% elif format == 'i64' %}
{% if isWrite %}
writer.writeI64({{ varName }})
{% else %}
reader.readI64()
{% endif %}
{% elif format == 'u128' or format == 'i128' %}
{% if isWrite %}
writer.writeBigInt({{ varName }})
{% else %}
reader.readBigInt()
{% endif %}
{% else %}
throw Exception('Unsupported number format: {{ format }}');
{% endif %}
{% endmacro %}

{# Reader for the base types (no nesting) #}
{% macro baseTypeReader(baseType, isStruct, size) %}
{% if baseType == 'int' %}
reader.readInt()
{% elif baseType == 'BigInt' %}
reader.readBigInt()
{% macro baseTypeReader(baseType, isStruct, size, format) %}
{% if baseType == 'int' or baseType == 'BigInt' %}
{{ intFormatUtil(format, false) }}
{% elif baseType == 'String' %}
reader.readString()
{% elif baseType == 'Uint8List' %}
Expand Down Expand Up @@ -100,10 +158,10 @@
{# Recursive Borsh Writer, responsible to handle nested type of collections #}
{% macro borshWriter(field, overrideFieldName="") %}
{% set name = overrideFieldName if overrideFieldName else field.name %}
{{ recursiveWriter(name, field.nesting, field.baseType, field.isStruct, field.size) }}
{{ recursiveWriter(name, field.nesting, field.baseType, field.isStruct, field.size, field.format) }}
{% endmacro %}

{% macro recursiveWriter(varName, depth, baseType, isStruct, size) %}
{% macro recursiveWriter(varName, depth, baseType, isStruct, size, format) %}
{% if depth > 5 %}
// ERROR: Nesting depth exceeds supported limit (5)
throw Exception('Nesting depth exceeds supported limit(5)');
Expand All @@ -112,26 +170,24 @@
{{ varName }}.toBorsh(writer);
{% else %}
{# TODO: I have problem here because of these recursions i put ';' twice because twice is iterated here first trough writeArray and on the recursion i go inside here and i put ';' again #}
{{ baseTypeWriter(baseType, varName, size) }};
{{ baseTypeWriter(baseType, varName, size, format) }};
{% endif %}
{% else %}
writer.writeArray<{{ baseType }}>({{ varName }}, ({{ baseType }} item) {
{% if isStruct %}
// Each item is a struct
item.toBorsh(writer);
{% else %}
{{ recursiveWriter("item", depth - 1, baseType, false, size) }};
{{ recursiveWriter("item", depth - 1, baseType, false, size, format) }};
{% endif %}
});
{% endif %}
{% endmacro %}

{# Base Writer, no nested types inside #}
{% macro baseTypeWriter(baseType, varName, size) %}
{% if baseType == 'int' %}
writer.writeInt({{ varName }})
{% elif baseType == 'BigInt' %}
writer.writeBigInt({{ varName }})
{% macro baseTypeWriter(baseType, varName, size, format) %}
{% if baseType == 'int' or baseType == 'BigInt' %}
{{ intFormatUtil(format, true, varName) }}
{% elif baseType == 'String' %}
writer.writeString({{ varName }})
{% elif baseType == 'Uint8List' %}
Expand Down
13 changes: 11 additions & 2 deletions src/TypeManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ export type TypeManifestOptions = {
export interface TypeManifestField {
baseType: string;
field: string;
format?: string; // Since dart have `int` or `BigInt` i need to know the original format to handle the borsh read/write
name: string;
nesting: number;
optional: boolean;
size?: number;
size?: number; // In case of fixed-sized i need to know the size to handle the borsh read/write
type: string;
}

Expand All @@ -38,9 +39,11 @@ export function extractFieldsFromTypeManifest(typeManifest: TypeManifest): TypeM
.split('\n')
.map((line): TypeManifestField | null => {
// That handles lines like: final Uint8List fieldName; and extracts the type and name in order to be used from borsh readers/writers
const match = line.trim().match(/^final\s+([\w<>, ?]+)(?:\s*\/\*\s*length:\s*\d+\s*\*\/)?\s+(\w+);$/);
const match = line.trim().match(/^final\s+([\w<>, ?]+)(?:\s*\/\*.*?\*\/)*\s+(\w+);$/);
if (match && match[2] !== 'discriminator') {
let size: number | undefined; // Placeholder for size extraction logic if needed
let format: string | undefined; // Placeholder for format extraction logic if needed

const isOptional = /\?$/.test(match[1].trim()); // check if the string ends with a '?'
const rawType = match[1].replace(/\?$/, '').trim();

Expand All @@ -49,6 +52,11 @@ export function extractFieldsFromTypeManifest(typeManifest: TypeManifest): TypeM
size = parseInt(lengthMatch[1]);
}

const typeMatch = line.match(/\/\*\s*type:\s*([\w<>, ?]+)\s*\*\//); // Extract type if present
if (typeMatch) {
format = typeMatch[1].trim();
}

// Count nesting depth of List<>
let nesting = 0;
let inner = rawType;
Expand All @@ -62,6 +70,7 @@ export function extractFieldsFromTypeManifest(typeManifest: TypeManifest): TypeM
return {
baseType: inner.trim(),
field: line,
format: format,
name: match[2],
nesting,
optional: isOptional,
Expand Down
50 changes: 45 additions & 5 deletions src/getTypeManifestVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,30 +239,70 @@ export function getTypeManifestVisitor(options: TypeManifestOptions) {
visitNumberType(numberType: NumberTypeNode): TypeManifest {
switch (numberType.format) {
case 'u8':
case 'u16':
case 'u32':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'int /* type: u8 */',
};
case 'i8':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'int /* type: i8 */',
};
case 'u16':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'int /* type: u16 */',
};
case 'i16':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'int /* type: i16 */',
};
case 'u32':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'int /* type: u32 */',
};
case 'i32':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'int',
type: 'int /* type: i32 */',
};
case 'u64':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'BigInt /* type: u64 */',
};
case 'i64':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'BigInt /* type: i64 */',
};
case 'u128':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'BigInt /* type: u128 */',
};
case 'i128':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'BigInt',
type: 'BigInt /* type: i128 */',
};
case 'shortU16':
return {
imports: new ImportMap(),
nestedStructs: [],
type: 'int',
type: 'int /* type: shortU16 */',
};
default:
throw new Error(`Unknown number format: ${numberType.format}`);
Expand Down