|
@@ -1,25 +1,25 @@
|
|
|
import { CODAMA_ERROR__LINKED_NODE_NOT_FOUND, CodamaError } from '@codama/errors';
|
|
import { CODAMA_ERROR__LINKED_NODE_NOT_FOUND, CodamaError } from '@codama/errors';
|
|
|
import {
|
|
import {
|
|
|
- AccountLinkNode,
|
|
|
|
|
AccountNode,
|
|
AccountNode,
|
|
|
CamelCaseString,
|
|
CamelCaseString,
|
|
|
- DefinedTypeLinkNode,
|
|
|
|
|
DefinedTypeNode,
|
|
DefinedTypeNode,
|
|
|
- InstructionAccountLinkNode,
|
|
|
|
|
InstructionAccountNode,
|
|
InstructionAccountNode,
|
|
|
- InstructionArgumentLinkNode,
|
|
|
|
|
InstructionArgumentNode,
|
|
InstructionArgumentNode,
|
|
|
- InstructionLinkNode,
|
|
|
|
|
InstructionNode,
|
|
InstructionNode,
|
|
|
isNode,
|
|
isNode,
|
|
|
LinkNode,
|
|
LinkNode,
|
|
|
- PdaLinkNode,
|
|
|
|
|
PdaNode,
|
|
PdaNode,
|
|
|
- ProgramLinkNode,
|
|
|
|
|
ProgramNode,
|
|
ProgramNode,
|
|
|
} from '@codama/nodes';
|
|
} from '@codama/nodes';
|
|
|
|
|
|
|
|
-import { NodeStack } from './NodeStack';
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ findInstructionNodeFromPath,
|
|
|
|
|
+ findProgramNodeFromPath,
|
|
|
|
|
+ getLastNodeFromPath,
|
|
|
|
|
+ getNodePathUntilLastNode,
|
|
|
|
|
+ isNodePath,
|
|
|
|
|
+ NodePath,
|
|
|
|
|
+} from './NodePath';
|
|
|
|
|
|
|
|
export type LinkableNode =
|
|
export type LinkableNode =
|
|
|
| AccountNode
|
|
| AccountNode
|
|
@@ -40,100 +40,114 @@ export const LINKABLE_NODES: LinkableNode['kind'][] = [
|
|
|
'programNode',
|
|
'programNode',
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
|
|
+export type GetLinkableFromLinkNode<TLinkNode extends LinkNode> = {
|
|
|
|
|
+ accountLinkNode: AccountNode;
|
|
|
|
|
+ definedTypeLinkNode: DefinedTypeNode;
|
|
|
|
|
+ instructionAccountLinkNode: InstructionAccountNode;
|
|
|
|
|
+ instructionArgumentLinkNode: InstructionArgumentNode;
|
|
|
|
|
+ instructionLinkNode: InstructionNode;
|
|
|
|
|
+ pdaLinkNode: PdaNode;
|
|
|
|
|
+ programLinkNode: ProgramNode;
|
|
|
|
|
+}[TLinkNode['kind']];
|
|
|
|
|
+
|
|
|
type ProgramDictionary = {
|
|
type ProgramDictionary = {
|
|
|
- accounts: Map<string, AccountNode>;
|
|
|
|
|
- definedTypes: Map<string, DefinedTypeNode>;
|
|
|
|
|
|
|
+ accounts: Map<string, NodePath<AccountNode>>;
|
|
|
|
|
+ definedTypes: Map<string, NodePath<DefinedTypeNode>>;
|
|
|
instructions: Map<string, InstructionDictionary>;
|
|
instructions: Map<string, InstructionDictionary>;
|
|
|
- pdas: Map<string, PdaNode>;
|
|
|
|
|
- program: ProgramNode;
|
|
|
|
|
|
|
+ pdas: Map<string, NodePath<PdaNode>>;
|
|
|
|
|
+ program: NodePath<ProgramNode>;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
type InstructionDictionary = {
|
|
type InstructionDictionary = {
|
|
|
- accounts: Map<string, InstructionAccountNode>;
|
|
|
|
|
- arguments: Map<string, InstructionArgumentNode>;
|
|
|
|
|
- instruction: InstructionNode;
|
|
|
|
|
|
|
+ accounts: Map<string, NodePath<InstructionAccountNode>>;
|
|
|
|
|
+ arguments: Map<string, NodePath<InstructionArgumentNode>>;
|
|
|
|
|
+ instruction: NodePath<InstructionNode>;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
export class LinkableDictionary {
|
|
export class LinkableDictionary {
|
|
|
readonly programs: Map<string, ProgramDictionary> = new Map();
|
|
readonly programs: Map<string, ProgramDictionary> = new Map();
|
|
|
|
|
|
|
|
- record(node: LinkableNode, stack: NodeStack): this {
|
|
|
|
|
- const programDictionary = this.getOrCreateProgramDictionary(node, stack);
|
|
|
|
|
|
|
+ recordPath(linkablePath: NodePath<LinkableNode>): this {
|
|
|
|
|
+ const linkableNode = getLastNodeFromPath(linkablePath);
|
|
|
|
|
+ const programDictionary = this.getOrCreateProgramDictionary(linkablePath);
|
|
|
if (!programDictionary) return this; // Do not record nodes that are outside of a program.
|
|
if (!programDictionary) return this; // Do not record nodes that are outside of a program.
|
|
|
- const instructionDictionary = this.getOrCreateInstructionDictionary(programDictionary, node, stack);
|
|
|
|
|
-
|
|
|
|
|
- if (isNode(node, 'accountNode')) {
|
|
|
|
|
- programDictionary.accounts.set(node.name, node);
|
|
|
|
|
- } else if (isNode(node, 'definedTypeNode')) {
|
|
|
|
|
- programDictionary.definedTypes.set(node.name, node);
|
|
|
|
|
- } else if (isNode(node, 'pdaNode')) {
|
|
|
|
|
- programDictionary.pdas.set(node.name, node);
|
|
|
|
|
- } else if (instructionDictionary && isNode(node, 'instructionAccountNode')) {
|
|
|
|
|
- instructionDictionary.accounts.set(node.name, node);
|
|
|
|
|
- } else if (instructionDictionary && isNode(node, 'instructionArgumentNode')) {
|
|
|
|
|
- instructionDictionary.arguments.set(node.name, node);
|
|
|
|
|
|
|
+ const instructionDictionary = this.getOrCreateInstructionDictionary(programDictionary, linkablePath);
|
|
|
|
|
+
|
|
|
|
|
+ if (isNodePath(linkablePath, 'accountNode')) {
|
|
|
|
|
+ programDictionary.accounts.set(linkableNode.name, linkablePath);
|
|
|
|
|
+ } else if (isNodePath(linkablePath, 'definedTypeNode')) {
|
|
|
|
|
+ programDictionary.definedTypes.set(linkableNode.name, linkablePath);
|
|
|
|
|
+ } else if (isNodePath(linkablePath, 'pdaNode')) {
|
|
|
|
|
+ programDictionary.pdas.set(linkableNode.name, linkablePath);
|
|
|
|
|
+ } else if (instructionDictionary && isNodePath(linkablePath, 'instructionAccountNode')) {
|
|
|
|
|
+ instructionDictionary.accounts.set(linkableNode.name, linkablePath);
|
|
|
|
|
+ } else if (instructionDictionary && isNodePath(linkablePath, 'instructionArgumentNode')) {
|
|
|
|
|
+ instructionDictionary.arguments.set(linkableNode.name, linkablePath);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- getOrThrow(linkNode: AccountLinkNode, stack: NodeStack): AccountNode;
|
|
|
|
|
- getOrThrow(linkNode: DefinedTypeLinkNode, stack: NodeStack): DefinedTypeNode;
|
|
|
|
|
- getOrThrow(linkNode: InstructionAccountLinkNode, stack: NodeStack): InstructionAccountNode;
|
|
|
|
|
- getOrThrow(linkNode: InstructionArgumentLinkNode, stack: NodeStack): InstructionArgumentNode;
|
|
|
|
|
- getOrThrow(linkNode: InstructionLinkNode, stack: NodeStack): InstructionNode;
|
|
|
|
|
- getOrThrow(linkNode: PdaLinkNode, stack: NodeStack): PdaNode;
|
|
|
|
|
- getOrThrow(linkNode: ProgramLinkNode, stack: NodeStack): ProgramNode;
|
|
|
|
|
- getOrThrow(linkNode: LinkNode, stack: NodeStack): LinkableNode {
|
|
|
|
|
- const node = this.get(linkNode as ProgramLinkNode, stack) as LinkableNode | undefined;
|
|
|
|
|
-
|
|
|
|
|
- if (!node) {
|
|
|
|
|
|
|
+ getPathOrThrow<TLinkNode extends LinkNode>(
|
|
|
|
|
+ linkPath: NodePath<TLinkNode>,
|
|
|
|
|
+ ): NodePath<GetLinkableFromLinkNode<TLinkNode>> {
|
|
|
|
|
+ const linkablePath = this.getPath(linkPath);
|
|
|
|
|
+
|
|
|
|
|
+ if (!linkablePath) {
|
|
|
|
|
+ const linkNode = getLastNodeFromPath(linkPath);
|
|
|
throw new CodamaError(CODAMA_ERROR__LINKED_NODE_NOT_FOUND, {
|
|
throw new CodamaError(CODAMA_ERROR__LINKED_NODE_NOT_FOUND, {
|
|
|
kind: linkNode.kind,
|
|
kind: linkNode.kind,
|
|
|
linkNode,
|
|
linkNode,
|
|
|
name: linkNode.name,
|
|
name: linkNode.name,
|
|
|
- stack: stack.all(),
|
|
|
|
|
|
|
+ path: linkablePath,
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return node;
|
|
|
|
|
|
|
+ return linkablePath;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- get(linkNode: AccountLinkNode, stack: NodeStack): AccountNode | undefined;
|
|
|
|
|
- get(linkNode: DefinedTypeLinkNode, stack: NodeStack): DefinedTypeNode | undefined;
|
|
|
|
|
- get(linkNode: InstructionAccountLinkNode, stack: NodeStack): InstructionAccountNode | undefined;
|
|
|
|
|
- get(linkNode: InstructionArgumentLinkNode, stack: NodeStack): InstructionArgumentNode | undefined;
|
|
|
|
|
- get(linkNode: InstructionLinkNode, stack: NodeStack): InstructionNode | undefined;
|
|
|
|
|
- get(linkNode: PdaLinkNode, stack: NodeStack): PdaNode | undefined;
|
|
|
|
|
- get(linkNode: ProgramLinkNode, stack: NodeStack): ProgramNode | undefined;
|
|
|
|
|
- get(linkNode: LinkNode, stack: NodeStack): LinkableNode | undefined {
|
|
|
|
|
- const programDictionary = this.getProgramDictionary(linkNode, stack);
|
|
|
|
|
|
|
+ getPath<TLinkNode extends LinkNode>(
|
|
|
|
|
+ linkPath: NodePath<TLinkNode>,
|
|
|
|
|
+ ): NodePath<GetLinkableFromLinkNode<TLinkNode>> | undefined {
|
|
|
|
|
+ const linkNode = getLastNodeFromPath(linkPath);
|
|
|
|
|
+ const programDictionary = this.getProgramDictionary(linkPath);
|
|
|
if (!programDictionary) return undefined;
|
|
if (!programDictionary) return undefined;
|
|
|
- const instructionDictionary = this.getInstructionDictionary(programDictionary, linkNode, stack);
|
|
|
|
|
|
|
+ const instructionDictionary = this.getInstructionDictionary(programDictionary, linkPath);
|
|
|
|
|
+ type LinkablePath = NodePath<GetLinkableFromLinkNode<TLinkNode>> | undefined;
|
|
|
|
|
|
|
|
if (isNode(linkNode, 'accountLinkNode')) {
|
|
if (isNode(linkNode, 'accountLinkNode')) {
|
|
|
- return programDictionary.accounts.get(linkNode.name);
|
|
|
|
|
|
|
+ return programDictionary.accounts.get(linkNode.name) as LinkablePath;
|
|
|
} else if (isNode(linkNode, 'definedTypeLinkNode')) {
|
|
} else if (isNode(linkNode, 'definedTypeLinkNode')) {
|
|
|
- return programDictionary.definedTypes.get(linkNode.name);
|
|
|
|
|
|
|
+ return programDictionary.definedTypes.get(linkNode.name) as LinkablePath;
|
|
|
} else if (isNode(linkNode, 'instructionAccountLinkNode')) {
|
|
} else if (isNode(linkNode, 'instructionAccountLinkNode')) {
|
|
|
- return instructionDictionary?.accounts.get(linkNode.name);
|
|
|
|
|
|
|
+ return instructionDictionary?.accounts.get(linkNode.name) as LinkablePath;
|
|
|
} else if (isNode(linkNode, 'instructionArgumentLinkNode')) {
|
|
} else if (isNode(linkNode, 'instructionArgumentLinkNode')) {
|
|
|
- return instructionDictionary?.arguments.get(linkNode.name);
|
|
|
|
|
|
|
+ return instructionDictionary?.arguments.get(linkNode.name) as LinkablePath;
|
|
|
} else if (isNode(linkNode, 'instructionLinkNode')) {
|
|
} else if (isNode(linkNode, 'instructionLinkNode')) {
|
|
|
- return instructionDictionary?.instruction;
|
|
|
|
|
|
|
+ return instructionDictionary?.instruction as LinkablePath;
|
|
|
} else if (isNode(linkNode, 'pdaLinkNode')) {
|
|
} else if (isNode(linkNode, 'pdaLinkNode')) {
|
|
|
- return programDictionary.pdas.get(linkNode.name);
|
|
|
|
|
|
|
+ return programDictionary.pdas.get(linkNode.name) as LinkablePath;
|
|
|
} else if (isNode(linkNode, 'programLinkNode')) {
|
|
} else if (isNode(linkNode, 'programLinkNode')) {
|
|
|
- return programDictionary.program;
|
|
|
|
|
|
|
+ return programDictionary.program as LinkablePath;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
return undefined;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- has(linkNode: LinkNode, stack: NodeStack): boolean {
|
|
|
|
|
- const programDictionary = this.getProgramDictionary(linkNode, stack);
|
|
|
|
|
|
|
+ getOrThrow<TLinkNode extends LinkNode>(linkPath: NodePath<TLinkNode>): GetLinkableFromLinkNode<TLinkNode> {
|
|
|
|
|
+ return getLastNodeFromPath(this.getPathOrThrow(linkPath));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ get<TLinkNode extends LinkNode>(linkPath: NodePath<TLinkNode>): GetLinkableFromLinkNode<TLinkNode> | undefined {
|
|
|
|
|
+ const path = this.getPath(linkPath);
|
|
|
|
|
+ return path ? getLastNodeFromPath(path) : undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ has(linkPath: NodePath<LinkNode>): boolean {
|
|
|
|
|
+ const linkNode = getLastNodeFromPath(linkPath);
|
|
|
|
|
+ const programDictionary = this.getProgramDictionary(linkPath);
|
|
|
if (!programDictionary) return false;
|
|
if (!programDictionary) return false;
|
|
|
- const instructionDictionary = this.getInstructionDictionary(programDictionary, linkNode, stack);
|
|
|
|
|
|
|
+ const instructionDictionary = this.getInstructionDictionary(programDictionary, linkPath);
|
|
|
|
|
|
|
|
if (isNode(linkNode, 'accountLinkNode')) {
|
|
if (isNode(linkNode, 'accountLinkNode')) {
|
|
|
return programDictionary.accounts.has(linkNode.name);
|
|
return programDictionary.accounts.has(linkNode.name);
|
|
@@ -154,8 +168,9 @@ export class LinkableDictionary {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private getOrCreateProgramDictionary(node: LinkableNode, stack: NodeStack): ProgramDictionary | undefined {
|
|
|
|
|
- const programNode = isNode(node, 'programNode') ? node : stack.getProgram();
|
|
|
|
|
|
|
+ private getOrCreateProgramDictionary(linkablePath: NodePath<LinkableNode>): ProgramDictionary | undefined {
|
|
|
|
|
+ const linkableNode = getLastNodeFromPath(linkablePath);
|
|
|
|
|
+ const programNode = isNode(linkableNode, 'programNode') ? linkableNode : findProgramNodeFromPath(linkablePath);
|
|
|
if (!programNode) return undefined;
|
|
if (!programNode) return undefined;
|
|
|
|
|
|
|
|
let programDictionary = this.programs.get(programNode.name);
|
|
let programDictionary = this.programs.get(programNode.name);
|
|
@@ -165,7 +180,7 @@ export class LinkableDictionary {
|
|
|
definedTypes: new Map(),
|
|
definedTypes: new Map(),
|
|
|
instructions: new Map(),
|
|
instructions: new Map(),
|
|
|
pdas: new Map(),
|
|
pdas: new Map(),
|
|
|
- program: programNode,
|
|
|
|
|
|
|
+ program: getNodePathUntilLastNode(linkablePath, 'programNode')!,
|
|
|
};
|
|
};
|
|
|
this.programs.set(programNode.name, programDictionary);
|
|
this.programs.set(programNode.name, programDictionary);
|
|
|
}
|
|
}
|
|
@@ -175,10 +190,12 @@ export class LinkableDictionary {
|
|
|
|
|
|
|
|
private getOrCreateInstructionDictionary(
|
|
private getOrCreateInstructionDictionary(
|
|
|
programDictionary: ProgramDictionary,
|
|
programDictionary: ProgramDictionary,
|
|
|
- node: LinkableNode,
|
|
|
|
|
- stack: NodeStack,
|
|
|
|
|
|
|
+ linkablePath: NodePath<LinkableNode>,
|
|
|
): InstructionDictionary | undefined {
|
|
): InstructionDictionary | undefined {
|
|
|
- const instructionNode = isNode(node, 'instructionNode') ? node : stack.getInstruction();
|
|
|
|
|
|
|
+ const linkableNode = getLastNodeFromPath(linkablePath);
|
|
|
|
|
+ const instructionNode = isNode(linkableNode, 'instructionNode')
|
|
|
|
|
+ ? linkableNode
|
|
|
|
|
+ : findInstructionNodeFromPath(linkablePath);
|
|
|
if (!instructionNode) return undefined;
|
|
if (!instructionNode) return undefined;
|
|
|
|
|
|
|
|
let instructionDictionary = programDictionary.instructions.get(instructionNode.name);
|
|
let instructionDictionary = programDictionary.instructions.get(instructionNode.name);
|
|
@@ -186,7 +203,7 @@ export class LinkableDictionary {
|
|
|
instructionDictionary = {
|
|
instructionDictionary = {
|
|
|
accounts: new Map(),
|
|
accounts: new Map(),
|
|
|
arguments: new Map(),
|
|
arguments: new Map(),
|
|
|
- instruction: instructionNode,
|
|
|
|
|
|
|
+ instruction: getNodePathUntilLastNode(linkablePath, 'instructionNode')!,
|
|
|
};
|
|
};
|
|
|
programDictionary.instructions.set(instructionNode.name, instructionDictionary);
|
|
programDictionary.instructions.set(instructionNode.name, instructionDictionary);
|
|
|
}
|
|
}
|
|
@@ -194,7 +211,8 @@ export class LinkableDictionary {
|
|
|
return instructionDictionary;
|
|
return instructionDictionary;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private getProgramDictionary(linkNode: LinkNode, stack: NodeStack): ProgramDictionary | undefined {
|
|
|
|
|
|
|
+ private getProgramDictionary(linkPath: NodePath<LinkNode>): ProgramDictionary | undefined {
|
|
|
|
|
+ const linkNode = getLastNodeFromPath(linkPath);
|
|
|
let programName: CamelCaseString | undefined = undefined;
|
|
let programName: CamelCaseString | undefined = undefined;
|
|
|
if (isNode(linkNode, 'programLinkNode')) {
|
|
if (isNode(linkNode, 'programLinkNode')) {
|
|
|
programName = linkNode.name;
|
|
programName = linkNode.name;
|
|
@@ -203,23 +221,23 @@ export class LinkableDictionary {
|
|
|
} else if ('instruction' in linkNode) {
|
|
} else if ('instruction' in linkNode) {
|
|
|
programName = linkNode.instruction?.program?.name;
|
|
programName = linkNode.instruction?.program?.name;
|
|
|
}
|
|
}
|
|
|
- programName = programName ?? stack.getProgram()?.name;
|
|
|
|
|
|
|
+ programName = programName ?? findProgramNodeFromPath(linkPath)?.name;
|
|
|
|
|
|
|
|
return programName ? this.programs.get(programName) : undefined;
|
|
return programName ? this.programs.get(programName) : undefined;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private getInstructionDictionary(
|
|
private getInstructionDictionary(
|
|
|
programDictionary: ProgramDictionary,
|
|
programDictionary: ProgramDictionary,
|
|
|
- linkNode: LinkNode,
|
|
|
|
|
- stack: NodeStack,
|
|
|
|
|
|
|
+ linkPath: NodePath<LinkNode>,
|
|
|
): InstructionDictionary | undefined {
|
|
): InstructionDictionary | undefined {
|
|
|
|
|
+ const linkNode = getLastNodeFromPath(linkPath);
|
|
|
let instructionName: CamelCaseString | undefined = undefined;
|
|
let instructionName: CamelCaseString | undefined = undefined;
|
|
|
if (isNode(linkNode, 'instructionLinkNode')) {
|
|
if (isNode(linkNode, 'instructionLinkNode')) {
|
|
|
instructionName = linkNode.name;
|
|
instructionName = linkNode.name;
|
|
|
} else if ('instruction' in linkNode) {
|
|
} else if ('instruction' in linkNode) {
|
|
|
instructionName = linkNode.instruction?.name;
|
|
instructionName = linkNode.instruction?.name;
|
|
|
}
|
|
}
|
|
|
- instructionName = instructionName ?? stack.getInstruction()?.name;
|
|
|
|
|
|
|
+ instructionName = instructionName ?? findInstructionNodeFromPath(linkPath)?.name;
|
|
|
|
|
|
|
|
return instructionName ? programDictionary.instructions.get(instructionName) : undefined;
|
|
return instructionName ? programDictionary.instructions.get(instructionName) : undefined;
|
|
|
}
|
|
}
|