From c7a141deb52d8c3e199dcdc3b95faa1cf8f1d749 Mon Sep 17 00:00:00 2001
From: QingFeng <331052249@qq.com>
Date: Tue, 7 Nov 2023 14:46:18 +0800
Subject: [PATCH 1/4] fix chinese encoding
---
src/utils/base64.ts | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/utils/base64.ts b/src/utils/base64.ts
index 16912ee3..171da984 100644
--- a/src/utils/base64.ts
+++ b/src/utils/base64.ts
@@ -1,7 +1,7 @@
export { textToBase64, base64ToText, isValidBase64, removePotentialDataAndMimePrefix };
function textToBase64(str: string, { makeUrlSafe = false }: { makeUrlSafe?: boolean } = {}) {
- const encoded = window.btoa(str);
+ const encoded = encode(str);
return makeUrlSafe ? makeUriSafe(encoded) : encoded;
}
@@ -16,13 +16,18 @@ function base64ToText(str: string, { makeUrlSafe = false }: { makeUrlSafe?: bool
}
try {
- return window.atob(cleanStr);
+ return decode(cleanStr);
}
catch (_) {
throw new Error('Incorrect base64 string');
}
}
-
+function encode(str: string) {
+ return window.btoa(unescape(encodeURIComponent(str)));
+}
+function decode(str: string) {
+ return decodeURIComponent(escape(window.atob(str)));
+}
function removePotentialDataAndMimePrefix(str: string) {
return str.replace(/^data:.*?;base64,/, '');
}
From 96dacfbfd5338b2acaedd130699232607e450205 Mon Sep 17 00:00:00 2001
From: QingFeng <331052249@qq.com>
Date: Wed, 8 Nov 2023 11:53:26 +0800
Subject: [PATCH 2/4] add new tools json to go
---
src/tools/index.ts | 2 +
src/tools/json-to-go/index.ts | 13 +
src/tools/json-to-go/json-to-go.service.ts | 413 +++++++++++++++++++++
src/tools/json-to-go/json-to-go.vue | 50 +++
src/tools/json-to-go/locales/en.yml | 7 +
5 files changed, 485 insertions(+)
create mode 100644 src/tools/json-to-go/index.ts
create mode 100644 src/tools/json-to-go/json-to-go.service.ts
create mode 100644 src/tools/json-to-go/json-to-go.vue
create mode 100644 src/tools/json-to-go/locales/en.yml
diff --git a/src/tools/index.ts b/src/tools/index.ts
index fa63d3d1..aa1c47f5 100644
--- a/src/tools/index.ts
+++ b/src/tools/index.ts
@@ -12,6 +12,7 @@ import { tool as emojiPicker } from './emoji-picker';
import { tool as passwordStrengthAnalyser } from './password-strength-analyser';
import { tool as yamlToToml } from './yaml-to-toml';
import { tool as jsonToToml } from './json-to-toml';
+import { tool as jsonToGo } from './json-to-go';
import { tool as tomlToYaml } from './toml-to-yaml';
import { tool as tomlToJson } from './toml-to-json';
import { tool as jsonToCsv } from './json-to-csv';
@@ -96,6 +97,7 @@ export const toolsByCategory: ToolCategory[] = [
yamlToToml,
jsonToYaml,
jsonToToml,
+ jsonToGo,
listConverter,
tomlToJson,
tomlToYaml,
diff --git a/src/tools/json-to-go/index.ts b/src/tools/json-to-go/index.ts
new file mode 100644
index 00000000..0dd11168
--- /dev/null
+++ b/src/tools/json-to-go/index.ts
@@ -0,0 +1,13 @@
+import { Braces } from '@vicons/tabler';
+import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
+
+export const tool = defineTool({
+ name: translate('tools.json-to-go.title'),
+ path: '/json-to-go',
+ description: translate('tools.json-to-go.description'),
+ keywords: ['json', 'parse', 'go', 'convert', 'transform'],
+ component: () => import('./json-to-go.vue'),
+ icon: Braces,
+ createdAt: new Date('2023-11-08'),
+});
diff --git a/src/tools/json-to-go/json-to-go.service.ts b/src/tools/json-to-go/json-to-go.service.ts
new file mode 100644
index 00000000..0b910a82
--- /dev/null
+++ b/src/tools/json-to-go/json-to-go.service.ts
@@ -0,0 +1,413 @@
+// JSON-to-Go
+// by Matt Holt
+// https://github.com/mholt/json-to-go
+// A simple utility to translate JSON into a Go type definition.
+export function jsonToGo(json, typename, flatten = true, example = false, allOmitempty = false) {
+ let data;
+ let scope;
+ let go = '';
+ let tabs = 0;
+
+ const seen = {};
+ const stack = [];
+ let accumulator = '';
+ const innerTabs = 0;
+ let parent = '';
+
+ try {
+ data = JSON.parse(json.replace(/(:\s*\[?\s*-?\d*)\.0/g, '$1.1')); // hack that forces floats to stay as floats
+ scope = data;
+ }
+ catch (e) {
+ return {
+ go: '',
+ error: e.message,
+ };
+ }
+
+ typename = format(typename || 'AutoGenerated');
+ append(`type ${typename} `);
+
+ parseScope(scope);
+
+ return {
+ go: flatten
+ ? go += accumulator
+ : go,
+ };
+
+ function parseScope(scope, depth = 0) {
+ if (typeof scope === 'object' && scope !== null) {
+ if (Array.isArray(scope)) {
+ let sliceType;
+ const scopeLength = scope.length;
+
+ for (let i = 0; i < scopeLength; i++) {
+ const thisType = goType(scope[i]);
+ if (!sliceType) { sliceType = thisType; }
+ else if (sliceType != thisType) {
+ sliceType = mostSpecificPossibleGoType(thisType, sliceType);
+ if (sliceType == 'any') { break; }
+ }
+ }
+
+ const slice = flatten && ['struct', 'slice'].includes(sliceType)
+ ? `[]${parent}`
+ : '[]';
+
+ if (flatten && depth >= 2) { appender(slice); }
+ else { append(slice); };
+ if (sliceType == 'struct') {
+ const allFields = {};
+
+ // for each field counts how many times appears
+ for (let i = 0; i < scopeLength; i++) {
+ const keys = Object.keys(scope[i]);
+ for (const k in keys) {
+ let keyname = keys[k];
+ if (!(keyname in allFields)) {
+ allFields[keyname] = {
+ value: scope[i][keyname],
+ count: 0,
+ };
+ }
+ else {
+ const existingValue = allFields[keyname].value;
+ const currentValue = scope[i][keyname];
+
+ if (compareObjects(existingValue, currentValue)) {
+ const comparisonResult = compareObjectKeys(
+ Object.keys(currentValue),
+ Object.keys(existingValue),
+ );
+ if (!comparisonResult) {
+ keyname = `${keyname}_${uuidv4()}`;
+ allFields[keyname] = {
+ value: currentValue,
+ count: 0,
+ };
+ }
+ }
+ }
+ allFields[keyname].count++;
+ }
+ }
+
+ // create a common struct with all fields found in the current array
+ // omitempty dict indicates if a field is optional
+ const keys = Object.keys(allFields); const struct = {}; const omitempty = {};
+ for (const k in keys) {
+ const keyname = keys[k]; const elem = allFields[keyname];
+
+ struct[keyname] = elem.value;
+ omitempty[keyname] = elem.count != scopeLength;
+ }
+ parseStruct(depth + 1, innerTabs, struct, omitempty); // finally parse the struct !!
+ }
+ else if (sliceType == 'slice') {
+ parseScope(scope[0], depth);
+ }
+ else {
+ if (flatten && depth >= 2) {
+ appender(sliceType || 'any');
+ }
+ else {
+ append(sliceType || 'any');
+ }
+ }
+ }
+ else {
+ if (flatten) {
+ if (depth >= 2) {
+ appender(parent);
+ }
+ else {
+ append(parent);
+ }
+ }
+ parseStruct(depth + 1, innerTabs, scope);
+ }
+ }
+ else {
+ if (flatten && depth >= 2) {
+ appender(goType(scope));
+ }
+ else {
+ append(goType(scope));
+ }
+ }
+ }
+
+ function parseStruct(depth, innerTabs, scope, omitempty) {
+ if (flatten) {
+ stack.push(
+ depth >= 2
+ ? '\n'
+ : '',
+ );
+ }
+
+ const seenTypeNames = [];
+
+ if (flatten && depth >= 2) {
+ const parentType = `type ${parent}`;
+ const scopeKeys = formatScopeKeys(Object.keys(scope));
+
+ // this can only handle two duplicate items
+ // future improvement will handle the case where there could
+ // three or more duplicate keys with different values
+ if (parent in seen && compareObjectKeys(scopeKeys, seen[parent])) {
+ stack.pop();
+ return;
+ }
+ seen[parent] = scopeKeys;
+
+ appender(`${parentType} struct {\n`);
+ ++innerTabs;
+ const keys = Object.keys(scope);
+ for (const i in keys) {
+ const keyname = getOriginalName(keys[i]);
+ indenter(innerTabs);
+ const typename = uniqueTypeName(format(keyname), seenTypeNames);
+ seenTypeNames.push(typename);
+
+ appender(`${typename} `);
+ parent = typename;
+ parseScope(scope[keys[i]], depth);
+ appender(` \`json:"${keyname}`);
+ if (allOmitempty || (omitempty && omitempty[keys[i]] === true)) {
+ appender(',omitempty');
+ }
+ appender('"`\n');
+ }
+ indenter(--innerTabs);
+ appender('}');
+ }
+ else {
+ append('struct {\n');
+ ++tabs;
+ const keys = Object.keys(scope);
+ for (const i in keys) {
+ const keyname = getOriginalName(keys[i]);
+ indent(tabs);
+ const typename = uniqueTypeName(format(keyname), seenTypeNames);
+ seenTypeNames.push(typename);
+ append(`${typename} `);
+ parent = typename;
+ parseScope(scope[keys[i]], depth);
+ append(` \`json:"${keyname}`);
+ if (allOmitempty || (omitempty && omitempty[keys[i]] === true)) {
+ append(',omitempty');
+ }
+ if (example && scope[keys[i]] !== '' && typeof scope[keys[i]] !== 'object') {
+ append(`" example:"${scope[keys[i]]}`);
+ }
+ append('"`\n');
+ }
+ indent(--tabs);
+ append('}');
+ }
+ if (flatten) { accumulator += stack.pop(); }
+ }
+
+ function indent(tabs) {
+ for (let i = 0; i < tabs; i++) { go += '\t'; }
+ }
+
+ function append(str) {
+ go += str;
+ }
+
+ function indenter(tabs) {
+ for (let i = 0; i < tabs; i++) { stack[stack.length - 1] += '\t'; }
+ }
+
+ function appender(str) {
+ stack[stack.length - 1] += str;
+ }
+
+ // Generate a unique name to avoid duplicate struct field names.
+ // This function appends a number at the end of the field name.
+ function uniqueTypeName(name, seen) {
+ if (!seen.includes(name)) {
+ return name;
+ }
+
+ let i = 0;
+ while (true) {
+ const newName = name + i.toString();
+ if (!seen.includes(newName)) {
+ return newName;
+ }
+
+ i++;
+ }
+ }
+
+ // Sanitizes and formats a string to make an appropriate identifier in Go
+ function format(str) {
+ str = formatNumber(str);
+
+ const sanitized = toProperCase(str).replace(/[^a-z0-9]/ig, '');
+ if (!sanitized) {
+ return 'NAMING_FAILED';
+ }
+
+ // After sanitizing the remaining characters can start with a number.
+ // Run the sanitized string again trough formatNumber to make sure the identifier is Num[0-9] or Zero_... instead of 1.
+ return formatNumber(sanitized);
+ }
+
+ // Adds a prefix to a number to make an appropriate identifier in Go
+ function formatNumber(str) {
+ if (!str) { return ''; }
+ else if (str.match(/^\d+$/)) { str = `Num${str}`; }
+ else if (str.charAt(0).match(/\d/)) {
+ const numbers = {
+ 0: 'Zero_',
+ 1: 'One_',
+ 2: 'Two_',
+ 3: 'Three_',
+ 4: 'Four_',
+ 5: 'Five_',
+ 6: 'Six_',
+ 7: 'Seven_',
+ 8: 'Eight_',
+ 9: 'Nine_',
+ };
+ str = numbers[str.charAt(0)] + str.substr(1);
+ }
+
+ return str;
+ }
+
+ // Determines the most appropriate Go type
+ function goType(val) {
+ if (val === null) { return 'any'; }
+
+ switch (typeof val) {
+ case 'string':
+ if (/\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(\+\d\d:\d\d|Z)/.test(val)) { return 'time.Time'; }
+ else { return 'string'; }
+ case 'number':
+ if (val % 1 === 0) {
+ if (val > -2147483648 && val < 2147483647) { return 'int'; }
+ else { return 'int64'; }
+ }
+ else { return 'float64'; }
+ case 'boolean':
+ return 'bool';
+ case 'object':
+ if (Array.isArray(val)) { return 'slice'; }
+ return 'struct';
+ default:
+ return 'any';
+ }
+ }
+
+ // Given two types, returns the more specific of the two
+ function mostSpecificPossibleGoType(typ1, typ2) {
+ if (typ1.substr(0, 5) == 'float'
+ && typ2.substr(0, 3) == 'int') { return typ1; }
+ else if (typ1.substr(0, 3) == 'int'
+ && typ2.substr(0, 5) == 'float') { return typ2; }
+ else { return 'any'; }
+ }
+
+ // Proper cases a string according to Go conventions
+ function toProperCase(str) {
+ // ensure that the SCREAMING_SNAKE_CASE is converted to snake_case
+ if (str.match(/^[_A-Z0-9]+$/)) {
+ str = str.toLowerCase();
+ }
+
+ // https://github.com/golang/lint/blob/5614ed5bae6fb75893070bdc0996a68765fdd275/lint.go#L771-L810
+ const commonInitialisms = [
+ 'ACL', 'API', 'ASCII', 'CPU', 'CSS', 'DNS', 'EOF', 'GUID', 'HTML', 'HTTP',
+ 'HTTPS', 'ID', 'IP', 'JSON', 'LHS', 'QPS', 'RAM', 'RHS', 'RPC', 'SLA',
+ 'SMTP', 'SQL', 'SSH', 'TCP', 'TLS', 'TTL', 'UDP', 'UI', 'UID', 'UUID',
+ 'URI', 'URL', 'UTF8', 'VM', 'XML', 'XMPP', 'XSRF', 'XSS',
+ ];
+
+ return str.replace(/(^|[^a-zA-Z])([a-z]+)/g, (unused, sep, frag) => {
+ if (commonInitialisms.includes(frag.toUpperCase())) { return sep + frag.toUpperCase(); }
+ else { return sep + frag[0].toUpperCase() + frag.substr(1).toLowerCase(); }
+ }).replace(/([A-Z])([a-z]+)/g, (unused, sep, frag) => {
+ if (commonInitialisms.includes(sep + frag.toUpperCase())) { return (sep + frag).toUpperCase(); }
+ else { return sep + frag; }
+ });
+ }
+
+ function uuidv4() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+ const r = Math.random() * 16 | 0; const v = c == 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+ }
+
+ function getOriginalName(unique) {
+ const reLiteralUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
+ const uuidLength = 36;
+
+ if (unique.length >= uuidLength) {
+ const tail = unique.substr(-uuidLength);
+ if (reLiteralUUID.test(tail)) {
+ return unique.slice(0, -1 * (uuidLength + 1));
+ }
+ }
+ return unique;
+ }
+
+ function compareObjects(objectA, objectB) {
+ const object = '[object Object]';
+ return Object.prototype.toString.call(objectA) === object
+ && Object.prototype.toString.call(objectB) === object;
+ }
+
+ function compareObjectKeys(itemAKeys, itemBKeys) {
+ const lengthA = itemAKeys.length;
+ const lengthB = itemBKeys.length;
+
+ // nothing to compare, probably identical
+ if (lengthA == 0 && lengthB == 0) { return true; }
+
+ // duh
+ if (lengthA != lengthB) { return false; }
+
+ for (const item of itemAKeys) {
+ if (!itemBKeys.includes(item)) { return false; }
+ }
+ return true;
+ }
+
+ function formatScopeKeys(keys) {
+ for (const i in keys) {
+ keys[i] = format(keys[i]);
+ }
+ return keys;
+ }
+}
+
+if (typeof module != 'undefined') {
+ if (!module.parent) {
+ if (process.argv.length > 2 && process.argv[2] === '-big') {
+ bufs = [];
+ process.stdin.on('data', (buf) => {
+ bufs.push(buf);
+ });
+ process.stdin.on('end', () => {
+ const json = Buffer.concat(bufs).toString('utf8');
+ console.log(jsonToGo(json).go);
+ });
+ }
+ else {
+ process.stdin.on('data', (buf) => {
+ const json = buf.toString('utf8');
+ console.log(jsonToGo(json).go);
+ });
+ }
+ }
+ else {
+ module.exports = jsonToGo;
+ }
+}
diff --git a/src/tools/json-to-go/json-to-go.vue b/src/tools/json-to-go/json-to-go.vue
new file mode 100644
index 00000000..b19bf615
--- /dev/null
+++ b/src/tools/json-to-go/json-to-go.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tools/json-to-go/locales/en.yml b/src/tools/json-to-go/locales/en.yml
new file mode 100644
index 00000000..218ae540
--- /dev/null
+++ b/src/tools/json-to-go/locales/en.yml
@@ -0,0 +1,7 @@
+tools:
+ json-to-go:
+ title: JSON to Go
+ description: Convert JSON to Go struct
+ definitions: Inline type definitions
+ omitempty: omitempty
+ example: example
\ No newline at end of file
From 3c12767c1b9fdd2405970c18870d1200e0838386 Mon Sep 17 00:00:00 2001
From: QingFeng <331052249@qq.com>
Date: Wed, 8 Nov 2023 12:23:48 +0800
Subject: [PATCH 3/4] fix code
---
src/tools/json-to-go/json-to-go.service.ts | 166 +++++++++++----------
1 file changed, 90 insertions(+), 76 deletions(-)
diff --git a/src/tools/json-to-go/json-to-go.service.ts b/src/tools/json-to-go/json-to-go.service.ts
index 0b910a82..cd7484ee 100644
--- a/src/tools/json-to-go/json-to-go.service.ts
+++ b/src/tools/json-to-go/json-to-go.service.ts
@@ -2,14 +2,14 @@
// by Matt Holt
// https://github.com/mholt/json-to-go
// A simple utility to translate JSON into a Go type definition.
-export function jsonToGo(json, typename, flatten = true, example = false, allOmitempty = false) {
+export function jsonToGo(json: string, typename: string | '', flatten = true, example = false, allOmitempty = false) {
let data;
let scope;
let go = '';
let tabs = 0;
const seen = {};
- const stack = [];
+ const stack: any[] = [];
let accumulator = '';
const innerTabs = 0;
let parent = '';
@@ -21,7 +21,6 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
catch (e) {
return {
go: '',
- error: e.message,
};
}
@@ -36,7 +35,7 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
: go,
};
- function parseScope(scope, depth = 0) {
+ function parseScope(scope: string | any[] | null, depth = 0) {
if (typeof scope === 'object' && scope !== null) {
if (Array.isArray(scope)) {
let sliceType;
@@ -44,10 +43,14 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
for (let i = 0; i < scopeLength; i++) {
const thisType = goType(scope[i]);
- if (!sliceType) { sliceType = thisType; }
- else if (sliceType != thisType) {
+ if (!sliceType) {
+ sliceType = thisType;
+ }
+ else if (sliceType !== thisType) {
sliceType = mostSpecificPossibleGoType(thisType, sliceType);
- if (sliceType == 'any') { break; }
+ if (sliceType === 'any') {
+ break;
+ }
}
}
@@ -55,9 +58,11 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
? `[]${parent}`
: '[]';
- if (flatten && depth >= 2) { appender(slice); }
+ if (flatten && depth >= 2) {
+ appender(slice);
+ }
else { append(slice); };
- if (sliceType == 'struct') {
+ if (sliceType === 'struct') {
const allFields = {};
// for each field counts how many times appears
@@ -95,16 +100,19 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
// create a common struct with all fields found in the current array
// omitempty dict indicates if a field is optional
- const keys = Object.keys(allFields); const struct = {}; const omitempty = {};
+ const keys = Object.keys(allFields);
+ const struct = {};
+ const omitempty = {};
for (const k in keys) {
- const keyname = keys[k]; const elem = allFields[keyname];
+ const keyname = keys[k];
+ const elem = allFields[keyname];
struct[keyname] = elem.value;
- omitempty[keyname] = elem.count != scopeLength;
+ omitempty[keyname] = elem.count !== scopeLength;
}
parseStruct(depth + 1, innerTabs, struct, omitempty); // finally parse the struct !!
}
- else if (sliceType == 'slice') {
+ else if (sliceType === 'slice') {
parseScope(scope[0], depth);
}
else {
@@ -138,7 +146,7 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
}
}
- function parseStruct(depth, innerTabs, scope, omitempty) {
+ function parseStruct(depth: number | undefined, innerTabs: number, scope: { [x: string]: any }, omitempty: { [x: string]: boolean } | undefined) {
if (flatten) {
stack.push(
depth >= 2
@@ -207,28 +215,34 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
indent(--tabs);
append('}');
}
- if (flatten) { accumulator += stack.pop(); }
+ if (flatten) {
+ accumulator += stack.pop();
+ }
}
- function indent(tabs) {
- for (let i = 0; i < tabs; i++) { go += '\t'; }
+ function indent(tabs: number) {
+ for (let i = 0; i < tabs; i++) {
+ go += '\t';
+ }
}
- function append(str) {
+ function append(str: string) {
go += str;
}
- function indenter(tabs) {
- for (let i = 0; i < tabs; i++) { stack[stack.length - 1] += '\t'; }
+ function indenter(tabs: number) {
+ for (let i = 0; i < tabs; i++) {
+ stack[stack.length - 1] += '\t';
+ }
}
- function appender(str) {
+ function appender(str: string) {
stack[stack.length - 1] += str;
}
// Generate a unique name to avoid duplicate struct field names.
// This function appends a number at the end of the field name.
- function uniqueTypeName(name, seen) {
+ function uniqueTypeName(name: string, seen: string | any[]) {
if (!seen.includes(name)) {
return name;
}
@@ -245,7 +259,7 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
}
// Sanitizes and formats a string to make an appropriate identifier in Go
- function format(str) {
+ function format(str: any) {
str = formatNumber(str);
const sanitized = toProperCase(str).replace(/[^a-z0-9]/ig, '');
@@ -259,9 +273,13 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
}
// Adds a prefix to a number to make an appropriate identifier in Go
- function formatNumber(str) {
- if (!str) { return ''; }
- else if (str.match(/^\d+$/)) { str = `Num${str}`; }
+ function formatNumber(str: string) {
+ if (!str) {
+ return '';
+ }
+ else if (str.match(/^\d+$/)) {
+ str = `Num${str}`;
+ }
else if (str.charAt(0).match(/\d/)) {
const numbers = {
0: 'Zero_',
@@ -282,23 +300,31 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
}
// Determines the most appropriate Go type
- function goType(val) {
- if (val === null) { return 'any'; }
+ function goType(val: string | number | null) {
+ if (val === null) {
+ return 'any';
+ }
switch (typeof val) {
case 'string':
- if (/\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(\+\d\d:\d\d|Z)/.test(val)) { return 'time.Time'; }
+ if (/\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(\+\d\d:\d\d|Z)/.test(val)) {
+ return 'time.Time';
+ }
else { return 'string'; }
case 'number':
if (val % 1 === 0) {
- if (val > -2147483648 && val < 2147483647) { return 'int'; }
+ if (val > -2147483648 && val < 2147483647) {
+ return 'int';
+ }
else { return 'int64'; }
}
else { return 'float64'; }
case 'boolean':
return 'bool';
case 'object':
- if (Array.isArray(val)) { return 'slice'; }
+ if (Array.isArray(val)) {
+ return 'slice';
+ }
return 'struct';
default:
return 'any';
@@ -306,16 +332,18 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
}
// Given two types, returns the more specific of the two
- function mostSpecificPossibleGoType(typ1, typ2) {
- if (typ1.substr(0, 5) == 'float'
- && typ2.substr(0, 3) == 'int') { return typ1; }
- else if (typ1.substr(0, 3) == 'int'
- && typ2.substr(0, 5) == 'float') { return typ2; }
+ function mostSpecificPossibleGoType(typ1: string, typ2: string) {
+ if (typ1.substr(0, 5) === 'float' && typ2.substr(0, 3) === 'int') {
+ return typ1;
+ }
+ else if (typ1.substr(0, 3) === 'int' && typ2.substr(0, 5) === 'float') {
+ return typ2;
+ }
else { return 'any'; }
}
// Proper cases a string according to Go conventions
- function toProperCase(str) {
+ function toProperCase(str: string) {
// ensure that the SCREAMING_SNAKE_CASE is converted to snake_case
if (str.match(/^[_A-Z0-9]+$/)) {
str = str.toLowerCase();
@@ -329,23 +357,28 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
'URI', 'URL', 'UTF8', 'VM', 'XML', 'XMPP', 'XSRF', 'XSS',
];
- return str.replace(/(^|[^a-zA-Z])([a-z]+)/g, (unused, sep, frag) => {
- if (commonInitialisms.includes(frag.toUpperCase())) { return sep + frag.toUpperCase(); }
+ return str.replace(/(^|[^a-zA-Z])([a-z]+)/g, (unused: any, sep: any, frag: string | string[]) => {
+ if (commonInitialisms.includes(frag.toUpperCase())) {
+ return sep + frag.toUpperCase();
+ }
else { return sep + frag[0].toUpperCase() + frag.substr(1).toLowerCase(); }
- }).replace(/([A-Z])([a-z]+)/g, (unused, sep, frag) => {
- if (commonInitialisms.includes(sep + frag.toUpperCase())) { return (sep + frag).toUpperCase(); }
+ }).replace(/([A-Z])([a-z]+)/g, (unused: any, sep: any, frag: string) => {
+ if (commonInitialisms.includes(sep + frag.toUpperCase())) {
+ return (sep + frag).toUpperCase();
+ }
else { return sep + frag; }
});
}
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
- const r = Math.random() * 16 | 0; const v = c == 'x' ? r : (r & 0x3 | 0x8);
- return v.toString(16);
+ const r = Math.random() * 16 | 0;
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
});
}
- function getOriginalName(unique) {
+ function getOriginalName(unique: string) {
const reLiteralUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const uuidLength = 36;
@@ -358,56 +391,37 @@ export function jsonToGo(json, typename, flatten = true, example = false, allOmi
return unique;
}
- function compareObjects(objectA, objectB) {
+ function compareObjects(objectA: any, objectB: any) {
const object = '[object Object]';
- return Object.prototype.toString.call(objectA) === object
- && Object.prototype.toString.call(objectB) === object;
+ return Object.prototype.toString.call(objectA) === object && Object.prototype.toString.call(objectB) === object;
}
- function compareObjectKeys(itemAKeys, itemBKeys) {
+ function compareObjectKeys(itemAKeys: string | any[], itemBKeys: string | any[]) {
const lengthA = itemAKeys.length;
const lengthB = itemBKeys.length;
// nothing to compare, probably identical
- if (lengthA == 0 && lengthB == 0) { return true; }
+ if (lengthA === 0 && lengthB === 0) {
+ return true;
+ }
// duh
- if (lengthA != lengthB) { return false; }
+ if (lengthA !== lengthB) {
+ return false;
+ }
for (const item of itemAKeys) {
- if (!itemBKeys.includes(item)) { return false; }
+ if (!itemBKeys.includes(item)) {
+ return false;
+ }
}
return true;
}
- function formatScopeKeys(keys) {
+ function formatScopeKeys(keys: string[]) {
for (const i in keys) {
keys[i] = format(keys[i]);
}
return keys;
}
}
-
-if (typeof module != 'undefined') {
- if (!module.parent) {
- if (process.argv.length > 2 && process.argv[2] === '-big') {
- bufs = [];
- process.stdin.on('data', (buf) => {
- bufs.push(buf);
- });
- process.stdin.on('end', () => {
- const json = Buffer.concat(bufs).toString('utf8');
- console.log(jsonToGo(json).go);
- });
- }
- else {
- process.stdin.on('data', (buf) => {
- const json = buf.toString('utf8');
- console.log(jsonToGo(json).go);
- });
- }
- }
- else {
- module.exports = jsonToGo;
- }
-}
From 88f53e2c61b094256ee68d26717449a3abb49797 Mon Sep 17 00:00:00 2001
From: QingFeng <331052249@qq.com>
Date: Wed, 8 Nov 2023 13:25:29 +0800
Subject: [PATCH 4/4] typecheck
---
src/tools/json-to-go/json-to-go.service.ts | 35 ++++++++++++----------
src/tools/json-to-go/json-to-go.vue | 2 ++
2 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/src/tools/json-to-go/json-to-go.service.ts b/src/tools/json-to-go/json-to-go.service.ts
index cd7484ee..b7ef2252 100644
--- a/src/tools/json-to-go/json-to-go.service.ts
+++ b/src/tools/json-to-go/json-to-go.service.ts
@@ -7,8 +7,7 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
let scope;
let go = '';
let tabs = 0;
-
- const seen = {};
+ const seen: { [key: string]: any } = {};
const stack: any[] = [];
let accumulator = '';
const innerTabs = 0;
@@ -54,7 +53,7 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
}
}
- const slice = flatten && ['struct', 'slice'].includes(sliceType)
+ const slice = flatten && ['struct', 'slice'].includes(sliceType ?? '[]')
? `[]${parent}`
: '[]';
@@ -63,7 +62,7 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
}
else { append(slice); };
if (sliceType === 'struct') {
- const allFields = {};
+ const allFields = {} as Record;
// for each field counts how many times appears
for (let i = 0; i < scopeLength; i++) {
@@ -101,8 +100,8 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
// create a common struct with all fields found in the current array
// omitempty dict indicates if a field is optional
const keys = Object.keys(allFields);
- const struct = {};
- const omitempty = {};
+ const struct = {} as Record;
+ const omitempty = {} as Record;
for (const k in keys) {
const keyname = keys[k];
const elem = allFields[keyname];
@@ -133,7 +132,7 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
append(parent);
}
}
- parseStruct(depth + 1, innerTabs, scope);
+ parseStruct(depth + 1, innerTabs, scope, undefined);
}
}
else {
@@ -148,16 +147,18 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
function parseStruct(depth: number | undefined, innerTabs: number, scope: { [x: string]: any }, omitempty: { [x: string]: boolean } | undefined) {
if (flatten) {
- stack.push(
- depth >= 2
- ? '\n'
- : '',
- );
+ if (depth !== undefined) {
+ stack.push(
+ depth >= 2
+ ? '\n'
+ : '',
+ );
+ }
}
const seenTypeNames = [];
- if (flatten && depth >= 2) {
+ if (flatten && depth !== undefined && depth >= 2) {
const parentType = `type ${parent}`;
const scopeKeys = formatScopeKeys(Object.keys(scope));
@@ -281,7 +282,7 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
str = `Num${str}`;
}
else if (str.charAt(0).match(/\d/)) {
- const numbers = {
+ const numbers: { [key: string]: string } = {
0: 'Zero_',
1: 'One_',
2: 'Two_',
@@ -358,10 +359,12 @@ export function jsonToGo(json: string, typename: string | '', flatten = true, ex
];
return str.replace(/(^|[^a-zA-Z])([a-z]+)/g, (unused: any, sep: any, frag: string | string[]) => {
- if (commonInitialisms.includes(frag.toUpperCase())) {
+ if (!Array.isArray(frag) && commonInitialisms.includes(frag.toUpperCase())) {
return sep + frag.toUpperCase();
}
- else { return sep + frag[0].toUpperCase() + frag.substr(1).toLowerCase(); }
+ else {
+ return sep + frag[0].toUpperCase() + (frag as string).substr(1).toLowerCase();
+ }
}).replace(/([A-Z])([a-z]+)/g, (unused: any, sep: any, frag: string) => {
if (commonInitialisms.includes(sep + frag.toUpperCase())) {
return (sep + frag).toUpperCase();
diff --git a/src/tools/json-to-go/json-to-go.vue b/src/tools/json-to-go/json-to-go.vue
index b19bf615..ef19a6a6 100644
--- a/src/tools/json-to-go/json-to-go.vue
+++ b/src/tools/json-to-go/json-to-go.vue
@@ -4,6 +4,7 @@ import { jsonToGo } from './json-to-go.service';
import type { UseValidationRule } from '@/composable/validation';
import TextareaCopyable from '@/components/TextareaCopyable.vue';
+const inputElement = ref();
const { t } = useI18n();
const definitions = ref(true);
const omitempty = ref(false);
@@ -30,6 +31,7 @@ const rules: UseValidationRule[] = [