Skip to content

Commit

Permalink
feat: add yaml parsing support to mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
isum committed Jan 31, 2025
1 parent 4311d0e commit 59f799f
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/rare-carpets-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphprotocol/graph-ts': minor
---

add yaml support in mappings
243 changes: 243 additions & 0 deletions packages/ts/common/yaml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import { Bytes, Result, TypedMap } from './collections';
import { BigInt } from './numbers';

/**
* Host YAML interface.
*/
export declare namespace yaml {
/**
* Parses a YAML document from UTF-8 encoded bytes.
* Aborts mapping execution if the bytes cannot be parsed.
*/
function fromBytes(data: Bytes): YAMLValue;

/**
* Parses a YAML document from UTF-8 encoded bytes.
* Returns `Result.error == true` if the bytes cannot be parsed.
*/
function try_fromBytes(data: Bytes): Result<YAMLValue, bool>;
}

export namespace yaml {
/**
* Parses a YAML document from a UTF-8 encoded string.
* Aborts mapping execution if the string cannot be parsed.
*/
export function fromString(data: string): YAMLValue {
const bytes = Bytes.fromUTF8(data);

return yaml.fromBytes(bytes);
}

/**
* Parses a YAML document from a UTF-8 encoded string.
* Returns `Result.error == true` if the string cannot be parsed.
*/
export function try_fromString(data: string): Result<YAMLValue, bool> {
const bytes = Bytes.fromUTF8(data);

return yaml.try_fromBytes(bytes);
}
}

/**
* All possible YAML value types.
*/
export enum YAMLValueKind {
NULL = 0,
BOOL = 1,
NUMBER = 2,
STRING = 3,
ARRAY = 4,
OBJECT = 5,
TAGGED = 6,
}

/**
* Pointer type for `YAMLValue` data.
*
* Big enough to fit any pointer or native `YAMLValue.data`.
*/
export type YAMLValuePayload = u64;

export class YAMLValue {
kind: YAMLValueKind;
data: YAMLValuePayload;

constructor(kind: YAMLValueKind, data: YAMLValuePayload) {
this.kind = kind;
this.data = data;
}

static newNull(): YAMLValue {
return new YAMLValue(YAMLValueKind.NULL, 0);
}

static newBool(data: bool): YAMLValue {
return new YAMLValue(YAMLValueKind.BOOL, data ? 1 : 0);
}

static newI64(data: i64): YAMLValue {
return new YAMLValue(YAMLValueKind.NUMBER, changetype<usize>(data.toString()));
}

static newU64(data: u64): YAMLValue {
return new YAMLValue(YAMLValueKind.NUMBER, changetype<usize>(data.toString()));
}

static newF64(data: f64): YAMLValue {
return new YAMLValue(YAMLValueKind.NUMBER, changetype<usize>(data.toString()));
}

static newBigInt(data: BigInt): YAMLValue {
return new YAMLValue(YAMLValueKind.STRING, changetype<usize>(data.toString()));
}

static newString(data: string): YAMLValue {
return new YAMLValue(YAMLValueKind.STRING, changetype<usize>(data));
}

static newArray(data: Array<YAMLValue>): YAMLValue {
return new YAMLValue(YAMLValueKind.ARRAY, changetype<usize>(data));
}

static newObject(data: TypedMap<YAMLValue, YAMLValue>): YAMLValue {
return new YAMLValue(YAMLValueKind.OBJECT, changetype<usize>(data));
}

static newTagged(tag: string, value: YAMLValue): YAMLValue {
let tagged = new YAMLTaggedValue(tag, value);
return new YAMLValue(YAMLValueKind.TAGGED, changetype<usize>(tagged));
}

isNull(): bool {
return this.kind == YAMLValueKind.NULL;
}

isBool(): bool {
return this.kind == YAMLValueKind.BOOL;
}

isNumber(): bool {
return this.kind == YAMLValueKind.NUMBER;
}

isString(): bool {
return this.kind == YAMLValueKind.STRING;
}

isArray(): bool {
return this.kind == YAMLValueKind.ARRAY;
}

isObject(): bool {
return this.kind == YAMLValueKind.OBJECT;
}

isTagged(): bool {
return this.kind == YAMLValueKind.TAGGED;
}

toBool(): bool {
assert(this.isBool(), 'YAML value is not a boolean');
return this.data != 0;
}

toNumber(): string {
assert(this.isNumber(), 'YAML value is not a number');
return changetype<string>(this.data as usize);
}

toI64(): i64 {
return I64.parseInt(this.toNumber());
}

toU64(): u64 {
return U64.parseInt(this.toNumber());
}

toF64(): f64 {
return F64.parseFloat(this.toNumber());
}

toBigInt(): BigInt {
assert(this.isNumber() || this.isString(), 'YAML value is not numeric');
return BigInt.fromString(changetype<string>(this.data as usize));
}

toString(): string {
assert(this.isString(), 'YAML value is not a string');
return changetype<string>(this.data as usize);
}

toArray(): Array<YAMLValue> {
assert(this.isArray(), 'YAML value is not an array');
return changetype<Array<YAMLValue>>(this.data as usize);
}

toObject(): TypedMap<YAMLValue, YAMLValue> {
assert(this.isObject(), 'YAML value is not an object');
return changetype<TypedMap<YAMLValue, YAMLValue>>(this.data as usize);
}

toTagged(): YAMLTaggedValue {
assert(this.isTagged(), 'YAML value is not tagged');
return changetype<YAMLTaggedValue>(this.data as usize);
}

// Allows access to YAML values from within an object.
@operator('==')
static __equals(a: YAMLValue, b: YAMLValue): bool {
if (a.isBool() && b.isBool()) {
return a.toBool() == b.toBool()
}

if (a.isNumber() && b.isNumber()) {
return a.toNumber() == b.toNumber();
}

if (a.isString() && b.isString()) {
return a.toString() == b.toString();
}

if (a.isTagged() && b.isTagged()) {
return a.toTagged() == b.toTagged();
}

return false;
}

// Makes it easier to access a specific index in a YAML array or a string key in a YAML object.
//
// Examples:
// Usage in YAML objects: `yaml.fromString(subgraphManifest)['specVersion']`;
// Nesting is also supported: `yaml.fromString(subgraphManifest)['schema']['file']`;
// Usage in YAML arrays: `yaml.fromString(subgraphManifest)['dataSources']['0']`;
// YAML arrays and objects: `yaml.fromString(subgraphManifest)['dataSources']['0']['source']['address']`;
@operator('[]')
__get(index: string): YAMLValue {
assert(this.isArray() || this.isObject(), 'YAML value can not be accessed by index');

if (this.isArray()) {
return this.toArray()[I32.parseInt(index)];
}

return this.toObject().mustGet(YAMLValue.newString(index));
}
}

