curlconverter
Version:
convert curl commands to Python, JavaScript, Go, PHP and more
282 lines (262 loc) • 7.72 kB
text/typescript
import { CCError } from "../../utils.js";
import { Word } from "../../shell/Word.js";
import type { Request } from "../../parse.js";
// Wrap in brakets so that splitting keeps the characters to escape
const regexEscape = /(\p{C}|[^ \P{Z}])/gu;
// TODO: do we need to consider that some strings could be used
// with sprintf() and have to have more stuff escaped?
function strToParts(s: string): string[] {
if (!s) {
return ["''"];
}
const parts = s
.replace(/'/g, "''")
.split(regexEscape)
.filter((x) => x) // empty strings between consecutive matches
.flatMap((x) => {
if (x.match(regexEscape)) {
if (x.length === 1) {
switch (x) {
case "\x07":
return "sprintf('\\a')";
case "\b":
return "sprintf('\\b')";
case "\f":
return "sprintf('\\f')";
case "\n":
return "newline";
case "\r":
return "sprintf('\\r')";
case "\t":
return "sprintf('\\t')";
case "\v":
return "sprintf('\\v')";
default:
return `char(${x.charCodeAt(0)})`;
}
} else {
return [`char(${x.charCodeAt(0)})`, `char(${x.charCodeAt(1)})`];
}
}
return "'" + x + "'";
});
return parts;
}
function joinParts(parts: string[]): string {
if (parts.length > 1) {
return "[" + parts.join(" ") + "]";
}
if (parts.length === 0) {
return "''"; // shouldn't happen
}
return parts[0];
}
function reprStr(s: string): string {
return joinParts(strToParts(s));
}
function repr(w: Word | null) {
// In context of url parameters, don't accept nulls and such.
if (w === null || w.length === 0) {
return "''";
}
let parts: string[] = [];
for (const t of w.tokens) {
if (typeof t === "string") {
parts = parts.concat(strToParts(t));
} else if (t.type === "variable") {
// https://www.mathworks.com/help/matlab/ref/getenv.html
parts.push("getenv(" + reprStr(t.value) + ")");
} else {
// TODO: is this array access correct?
// https://www.mathworks.com/help/matlab/ref/system.html
parts.push("system(" + reprStr(t.value) + "){2}");
}
}
return joinParts(parts);
}
function setVariableValue(
outputVariable: string | null,
value: string,
termination?: string,
): string {
let result = "";
if (outputVariable) {
result += outputVariable + " = ";
}
result += value;
result +=
typeof termination === "undefined" || termination === null
? ";"
: termination;
return result;
}
function callFunction(
outputVariable: string | null,
functionName: string,
params: string | string[] | string[][],
termination?: string,
) {
let functionCall = functionName + "(";
if (Array.isArray(params)) {
const singleLine = params
.map((x) => (Array.isArray(x) ? x.join(", ") : x))
.join(", ");
const indentLevel = 1;
const indent = " ".repeat(4 * indentLevel);
const skipToNextLine = "...\n" + indent;
let multiLine = skipToNextLine;
multiLine += params
.map((x) => (Array.isArray(x) ? x.join(", ") : x))
.join("," + skipToNextLine);
multiLine += "...\n";
// Split the params in multiple lines - if one line is not enough
const combinedSingleLineLength =
[outputVariable, functionName, singleLine]
.map((x) => (x ? x.length : 0))
.reduce((x, y) => x + y) +
(outputVariable ? 3 : 0) +
2 +
(termination ? termination.length : 1);
functionCall += combinedSingleLineLength < 120 ? singleLine : multiLine;
} else {
functionCall += params;
}
functionCall += ")";
return setVariableValue(outputVariable, functionCall, termination);
}
function addCellArray(
mapping: ([Word, Word] | [string, Word | string])[],
keysNotToQuote?: string[],
indentLevel = 1,
pairs?: boolean,
) {
if (mapping.length === 0) return ""; // shouldn't happen
const indentUnit = " ".repeat(4);
const indent = indentUnit.repeat(indentLevel);
const indentPrevLevel = indentUnit.repeat(indentLevel - 1);
const separator = pairs ? ", " : " ";
let response = "";
if (!pairs) {
response += "{";
}
if (pairs && mapping.length > 1) {
response += "...";
}
for (const [counter, [key, value]] of mapping.entries()) {
const k = typeof key === "string" ? reprStr(key) : repr(key);
let val: string;
if (
keysNotToQuote &&
keysNotToQuote.includes(key.toLowerCase().toString())
) {
val = value.toString();
} else {
val = typeof value === "string" ? reprStr(value) : repr(value);
}
if (mapping.length > 1) {
response += "\n" + indent;
}
response += k + separator + val;
if (pairs && mapping.length > 1) {
// Don't add trailing comma
if (counter !== mapping.length - 1) {
response += ",";
}
response += "...";
}
}
if (mapping.length > 1) {
response += "\n" + indentPrevLevel;
}
if (!pairs) {
response += "}";
}
return response;
}
function structify(
obj: number[] | string[] | { [key: string]: string } | string | number | null,
indentLevel?: number,
) {
let response = "";
indentLevel = !indentLevel ? 1 : ++indentLevel;
const indent = " ".repeat(4 * indentLevel);
const prevIndent = " ".repeat(4 * (indentLevel - 1));
if (obj instanceof Array) {
const list: string[] = [];
let listContainsNumbers = true;
for (const k in obj) {
if (listContainsNumbers && typeof obj[k] !== "number") {
listContainsNumbers = false;
}
const value = structify(obj[k], indentLevel);
list.push(`${value}`);
}
if (listContainsNumbers) {
const listString = list.join(" ");
response += `[${listString}]`;
} else {
list.unshift("{{");
const listString = list.join(`\n${indent}`);
response += `${listString}\n${prevIndent}}}`;
}
} else if (obj instanceof Object) {
response += "struct(...";
let first = true;
for (const k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
if (!k[0].match(/[a-z]/i)) {
throw new CCError(
"MATLAB structs do not support keys starting with non-alphabet symbols",
);
}
// recursive call to scan property
if (first) {
first = false;
} else {
response += ",...";
}
response += `\n${indent}`;
response += `'${k}', `;
response += structify(obj[k], indentLevel);
}
}
response += "...";
response += `\n${prevIndent})`;
} else if (typeof obj === "number") {
// not an Object so obj[k] here is a value
response += obj.toString();
} else {
response += obj === null ? "string(nan)" : reprStr(obj);
}
return response;
}
function containsBody(request: Request): boolean {
return Boolean(request.data || request.multipartUploads);
}
function prepareQueryString(request: Request): string | null {
if (!request.urls[0].queryList) {
return null;
}
return setVariableValue("params", addCellArray(request.urls[0].queryList));
}
function prepareCookies(request: Request): string | null {
if (!request.cookies) {
return null;
}
return setVariableValue("cookies", addCellArray(request.cookies));
}
const cookieString = "char(join(join(cookies, '='), '; '))";
const paramsString = "char(join(join(params, '='), '&'))";
export {
reprStr,
repr,
setVariableValue,
callFunction,
addCellArray,
structify,
containsBody,
prepareQueryString,
prepareCookies,
cookieString,
paramsString,
};