Skip to content

Commit 59f799f

Browse files
committed
feat: add yaml parsing support to mappings
1 parent 4311d0e commit 59f799f

File tree

4 files changed

+280
-1
lines changed

4 files changed

+280
-1
lines changed

.changeset/rare-carpets-tell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphprotocol/graph-ts': minor
3+
---
4+
5+
add yaml support in mappings

packages/ts/common/yaml.ts

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
import { Bytes, Result, TypedMap } from './collections';
2+
import { BigInt } from './numbers';
3+
4+
/**
5+
* Host YAML interface.
6+
*/
7+
export declare namespace yaml {
8+
/**
9+
* Parses a YAML document from UTF-8 encoded bytes.
10+
* Aborts mapping execution if the bytes cannot be parsed.
11+
*/
12+
function fromBytes(data: Bytes): YAMLValue;
13+
14+
/**
15+
* Parses a YAML document from UTF-8 encoded bytes.
16+
* Returns `Result.error == true` if the bytes cannot be parsed.
17+
*/
18+
function try_fromBytes(data: Bytes): Result<YAMLValue, bool>;
19+
}
20+
21+
export namespace yaml {
22+
/**
23+
* Parses a YAML document from a UTF-8 encoded string.
24+
* Aborts mapping execution if the string cannot be parsed.
25+
*/
26+
export function fromString(data: string): YAMLValue {
27+
const bytes = Bytes.fromUTF8(data);
28+
29+
return yaml.fromBytes(bytes);
30+
}
31+
32+
/**
33+
* Parses a YAML document from a UTF-8 encoded string.
34+
* Returns `Result.error == true` if the string cannot be parsed.
35+
*/
36+
export function try_fromString(data: string): Result<YAMLValue, bool> {
37+
const bytes = Bytes.fromUTF8(data);
38+
39+
return yaml.try_fromBytes(bytes);
40+
}
41+
}
42+
43+
/**
44+
* All possible YAML value types.
45+
*/
46+
export enum YAMLValueKind {
47+
NULL = 0,
48+
BOOL = 1,
49+
NUMBER = 2,
50+
STRING = 3,
51+
ARRAY = 4,
52+
OBJECT = 5,
53+
TAGGED = 6,
54+
}
55+
56+
/**
57+
* Pointer type for `YAMLValue` data.
58+
*
59+
* Big enough to fit any pointer or native `YAMLValue.data`.
60+
*/
61+
export type YAMLValuePayload = u64;
62+
63+
export class YAMLValue {
64+
kind: YAMLValueKind;
65+
data: YAMLValuePayload;
66+
67+
constructor(kind: YAMLValueKind, data: YAMLValuePayload) {
68+
this.kind = kind;
69+
this.data = data;
70+
}
71+
72+
static newNull(): YAMLValue {
73+
return new YAMLValue(YAMLValueKind.NULL, 0);
74+
}
75+
76+
static newBool(data: bool): YAMLValue {
77+
return new YAMLValue(YAMLValueKind.BOOL, data ? 1 : 0);
78+
}
79+
80+
static newI64(data: i64): YAMLValue {
81+
return new YAMLValue(YAMLValueKind.NUMBER, changetype<usize>(data.toString()));
82+
}
83+
84+
static newU64(data: u64): YAMLValue {
85+
return new YAMLValue(YAMLValueKind.NUMBER, changetype<usize>(data.toString()));
86+
}
87+
88+
static newF64(data: f64): YAMLValue {
89+
return new YAMLValue(YAMLValueKind.NUMBER, changetype<usize>(data.toString()));
90+
}
91+
92+
static newBigInt(data: BigInt): YAMLValue {
93+
return new YAMLValue(YAMLValueKind.STRING, changetype<usize>(data.toString()));
94+
}
95+
96+
static newString(data: string): YAMLValue {
97+
return new YAMLValue(YAMLValueKind.STRING, changetype<usize>(data));
98+
}
99+
100+
static newArray(data: Array<YAMLValue>): YAMLValue {
101+
return new YAMLValue(YAMLValueKind.ARRAY, changetype<usize>(data));
102+
}
103+
104+
static newObject(data: TypedMap<YAMLValue, YAMLValue>): YAMLValue {
105+
return new YAMLValue(YAMLValueKind.OBJECT, changetype<usize>(data));
106+
}
107+
108+
static newTagged(tag: string, value: YAMLValue): YAMLValue {
109+
let tagged = new YAMLTaggedValue(tag, value);
110+
return new YAMLValue(YAMLValueKind.TAGGED, changetype<usize>(tagged));
111+
}
112+
113+
isNull(): bool {
114+
return this.kind == YAMLValueKind.NULL;
115+
}
116+
117+
isBool(): bool {
118+
return this.kind == YAMLValueKind.BOOL;
119+
}
120+
121+
isNumber(): bool {
122+
return this.kind == YAMLValueKind.NUMBER;
123+
}
124+
125+
isString(): bool {
126+
return this.kind == YAMLValueKind.STRING;
127+
}
128+
129+
isArray(): bool {
130+
return this.kind == YAMLValueKind.ARRAY;
131+
}
132+
133+
isObject(): bool {
134+
return this.kind == YAMLValueKind.OBJECT;
135+
}
136+
137+
isTagged(): bool {
138+
return this.kind == YAMLValueKind.TAGGED;
139+
}
140+
141+
toBool(): bool {
142+
assert(this.isBool(), 'YAML value is not a boolean');
143+
return this.data != 0;
144+
}
145+
146+
toNumber(): string {
147+
assert(this.isNumber(), 'YAML value is not a number');
148+
return changetype<string>(this.data as usize);
149+
}
150+
151+
toI64(): i64 {
152+
return I64.parseInt(this.toNumber());
153+
}
154+
155+
toU64(): u64 {
156+
return U64.parseInt(this.toNumber());
157+
}
158+
159+
toF64(): f64 {
160+
return F64.parseFloat(this.toNumber());
161+
}
162+
163+
toBigInt(): BigInt {
164+
assert(this.isNumber() || this.isString(), 'YAML value is not numeric');
165+
return BigInt.fromString(changetype<string>(this.data as usize));
166+
}
167+
168+
toString(): string {
169+
assert(this.isString(), 'YAML value is not a string');
170+
return changetype<string>(this.data as usize);
171+
}
172+
173+
toArray(): Array<YAMLValue> {
174+
assert(this.isArray(), 'YAML value is not an array');
175+
return changetype<Array<YAMLValue>>(this.data as usize);
176+
}
177+
178+
toObject(): TypedMap<YAMLValue, YAMLValue> {
179+
assert(this.isObject(), 'YAML value is not an object');
180+
return changetype<TypedMap<YAMLValue, YAMLValue>>(this.data as usize);
181+
}
182+
183+
toTagged(): YAMLTaggedValue {
184+
assert(this.isTagged(), 'YAML value is not tagged');
185+
return changetype<YAMLTaggedValue>(this.data as usize);
186+
}
187+
188+
// Allows access to YAML values from within an object.
189+
@operator('==')
190+
static __equals(a: YAMLValue, b: YAMLValue): bool {
191+
if (a.isBool() && b.isBool()) {
192+
return a.toBool() == b.toBool()
193+
}
194+
195+
if (a.isNumber() && b.isNumber()) {
196+
return a.toNumber() == b.toNumber();
197+
}
198+
199+
if (a.isString() && b.isString()) {
200+
return a.toString() == b.toString();
201+
}
202+
203+
if (a.isTagged() && b.isTagged()) {
204+
return a.toTagged() == b.toTagged();
205+
}
206+
207+
return false;
208+
}
209+
210+
// Makes it easier to access a specific index in a YAML array or a string key in a YAML object.
211+
//
212+
// Examples:
213+
// Usage in YAML objects: `yaml.fromString(subgraphManifest)['specVersion']`;
214+
// Nesting is also supported: `yaml.fromString(subgraphManifest)['schema']['file']`;
215+
// Usage in YAML arrays: `yaml.fromString(subgraphManifest)['dataSources']['0']`;
216+
// YAML arrays and objects: `yaml.fromString(subgraphManifest)['dataSources']['0']['source']['address']`;
217+
@operator('[]')
218+
__get(index: string): YAMLValue {
219+
assert(this.isArray() || this.isObject(), 'YAML value can not be accessed by index');
220+
221+
if (this.isArray()) {
222+
return this.toArray()[I32.parseInt(index)];
223+
}
224+
225+
return this.toObject().mustGet(YAMLValue.newString(index));
226+
}
227+
}
228+
229+
export class YAMLTaggedValue {
230+
tag: string;
231+
value: YAMLValue;
232+
233+
constructor(tag: string, value: YAMLValue) {
234+
this.tag = tag;
235+
this.value = value;
236+
}
237+
238+
// Allows access to YAML values from within an object.
239+
@operator('==')
240+
static __equals(a: YAMLTaggedValue, b: YAMLTaggedValue): bool {
241+
return a.tag == b.tag && a.value == b.value;
242+
}
243+
}

