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[] = [