Skip to content
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

Make nodes generic interfaces #195

Merged
merged 26 commits into from
Apr 9, 2024
5 changes: 5 additions & 0 deletions .changeset/grumpy-knives-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@metaplex-foundation/kinobi": minor
---

Make nodes generic interfaces
49 changes: 39 additions & 10 deletions src/nodes/AccountNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,64 @@ import { InvalidKinobiTreeError, MainCaseString, mainCase } from '../shared';
import { assertIsNode } from './Node';
import { DiscriminatorNode } from './discriminatorNodes';
import { PdaLinkNode, pdaLinkNode } from './linkNodes';
import { StructTypeNode, structTypeNode } from './typeNodes';
import {
ResolveNestedTypeNode,
StructTypeNode,
structTypeNode,
} from './typeNodes';
import { createTypeNodeFromIdl } from './typeNodes/TypeNode';

export type AccountNode = {
export interface AccountNode<
TData extends
ResolveNestedTypeNode<StructTypeNode> = ResolveNestedTypeNode<StructTypeNode>,
TPda extends PdaLinkNode | undefined = PdaLinkNode | undefined,
TDiscriminators extends DiscriminatorNode[] | undefined =
| DiscriminatorNode[]
| undefined,
> {
readonly kind: 'accountNode';

// Children.
readonly data: StructTypeNode;
readonly pda?: PdaLinkNode;
readonly discriminators?: DiscriminatorNode[];
readonly data: TData;
readonly pda?: TPda;
readonly discriminators?: TDiscriminators;

// Data.
readonly name: MainCaseString;
readonly idlName: string;
readonly docs: string[];
readonly size?: number | null;
};
}

