Combine toPromise.ts and some functions into new utils.ts

pull/285/head
Kelvin Schoofs 3 years ago
parent 1258a8e97d
commit f86e33a7de

@ -4,7 +4,7 @@ import { parse as parseJsonc, ParseError } from 'jsonc-parser';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { ConfigLocation, FileSystemConfig, invalidConfigName, parseConnectionString } from './fileSystemConfig'; import { ConfigLocation, FileSystemConfig, invalidConfigName, parseConnectionString } from './fileSystemConfig';
import { Logging } from './logging'; import { Logging } from './logging';
import { toPromise } from './toPromise'; import { toPromise } from './utils';
// Logger scope with default warning/error options (which enables stacktraces) disabled // Logger scope with default warning/error options (which enables stacktraces) disabled
const logging = Logging.scope(undefined, false); const logging = Logging.scope(undefined, false);

@ -8,7 +8,7 @@ import { getConfig, getFlagBoolean } from './config';
import type { FileSystemConfig } from './fileSystemConfig'; import type { FileSystemConfig } from './fileSystemConfig';
import { censorConfig, Logging } from './logging'; import { censorConfig, Logging } from './logging';
import type { PuttySession } from './putty'; import type { PuttySession } from './putty';
import { toPromise } from './toPromise'; import { toPromise } from './utils';
// tslint:disable-next-line:variable-name // tslint:disable-next-line:variable-name
const SFTPWrapper = require('ssh2/lib/SFTPWrapper') as (new (stream: SFTPStream) => SFTPWrapperReal); const SFTPWrapper = require('ssh2/lib/SFTPWrapper') as (new (stream: SFTPStream) => SFTPWrapperReal);