export class YAMLTaggedValue {
tag: string;
value: YAMLValue;

constructor(tag: string, value: YAMLValue) {
this.tag = tag;
this.value = value;
}

// Allows access to YAML values from within an object.
@operator('==')
static __equals(a: YAMLTaggedValue, b: YAMLTaggedValue): bool {
return a.tag == b.tag && a.value == b.value;
}
}
32 changes: 31 additions & 1 deletion packages/ts/global/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { starknet } from '../chain/starknet';
import { Bytes, Entity, Result, TypedMap, TypedMapEntry, Wrapped } from '../common/collections';
import { BigDecimal } from '../common/numbers';
import { JSONValue, Value } from '../common/value';
import { YAMLValue, YAMLTaggedValue } from '../common/yaml';

/**
* Contains type IDs and their discriminants for every blockchain supported by Graph-Node.
Expand Down Expand Up @@ -247,7 +248,17 @@ export enum TypeId {
```
*/

// Reserved discriminant space for a future blockchain type IDs: [4,500, 5,499]
// Reserved discriminant space for YAML type IDs: [4,500, 5,499]
YamlValue = 4500,
YamlTaggedValue = 4501,
YamlTypedMapEntryValueValue = 4502,
YamlTypedMapValueValue = 4503,
YamlArrayValue = 4504,
YamlArrayTypedMapEntryValueValue = 4505,
YamlWrappedValue = 4506,
YamlResultValueBool = 4507,

// Reserved discriminant space for a future blockchain type IDs: [5,500, 6,499]
}

export function id_of_type(typeId: TypeId): usize {
Expand Down Expand Up @@ -593,6 +604,25 @@ export function id_of_type(typeId: TypeId): usize {
return idof<starknet.Event>();
case TypeId.StarknetArrayBytes:
return idof<Array<Bytes>>();
/**
* YAML type IDs.
*/
case TypeId.YamlValue:
return idof<YAMLValue>();
case TypeId.YamlTaggedValue:
return idof<YAMLTaggedValue>();
case TypeId.YamlTypedMapEntryValueValue:
return idof<TypedMapEntry<YAMLValue, YAMLValue>>();
case TypeId.YamlTypedMapValueValue:
return idof<TypedMap<YAMLValue, YAMLValue>>();
case TypeId.YamlArrayValue:
return idof<Array<YAMLValue>>();
case TypeId.YamlArrayTypedMapEntryValueValue:
return idof<Array<TypedMapEntry<YAMLValue, YAMLValue>>>();
case TypeId.YamlWrappedValue:
return idof<Wrapped<YAMLValue>>();
case TypeId.YamlResultValueBool:
return idof<Result<YAMLValue, boolean>>();
default:
return 0;
}
Expand Down
1 change: 1 addition & 0 deletions packages/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from './common/datasource';
export * from './common/json';
export * from './common/numbers';
export * from './common/value';
export * from './common/yaml';

/**
* Host store interface.
Expand Down

0 comments on commit 59f799f

Please sign in to comment.