export type AccountNodeInput = Omit<Partial<AccountNode>, 'kind' | 'name'> & {
export type AccountNodeInput<
TData extends
ResolveNestedTypeNode<StructTypeNode> = ResolveNestedTypeNode<StructTypeNode>,
TPda extends PdaLinkNode | undefined = PdaLinkNode | undefined,
TDiscriminators extends DiscriminatorNode[] | undefined =
| DiscriminatorNode[]
| undefined,
> = Omit<
Partial<AccountNode<TData, TPda, TDiscriminators>>,
'kind' | 'name'
> & {
readonly name: string;
};

export function accountNode(input: AccountNodeInput): AccountNode {
export function accountNode<
TData extends ResolveNestedTypeNode<StructTypeNode> = StructTypeNode<[]>,
TPda extends PdaLinkNode | undefined = undefined,
const TDiscriminators extends DiscriminatorNode[] | undefined = undefined,
>(
input: AccountNodeInput<TData, TPda, TDiscriminators>
): AccountNode<TData, TPda, TDiscriminators> {
if (!input.name) {
throw new InvalidKinobiTreeError('AccountNode must have a name.');
}
return {
kind: 'accountNode',

// Children.
data: input.data ?? structTypeNode([]),
data: (input.data ?? structTypeNode([])) as TData,
pda: input.pda,
discriminators: input.discriminators,

Expand All @@ -45,7 +72,9 @@ export function accountNode(input: AccountNodeInput): AccountNode {
};
}

export function accountNodeFromIdl(idl: Partial<IdlAccount>): AccountNode {
export function accountNodeFromIdl(
idl: Partial<IdlAccount>
): AccountNode<StructTypeNode> {
const idlName = idl.name ?? '';
const name = mainCase(idlName);
const idlStruct = idl.type ?? { kind: 'struct', fields: [] };
Expand Down
14 changes: 8 additions & 6 deletions src/nodes/DefinedTypeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@ import type { IdlDefinedType } from '../idl';
import { InvalidKinobiTreeError, MainCaseString, mainCase } from '../shared';
import { TypeNode, createTypeNodeFromIdl } from './typeNodes/TypeNode';

export type DefinedTypeNode = {
export interface DefinedTypeNode<TType extends TypeNode = TypeNode> {
readonly kind: 'definedTypeNode';

// Children.
readonly type: TypeNode;
readonly type: TType;

// Data.
readonly name: MainCaseString;
readonly idlName: string;
readonly docs: string[];
};
}

export type DefinedTypeNodeInput = {
export type DefinedTypeNodeInput<TType extends TypeNode = TypeNode> = {
readonly name: string;
readonly type: TypeNode;
readonly type: TType;
readonly idlName?: string;
readonly docs?: string[];
};

export function definedTypeNode(input: DefinedTypeNodeInput): DefinedTypeNode {
export function definedTypeNode<TType extends TypeNode>(
input: DefinedTypeNodeInput<TType>
): DefinedTypeNode<TType> {
if (!input.name) {
throw new InvalidKinobiTreeError('DefinedTypeNode must have a name.');
}
Expand Down
4 changes: 2 additions & 2 deletions src/nodes/ErrorNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
mainCase,
} from '../shared';

export type ErrorNode = {
export interface ErrorNode {
readonly kind: 'errorNode';

// Data.
Expand All @@ -15,7 +15,7 @@ export type ErrorNode = {
readonly code: number;
readonly message: string;
readonly docs: string[];
};
}

export type ErrorNodeInput = Omit<
PartialExcept<ErrorNode, 'name' | 'code' | 'message'>,
Expand Down
29 changes: 21 additions & 8 deletions src/nodes/InstructionAccountNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,43 @@ import { IdlInstructionAccount } from '../idl';
import { MainCaseString, PartialExcept, mainCase } from '../shared';
import { InstructionInputValueNode } from './contextualValueNodes';

export type InstructionAccountNode = {
export interface InstructionAccountNode<
TDefaultValue extends InstructionInputValueNode | undefined =
| InstructionInputValueNode
| undefined,
> {
readonly kind: 'instructionAccountNode';

// Children.
readonly defaultValue?: InstructionInputValueNode;
readonly defaultValue?: TDefaultValue;

// Data.
readonly name: MainCaseString;
readonly isWritable: boolean;
readonly isSigner: boolean | 'either';
readonly isOptional: boolean;
readonly docs: string[];
};
}

export type InstructionAccountNodeInput = Omit<
PartialExcept<InstructionAccountNode, 'isWritable' | 'isSigner'>,
export type InstructionAccountNodeInput<
TDefaultValue extends InstructionInputValueNode | undefined =
| InstructionInputValueNode
| undefined,
> = Omit<
PartialExcept<
InstructionAccountNode<TDefaultValue>,
'isWritable' | 'isSigner'
>,
'kind' | 'name'
> & {
readonly name: string;
};

export function instructionAccountNode(
input: InstructionAccountNodeInput
): InstructionAccountNode {
export function instructionAccountNode<
TDefaultValue extends InstructionInputValueNode | undefined = undefined,
>(
input: InstructionAccountNodeInput<TDefaultValue>
): InstructionAccountNode<TDefaultValue> {
return {
kind: 'instructionAccountNode',
name: mainCase(input.name),
Expand Down
26 changes: 18 additions & 8 deletions src/nodes/InstructionArgumentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,40 @@ import {
} from './typeNodes';
import { VALUE_NODES } from './valueNodes';

export type InstructionArgumentNode = {
export interface InstructionArgumentNode<
TDefaultValue extends InstructionInputValueNode | undefined =
| InstructionInputValueNode
| undefined,
> {
readonly kind: 'instructionArgumentNode';

// Children.
readonly type: TypeNode;
readonly defaultValue?: InstructionInputValueNode;
readonly defaultValue?: TDefaultValue;

// Data.
readonly name: MainCaseString;
readonly docs: string[];
readonly defaultValueStrategy?: 'optional' | 'omitted';
};
}

export type InstructionArgumentNodeInput = {
export type InstructionArgumentNodeInput<
TDefaultValue extends InstructionInputValueNode | undefined =
| InstructionInputValueNode
| undefined,
> = {
readonly name: string;
readonly type: TypeNode;
readonly docs?: string[];
readonly defaultValue?: InstructionInputValueNode;
readonly defaultValue?: TDefaultValue;
readonly defaultValueStrategy?: 'optional' | 'omitted';
};

export function instructionArgumentNode(
input: InstructionArgumentNodeInput
): InstructionArgumentNode {
export function instructionArgumentNode<
TDefaultValue extends InstructionInputValueNode | undefined = undefined,
>(
input: InstructionArgumentNodeInput<TDefaultValue>
): InstructionArgumentNode<TDefaultValue> {
if (!input.name) {
throw new InvalidKinobiTreeError(
'InstructionArgumentNode must have a name.'
Expand Down
26 changes: 16 additions & 10 deletions src/nodes/InstructionByteDeltaNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,34 @@ import { ArgumentValueNode, ResolverValueNode } from './contextualValueNodes';
import { AccountLinkNode } from './linkNodes';
import { NumberValueNode } from './valueNodes';

export type InstructionByteDeltaNode = {
type InstructionByteDeltaNodeValue =
| NumberValueNode
| AccountLinkNode
| ArgumentValueNode
| ResolverValueNode;

export interface InstructionByteDeltaNode<
TValue extends InstructionByteDeltaNodeValue = InstructionByteDeltaNodeValue,
> {
readonly kind: 'instructionByteDeltaNode';

// Children.
readonly value:
| NumberValueNode
| AccountLinkNode
| ArgumentValueNode
| ResolverValueNode;
readonly value: TValue;

// Data.
readonly withHeader: boolean;
readonly subtract?: boolean;
};
}

export function instructionByteDeltaNode(
value: InstructionByteDeltaNode['value'],
export function instructionByteDeltaNode<
TValue extends InstructionByteDeltaNodeValue,
>(
value: TValue,
options: {
withHeader?: boolean;
subtract?: boolean;
} = {}
): InstructionByteDeltaNode {
): InstructionByteDeltaNode<TValue> {
return {
kind: 'instructionByteDeltaNode',
value,
Expand Down
Loading
Loading