import { fCallArguments } from './commander'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunction, RedisScript } from './commands'; import { WatchError } from './errors'; export interface RedisMultiQueuedCommand { args: RedisCommandArguments; transformReply?: RedisCommand['transformReply']; } export default class RedisMultiCommand { static generateChainId(): symbol { return Symbol('RedisMultiCommand Chain Id'); } readonly queue: Array = []; readonly scriptsInUse = new Set(); addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): void { this.queue.push({ args, transformReply }); } addFunction(name: string, fn: RedisFunction, args: Array): RedisCommandArguments { const transformedArguments = fCallArguments( name, fn, fn.transformArguments(...args) ); this.queue.push({ args: transformedArguments, transformReply: fn.transformReply }); return transformedArguments; } addScript(script: RedisScript, args: Array): RedisCommandArguments { const transformedArguments: RedisCommandArguments = []; if (this.scriptsInUse.has(script.SHA1)) { transformedArguments.push( 'EVALSHA', script.SHA1 ); } else { this.scriptsInUse.add(script.SHA1); transformedArguments.push( 'EVAL', script.SCRIPT ); } if (script.NUMBER_OF_KEYS !== undefined) { transformedArguments.push(script.NUMBER_OF_KEYS.toString()); } const scriptArguments = script.transformArguments(...args); transformedArguments.push(...scriptArguments); if (scriptArguments.preserve) { transformedArguments.preserve = scriptArguments.preserve; } this.addCommand( transformedArguments, script.transformReply ); return transformedArguments; } exec(): undefined | Array { if (!this.queue.length) { return; } return [ { args: ['MULTI'] }, ...this.queue, { args: ['EXEC'] } ]; } handleExecReplies(rawReplies: Array): Array { const execReply = rawReplies[rawReplies.length - 1] as (null | Array); if (execReply === null) { throw new WatchError(); } return this.transformReplies(execReply); } transformReplies(rawReplies: Array): Array { return rawReplies.map((reply, i) => { const { transformReply, args } = this.queue[i]; return transformReply ? transformReply(reply, args.preserve) : reply; }); } }