|
|
@@ -2,6 +2,8 @@ import { KINOBI_ERROR__LINKED_NODE_NOT_FOUND, KinobiError } from '@kinobi-so/err
|
|
|
import {
|
|
|
AccountLinkNode,
|
|
|
AccountNode,
|
|
|
+ camelCase,
|
|
|
+ CamelCaseString,
|
|
|
DefinedTypeLinkNode,
|
|
|
DefinedTypeNode,
|
|
|
isNode,
|
|
|
@@ -12,52 +14,83 @@ import {
|
|
|
ProgramNode,
|
|
|
} from '@kinobi-so/nodes';
|
|
|
|
|
|
+import { NodeStack } from './NodeStack';
|
|
|
+
|
|
|
export type LinkableNode = AccountNode | DefinedTypeNode | PdaNode | ProgramNode;
|
|
|
|
|
|
export const LINKABLE_NODES: LinkableNode['kind'][] = ['accountNode', 'definedTypeNode', 'pdaNode', 'programNode'];
|
|
|
|
|
|
-export class LinkableDictionary {
|
|
|
- private readonly programs: Map<string, ProgramNode> = new Map();
|
|
|
+type ProgramDictionary = {
|
|
|
+ accounts: Map<string, AccountNode>;
|
|
|
+ definedTypes: Map<string, DefinedTypeNode>;
|
|
|
+ pdas: Map<string, PdaNode>;
|
|
|
+ program: ProgramNode;
|
|
|
+};
|
|
|
|
|
|
- private readonly pdas: Map<string, PdaNode> = new Map();
|
|
|
+type ProgramInput = ProgramLinkNode | ProgramNode | string;
|
|
|
|
|
|
- private readonly accounts: Map<string, AccountNode> = new Map();
|
|
|
+function getProgramName(program: ProgramInput): CamelCaseString;
|
|
|
+function getProgramName(program: ProgramInput | undefined): CamelCaseString | undefined;
|
|
|
+function getProgramName(program: ProgramInput | undefined): CamelCaseString | undefined {
|
|
|
+ if (!program) return undefined;
|
|
|
+ return typeof program === 'string' ? camelCase(program) : program.name;
|
|
|
+}
|
|
|
|
|
|
- private readonly definedTypes: Map<string, DefinedTypeNode> = new Map();
|
|
|
+export class LinkableDictionary {
|
|
|
+ readonly programs: Map<string, ProgramDictionary> = new Map();
|
|
|
+
|
|
|
+ readonly stack: NodeStack = new NodeStack();
|
|
|
+
|
|
|
+ private getOrCreateProgramDictionary(node: ProgramNode): ProgramDictionary {
|
|
|
+ let programDictionary = this.programs.get(node.name);
|
|
|
+ if (!programDictionary) {
|
|
|
+ programDictionary = {
|
|
|
+ accounts: new Map(),
|
|
|
+ definedTypes: new Map(),
|
|
|
+ pdas: new Map(),
|
|
|
+ program: node,
|
|
|
+ };
|
|
|
+ this.programs.set(node.name, programDictionary);
|
|
|
+ }
|
|
|
+ return programDictionary;
|
|
|
+ }
|
|
|
|
|
|
record(node: LinkableNode): this {
|
|
|
if (isNode(node, 'programNode')) {
|
|
|
- this.programs.set(node.name, node);
|
|
|
+ this.getOrCreateProgramDictionary(node);
|
|
|
+ return this;
|
|
|
}
|
|
|
+
|
|
|
+ // Do not record nodes that are outside of a program.
|
|
|
+ const program = this.stack.getProgram();
|
|
|
+ if (!program) return this;
|
|
|
+
|
|
|
+ const programDictionary = this.getOrCreateProgramDictionary(program);
|
|
|
if (isNode(node, 'pdaNode')) {
|
|
|
- this.pdas.set(node.name, node);
|
|
|
- }
|
|
|
- if (isNode(node, 'accountNode')) {
|
|
|
- this.accounts.set(node.name, node);
|
|
|
- }
|
|
|
- if (isNode(node, 'definedTypeNode')) {
|
|
|
- this.definedTypes.set(node.name, node);
|
|
|
+ programDictionary.pdas.set(node.name, node);
|
|
|
+ } else if (isNode(node, 'accountNode')) {
|
|
|
+ programDictionary.accounts.set(node.name, node);
|
|
|
+ } else if (isNode(node, 'definedTypeNode')) {
|
|
|
+ programDictionary.definedTypes.set(node.name, node);
|
|
|
}
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- recordAll(nodes: LinkableNode[]): this {
|
|
|
- nodes.forEach(node => this.record(node));
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
getOrThrow(linkNode: ProgramLinkNode): ProgramNode;
|
|
|
getOrThrow(linkNode: PdaLinkNode): PdaNode;
|
|
|
getOrThrow(linkNode: AccountLinkNode): AccountNode;
|
|
|
getOrThrow(linkNode: DefinedTypeLinkNode): DefinedTypeNode;
|
|
|
getOrThrow(linkNode: LinkNode): LinkableNode {
|
|
|
- const node = this.get(linkNode as ProgramLinkNode) as LinkableNode;
|
|
|
+ const node = this.get(linkNode as ProgramLinkNode) as LinkableNode | undefined;
|
|
|
|
|
|
if (!node) {
|
|
|
throw new KinobiError(KINOBI_ERROR__LINKED_NODE_NOT_FOUND, {
|
|
|
kind: linkNode.kind,
|
|
|
linkNode,
|
|
|
name: linkNode.name,
|
|
|
+ program: isNode(linkNode, 'pdaLinkNode')
|
|
|
+ ? getProgramName(linkNode.program ?? this.stack.getProgram())
|
|
|
+ : undefined,
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -70,17 +103,23 @@ export class LinkableDictionary {
|
|
|
get(linkNode: DefinedTypeLinkNode): DefinedTypeNode | undefined;
|
|
|
get(linkNode: LinkNode): LinkableNode | undefined {
|
|
|
if (isNode(linkNode, 'programLinkNode')) {
|
|
|
- return this.programs.get(linkNode.name);
|
|
|
+ return this.programs.get(linkNode.name)?.program;
|
|
|
}
|
|
|
+
|
|
|
+ const programName = getProgramName(linkNode.program ?? this.stack.getProgram());
|
|
|
+ if (!programName) return undefined;
|
|
|
+
|
|
|
+ const programDictionary = this.programs.get(programName);
|
|
|
+ if (!programDictionary) return undefined;
|
|
|
+
|
|
|
if (isNode(linkNode, 'pdaLinkNode')) {
|
|
|
- return this.pdas.get(linkNode.name);
|
|
|
- }
|
|
|
- if (isNode(linkNode, 'accountLinkNode')) {
|
|
|
- return this.accounts.get(linkNode.name);
|
|
|
- }
|
|
|
- if (isNode(linkNode, 'definedTypeLinkNode')) {
|
|
|
- return this.definedTypes.get(linkNode.name);
|
|
|
+ return programDictionary.pdas.get(linkNode.name);
|
|
|
+ } else if (isNode(linkNode, 'accountLinkNode')) {
|
|
|
+ return programDictionary.accounts.get(linkNode.name);
|
|
|
+ } else if (isNode(linkNode, 'definedTypeLinkNode')) {
|
|
|
+ return programDictionary.definedTypes.get(linkNode.name);
|
|
|
}
|
|
|
+
|
|
|
return undefined;
|
|
|
}
|
|
|
|
|
|
@@ -88,15 +127,21 @@ export class LinkableDictionary {
|
|
|
if (isNode(linkNode, 'programLinkNode')) {
|
|
|
return this.programs.has(linkNode.name);
|
|
|
}
|
|
|
+
|
|
|
+ const programName = getProgramName(linkNode.program ?? this.stack.getProgram());
|
|
|
+ if (!programName) return false;
|
|
|
+
|
|
|
+ const programDictionary = this.programs.get(programName);
|
|
|
+ if (!programDictionary) return false;
|
|
|
+
|
|
|
if (isNode(linkNode, 'pdaLinkNode')) {
|
|
|
- return this.pdas.has(linkNode.name);
|
|
|
- }
|
|
|
- if (isNode(linkNode, 'accountLinkNode')) {
|
|
|
- return this.accounts.has(linkNode.name);
|
|
|
- }
|
|
|
- if (isNode(linkNode, 'definedTypeLinkNode')) {
|
|
|
- return this.definedTypes.has(linkNode.name);
|
|
|
+ return programDictionary.pdas.has(linkNode.name);
|
|
|
+ } else if (isNode(linkNode, 'accountLinkNode')) {
|
|
|
+ return programDictionary.accounts.has(linkNode.name);
|
|
|
+ } else if (isNode(linkNode, 'definedTypeLinkNode')) {
|
|
|
+ return programDictionary.definedTypes.has(linkNode.name);
|
|
|
}
|
|
|
+
|
|
|
return false;
|
|
|
}
|
|
|
}
|