@ -7,7 +7,7 @@ import type { EnvironmentVariable, FileSystemConfig } from './fileSystemConfig';
import { Logging } from './logging'; import { Logging } from './logging';
import type { SSHPseudoTerminal } from './pseudoTerminal'; import type { SSHPseudoTerminal } from './pseudoTerminal';
import type { SSHFileSystem } from './sshFileSystem'; import type { SSHFileSystem } from './sshFileSystem';
import { toPromise } from './toPromise'; import { mergeEnvironment, toPromise } from './utils';
export interface Connection { export interface Connection {
config: FileSystemConfig; config: FileSystemConfig;
@ -21,41 +21,6 @@ export interface Connection {
idleTimer: NodeJS.Timeout; idleTimer: NodeJS.Timeout;
} }
export function mergeEnvironment(env: EnvironmentVariable[], ...others: (EnvironmentVariable[] | Record<string, string> | undefined)[]): EnvironmentVariable[] {
const result = [...env];
for (const other of others) {
if (!other) continue;
if (Array.isArray(other)) {
for (const variable of other) {
const index = result.findIndex(v => v.key === variable.key);
if (index === -1) result.push(variable);
else result[index] = variable;
}
} else {
for (const [key, value] of Object.entries(other)) {
result.push({ key, value });
}
}
}
return result;
}
// https://stackoverflow.com/a/20053121 way 1
const CLEAN_BASH_VALUE_REGEX = /^[\w-/\\]+$/;
function escapeBashValue(value: string) {
if (CLEAN_BASH_VALUE_REGEX.test(value)) return value;
return `'${value.replace(/'/g, `'\\''`)}'`;
}
export function environmentToExportString(env: EnvironmentVariable[]): string {
return env.map(({ key, value }) => `export ${escapeBashValue(key)}=${escapeBashValue(value)}`).join('; ');
}
export function joinCommands(commands: string | string[] | undefined, separator: string): string | undefined {
if (!commands) return undefined;
if (typeof commands === 'string') return commands;
return commands.filter(c => c && c.trim()).join(separator);
}
async function tryGetHome(ssh: Client): Promise<string | null> { async function tryGetHome(ssh: Client): Promise<string | null> {
const exec = await toPromise<ClientChannel>(cb => ssh.exec('echo Home: ~', cb)); const exec = await toPromise<ClientChannel>(cb => ssh.exec('echo Home: ~', cb));
let home = ''; let home = '';

@ -1,12 +1,12 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getConfig, getFlagBoolean, loadConfigsRaw } from './config'; import { getConfig, getFlagBoolean, loadConfigsRaw } from './config';
import { Connection, ConnectionManager, joinCommands } from './connection'; import { Connection, ConnectionManager } from './connection';
import type { FileSystemConfig } from './fileSystemConfig'; import type { FileSystemConfig } from './fileSystemConfig';
import { Logging, LOGGING_NO_STACKTRACE } from './logging'; import { Logging, LOGGING_NO_STACKTRACE } from './logging';
import { isSSHPseudoTerminal, replaceVariables, replaceVariablesRecursive } from './pseudoTerminal'; import { isSSHPseudoTerminal, replaceVariables, replaceVariablesRecursive } from './pseudoTerminal';
import type { SSHFileSystem } from './sshFileSystem'; import type { SSHFileSystem } from './sshFileSystem';
import { catchingPromise } from './toPromise'; import { catchingPromise, joinCommands } from './utils';
import type { Navigation } from './webviewMessages'; import type { Navigation } from './webviewMessages';
function commandArgumentToName(arg?: string | FileSystemConfig | Connection): string { function commandArgumentToName(arg?: string | FileSystemConfig | Connection): string {

@ -4,7 +4,7 @@ import { request } from 'http';
import { SocksClient } from 'socks'; import { SocksClient } from 'socks';
import type { FileSystemConfig } from './fileSystemConfig'; import type { FileSystemConfig } from './fileSystemConfig';
import { Logging } from './logging'; import { Logging } from './logging';
import { toPromise } from './toPromise'; import { toPromise } from './utils';
async function resolveHostname(hostname: string): Promise<string> { async function resolveHostname(hostname: string): Promise<string> {
return toPromise<string>(cb => dns.lookup(hostname, cb)).then((ip) => { return toPromise<string>(cb => dns.lookup(hostname, cb)).then((ip) => {

@ -3,10 +3,10 @@ import type { ClientChannel, PseudoTtyOptions } from "ssh2";
import type { Readable } from "stream"; import type { Readable } from "stream";
import * as vscode from "vscode"; import * as vscode from "vscode";
import { getFlagBoolean } from './config'; import { getFlagBoolean } from './config';
import { Connection, environmentToExportString, joinCommands, mergeEnvironment } from './connection'; import type { Connection } from './connection';
import type { EnvironmentVariable, FileSystemConfig } from "./fileSystemConfig"; import type { EnvironmentVariable, FileSystemConfig } from "./fileSystemConfig";
import { Logging, LOGGING_NO_STACKTRACE } from "./logging"; import { Logging, LOGGING_NO_STACKTRACE } from "./logging";
import { toPromise } from "./toPromise"; import { environmentToExportString, joinCommands, mergeEnvironment, toPromise } from './utils';
const [HEIGHT, WIDTH] = [480, 640]; const [HEIGHT, WIDTH] = [480, 640];
const PSEUDO_TTY_OPTIONS: PseudoTtyOptions = { const PSEUDO_TTY_OPTIONS: PseudoTtyOptions = {

@ -1,7 +1,7 @@
import * as Winreg from 'winreg'; import * as Winreg from 'winreg';
import { Logging } from './logging'; import { Logging } from './logging';
import { toPromise } from './toPromise'; import { toPromise } from './utils';
const winreg = new Winreg({ const winreg = new Winreg({
hive: Winreg.HKCU, hive: Winreg.HKCU,

@ -1,24 +0,0 @@
export type toPromiseCallback<T> = (err?: Error | null | void, res?: T) => void;
export async function toPromise<T>(func: (cb: toPromiseCallback<T>) => void): Promise<T> {
return new Promise<T>((resolve, reject) => {
try {
func((err, res) => err ? reject(err) : resolve(res!));
} catch (e) {
reject(e);
}
});
}
export async function catchingPromise<T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => any): Promise<T> {
return new Promise<T>((resolve, reject) => {
try {
const p = executor(resolve, reject);
if (p instanceof Promise) {
p.catch(reject);
}
} catch (e) {
reject(e);
}
});
}

@ -0,0 +1,67 @@
import type { EnvironmentVariable } from "./fileSystemConfig";
export type toPromiseCallback<T> = (err?: Error | null | void, res?: T) => void;
/** Wrapper around async callback-based functions */
export async function toPromise<T>(func: (cb: toPromiseCallback<T>) => void): Promise<T> {
return new Promise<T>((resolve, reject) => {
try {
func((err, res) => err ? reject(err) : resolve(res!));
} catch (e) {
reject(e);
}
});
}
/** Wrapper around async callback-based functions */
export async function catchingPromise<T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => any): Promise<T> {
return new Promise<T>((resolve, reject) => {
try {
const p = executor(resolve, reject);
if (p instanceof Promise) {
p.catch(reject);
}
} catch (e) {
reject(e);
}
});
}
const CLEAN_BASH_VALUE_REGEX = /^[\w-/\\]+$/;
/** Based on way 1 in https://stackoverflow.com/a/20053121 */
function escapeBashValue(value: string) {
if (CLEAN_BASH_VALUE_REGEX.test(value)) return value;
return `'${value.replace(/'/g, `'\\''`)}'`;
}
/** Convert an {@link EnvironmentVariable} array to a `export var1=val; export var2='escaped$val'` etc */
export function environmentToExportString(env: EnvironmentVariable[]): string {
return env.map(({ key, value }) => `export ${escapeBashValue(key)}=${escapeBashValue(value)}`).join('; ');
}
/** Returns a new {@link EnvironmentVariable} array with all the given environments merged into it, overwriting same-key variables */
export function mergeEnvironment(env: EnvironmentVariable[], ...others: (EnvironmentVariable[] | Record<string, string> | undefined)[]): EnvironmentVariable[] {
const result = [...env];
for (const other of others) {
if (!other) continue;
if (Array.isArray(other)) {
for (const variable of other) {
const index = result.findIndex(v => v.key === variable.key);
if (index === -1) result.push(variable);
else result[index] = variable;
}
} else {
for (const [key, value] of Object.entries(other)) {
result.push({ key, value });
}
}
}
return result;
}
/** Joins the commands together using the given separator. Automatically ignores `undefined` and empty strings */
export function joinCommands(commands: string | string[] | undefined, separator: string): string | undefined {
if (!commands) return undefined;
if (typeof commands === 'string') return commands;
return commands.filter(c => c && c.trim()).join(separator);
}

@ -5,7 +5,7 @@ import * as vscode from 'vscode';
import { deleteConfig, loadConfigsRaw, updateConfig } from './config'; import { deleteConfig, loadConfigsRaw, updateConfig } from './config';
import { getLocations } from './fileSystemConfig'; import { getLocations } from './fileSystemConfig';
import { DEBUG, Logging as _Logging, LOGGING_NO_STACKTRACE } from './logging'; import { DEBUG, Logging as _Logging, LOGGING_NO_STACKTRACE } from './logging';
import { toPromise } from './toPromise'; import { toPromise } from './utils';
import type { Message, Navigation } from './webviewMessages'; import type { Message, Navigation } from './webviewMessages';
const Logging = _Logging.scope('WebView'); const Logging = _Logging.scope('WebView');

Loading…
Cancel
Save