import { ansi } from "cliffy/ansi/mod.ts";
import { writeAllSync } from "io/write-all";
import { readAllSync } from "io/read-all";
import { info } from "../deno_ral/log.ts";
import { runningInCI } from "./ci-info.ts";
import { SpinnerOptions } from "./console-types.ts";
import { isWindows } from "../deno_ral/platform.ts";
const kSpinnerChars = ["|", "/", "-", "\\"];
const kSpinerContainerChars = ["(", ")"];
const kSpinerCompleteContainerChars = ["[", "]"];
const kSpinnerCompleteChar = !isWindows ? "✓" : ">";
const kProgressIncrementChar = "#";
const kProgressContainerChars = ["[", "]"];
const kProgressBarWidth = 35;
export function progressBar(total: number, prefixMessage?: string): {
update: (progress: number, status?: string) => void;
complete: (finalMsg?: string | boolean) => void;
} {
const isCi = runningInCI();
if (isCi && prefixMessage) {
info(prefixMessage);
}
const updateProgress = (progress: number, status?: string) => {
if (!isCi) {
const progressBar = `${
asciiProgressBar((progress / total) * 100, kProgressBarWidth)
}`;
const progressText = `\r${
prefixMessage ? prefixMessage + " " : ""
}${progressBar}${status ? " " + status : ""}`;
clearLine();
info(progressText, { newline: false });
}
};
return {
update: updateProgress,
complete: (finalMsg?: string | boolean) => {
if (!isCi) {
clearLine();
}
if (typeof finalMsg === "string") {
if (isCi) {
info(finalMsg);
} else {
updateProgress(total, finalMsg);
}
} else {
if (finalMsg !== false && prefixMessage) {
completeMessage(prefixMessage);
} else if (finalMsg !== false) {
updateProgress(total);
}
}
},
};
}
export async function withSpinner<T>(
options: SpinnerOptions,
op: () => Promise<T>,
) {
const cancel = spinner(options.message);
try {
return await op();
} finally {
cancel(options.doneMessage);
}
}
export function spinner(
status: string | (() => string),
timeInterval = 100,
): (finalMsg?: string | boolean) => void {
let spin = 0;
const statusFn = typeof status === "string"
? () => {
return status;
}
: () => {
if (!runningInCI()) {
clearLine();
}
return status();
};
const id = setInterval(() => {
const char = kSpinnerChars[spin % kSpinnerChars.length];
const msg = `${spinContainer(char)} ${statusFn()}`;
if (!runningInCI() || spin === 0) {
info(`\r${msg}`, {
newline: false,
});
}
spin = spin + 1;
}, timeInterval);
return (finalMsg?: string | boolean) => {
clearInterval(id);
clearLine();
if (typeof finalMsg === "string") {
completeMessage(finalMsg);
} else {
if (finalMsg !== false) {
completeMessage(statusFn());
}
}
};
}
function spinContainer(body: string) {
return `${kSpinerContainerChars[0]}${body}${kSpinerContainerChars[1]}`;
}
export function completeMessage(msg: string) {
info(
`\r${kSpinerCompleteContainerChars[0]}${kSpinnerCompleteChar}${
kSpinerCompleteContainerChars[1]
} ${msg}`,
{
newline: true,
},
);
}
export function formatLine(values: string[], lengths: number[]) {
const line: string[] = [];
values.forEach((value, i) => {
const len = lengths[i];
if (value.length === len) {
line.push(value);
} else if (value.length > len) {
line.push(value.substr(0, len));
} else {
line.push(value.padEnd(len, " "));
}
});
return line.join("");
}
export function writeFileToStdout(file: string) {
const df = Deno.openSync(file, { read: true });
const contents = readAllSync(df);
writeAllSync(Deno.stdout, contents);
df.close();
}
export function clearLine() {
info(ansi.eraseLine.cursorLeft(), { newline: false });
}
function asciiProgressBar(percent: number, width = 25): string {
const segsComplete = Math.floor(percent / (100 / width));
let progressBar = kProgressContainerChars[0];
for (let i = 0; i < width; i++) {
progressBar = progressBar +
(i < segsComplete ? kProgressIncrementChar : " ");
}
progressBar = progressBar + kProgressContainerChars[1];
return progressBar;
}