import { BufReader, concat, writeAll } from "./deps.ts";
export type Command = (string | number)[];export type Reply = string | number | null | Reply[];
const CRLF = "\r\n";const encoder = new TextEncoder();const decoder = new TextDecoder();
const SIMPLE_STRING_PREFIX = "+";const ERROR_PREFIX = "-";const INTEGER_PREFIX = ":";const BULK_STRING_PREFIX = "$";const ARRAY_PREFIX = "*";
function removePrefix(line: string): string { return line.slice(1);}
function createRequest(command: Command): Uint8Array { let request = ARRAY_PREFIX + command.length + CRLF; for (const arg of command) { request += BULK_STRING_PREFIX + arg.toString().length + CRLF; request += arg + CRLF; } return encoder.encode(request);}
export async function writeCommand( redisConn: Deno.Conn, command: Command,): Promise<void> { await writeAll(redisConn, createRequest(command));}
function readSimpleString(line: string): string { return removePrefix(line);}
async function readError(line: string): Promise<never> { return await Promise.reject(removePrefix(line));}
function readInteger(line: string): number { return Number(removePrefix(line));}
async function readBulkString( line: string, bufReader: BufReader,): Promise<null | string> { return readInteger(line) === -1 ? null : await readReply(bufReader) as string;}
async function readRepliesN( length: number, bufReader: BufReader,): Promise<Reply[]> { const array: Reply[] = []; for (let i = 0; i < length; i++) { array.push(await readReply(bufReader)); } return array;}
async function readArray( line: string, bufReader: BufReader,): Promise<null | Reply[]> { const length = readInteger(line); return length === -1 ? null : await readRepliesN(length, bufReader);}
async function readReply(bufReader: BufReader): Promise<Reply> { const result = await bufReader.readLine(); if (!result) { return await Promise.reject("No reply received from Redis server"); } const line = decoder.decode(result.line); switch (line.charAt(0)) { case SIMPLE_STRING_PREFIX: return readSimpleString(line); case ERROR_PREFIX: return readError(line); case INTEGER_PREFIX: return readInteger(line); case BULK_STRING_PREFIX: return await readBulkString(line, bufReader); case ARRAY_PREFIX: return await readArray(line, bufReader); default: return line; }}
export async function sendCommand( redisConn: Deno.Conn, command: Command,): Promise<Reply> { await writeCommand(redisConn, command); return await readReply(new BufReader(redisConn));}
export async function pipelineCommands( redisConn: Deno.Conn, commands: Command[],): Promise<Reply[]> { const request = concat(...commands.map(createRequest)); await writeAll(redisConn, request); return readRepliesN(commands.length, new BufReader(redisConn));}
export async function* listenReplies( redisConn: Deno.Conn,): AsyncIterableIterator<Reply> { const bufReader = new BufReader(redisConn); while (true) { yield await readReply(bufReader); }}