packages/ts/global/global.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { starknet } from '../chain/starknet';
66
import { Bytes, Entity, Result, TypedMap, TypedMapEntry, Wrapped } from '../common/collections';
77
import { BigDecimal } from '../common/numbers';
88
import { JSONValue, Value } from '../common/value';
9+
import { YAMLValue, YAMLTaggedValue } from '../common/yaml';
910

1011
/**
1112
* Contains type IDs and their discriminants for every blockchain supported by Graph-Node.
@@ -247,7 +248,17 @@ export enum TypeId {
247248
```
248249
*/
249250

250-
// Reserved discriminant space for a future blockchain type IDs: [4,500, 5,499]
251+
// Reserved discriminant space for YAML type IDs: [4,500, 5,499]
252+
YamlValue = 4500,
253+
YamlTaggedValue = 4501,
254+
YamlTypedMapEntryValueValue = 4502,
255+
YamlTypedMapValueValue = 4503,
256+
YamlArrayValue = 4504,
257+
YamlArrayTypedMapEntryValueValue = 4505,
258+
YamlWrappedValue = 4506,
259+
YamlResultValueBool = 4507,
260+
261+
// Reserved discriminant space for a future blockchain type IDs: [5,500, 6,499]
251262
}
252263

253264
export function id_of_type(typeId: TypeId): usize {
@@ -593,6 +604,25 @@ export function id_of_type(typeId: TypeId): usize {
593604
return idof<starknet.Event>();
594605
case TypeId.StarknetArrayBytes:
595606
return idof<Array<Bytes>>();
607+
/**
608+
* YAML type IDs.
609+
*/
610+
case TypeId.YamlValue:
611+
return idof<YAMLValue>();
612+
case TypeId.YamlTaggedValue:
613+
return idof<YAMLTaggedValue>();
614+
case TypeId.YamlTypedMapEntryValueValue:
615+
return idof<TypedMapEntry<YAMLValue, YAMLValue>>();
616+
case TypeId.YamlTypedMapValueValue:
617+
return idof<TypedMap<YAMLValue, YAMLValue>>();
618+
case TypeId.YamlArrayValue:
619+
return idof<Array<YAMLValue>>();
620+
case TypeId.YamlArrayTypedMapEntryValueValue:
621+
return idof<Array<TypedMapEntry<YAMLValue, YAMLValue>>>();
622+
case TypeId.YamlWrappedValue:
623+
return idof<Wrapped<YAMLValue>>();
624+
case TypeId.YamlResultValueBool:
625+
return idof<Result<YAMLValue, boolean>>();
596626
default:
597627
return 0;
598628
}

packages/ts/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from './common/datasource';
1919
export * from './common/json';
2020
export * from './common/numbers';
2121
export * from './common/value';
22+
export * from './common/yaml';
2223

2324
/**
2425
* Host store interface.

0 commit comments

Comments
 (0)