From a200b554de85e9aa95f22aaf263f0950c01b1bdb Mon Sep 17 00:00:00 2001 From: tiny-craft <137850705+tiny-craft@users.noreply.github.com> Date: Tue, 4 Jul 2023 22:56:57 +0800 Subject: [PATCH] feat: add key and prefix reload refactor: separate update db tree node logic from open database --- backend/services/connection_service.go | 11 +- frontend/package-lock.json | 36 +- frontend/package.json | 12 +- frontend/package.json.md5 | 2 +- .../content_value/ContentToolbar.vue | 2 +- .../components/dialogs/ConnectionDialog.vue | 2 +- .../components/dialogs/PreferencesDialog.vue | 2 +- .../src/components/sidebar/BrowserTree.vue | 32 +- frontend/src/langs/en.json | 1 - frontend/src/langs/zh-cn.json | 1 - frontend/src/stores/connections.js | 207 +++-- frontend/src/stores/database.js | 857 ------------------ go.mod | 14 +- go.sum | 28 +- 14 files changed, 209 insertions(+), 998 deletions(-) delete mode 100644 frontend/src/stores/database.js diff --git a/backend/services/connection_service.go b/backend/services/connection_service.go index 9fe56cd..9a0144b 100644 --- a/backend/services/connection_service.go +++ b/backend/services/connection_service.go @@ -307,18 +307,27 @@ func (c *connectionService) parseDBItemInfo(info string) map[string]int { // OpenDatabase open select database, and list all keys // @param path contain connection name and db name func (c *connectionService) OpenDatabase(connName string, db int) (resp types.JSResp) { + return c.ScanKeys(connName, db, "*") +} + +// ScanKeys scan all keys below prefix +func (c *connectionService) ScanKeys(connName string, db int, prefix string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + if !strings.HasSuffix(prefix, "*") { + prefix += ":*" + } + //var keys []string keys := map[string]keyItem{} var cursor uint64 for { var loadedKey []string - loadedKey, cursor, err = rdb.Scan(ctx, cursor, "*", 10000).Result() + loadedKey, cursor, err = rdb.Scan(ctx, cursor, prefix, 10000).Result() if err != nil { resp.Msg = err.Error() return diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c62cbe3..9029b20 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,9 +10,9 @@ "dependencies": { "highlight.js": "^11.8.0", "lodash": "^4.17.21", - "pinia": "^2.1.3", - "sass": "^1.62.1", - "vue": "^3.2.37", + "pinia": "^2.1.4", + "sass": "^1.63.6", + "vue": "^3.3.4", "vue-i18n": "^9.2.2" }, "devDependencies": { @@ -20,9 +20,9 @@ "naive-ui": "^2.34.4", "prettier": "^2.8.8", "unplugin-auto-import": "^0.16.4", - "unplugin-icons": "^0.16.1", - "unplugin-vue-components": "^0.25.0", - "vite": "^4.3.0" + "unplugin-icons": "^0.16.3", + "unplugin-vue-components": "^0.25.1", + "vite": "^4.3.9" } }, "node_modules/@antfu/install-pkg": { @@ -1503,9 +1503,9 @@ } }, "node_modules/pinia": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.3.tgz", - "integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==", + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz", + "integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==", "dependencies": { "@vue/devtools-api": "^6.5.0", "vue-demi": ">=0.14.5" @@ -1655,9 +1655,9 @@ } }, "node_modules/sass": { - "version": "1.63.2", - "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.2.tgz", - "integrity": "sha512-u56TU0AIFqMtauKl/OJ1AeFsXqRHkgO7nCWmHaDwfxDo9GUMSqBA4NEh6GMuh1CYVM7zuROYtZrHzPc2ixK+ww==", + "version": "1.63.6", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.6.tgz", + "integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -3113,9 +3113,9 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "pinia": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.3.tgz", - "integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==", + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz", + "integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==", "requires": { "@vue/devtools-api": "^6.5.0", "vue-demi": ">=0.14.5" @@ -3212,9 +3212,9 @@ } }, "sass": { - "version": "1.63.2", - "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.2.tgz", - "integrity": "sha512-u56TU0AIFqMtauKl/OJ1AeFsXqRHkgO7nCWmHaDwfxDo9GUMSqBA4NEh6GMuh1CYVM7zuROYtZrHzPc2ixK+ww==", + "version": "1.63.6", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.6.tgz", + "integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==", "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", diff --git a/frontend/package.json b/frontend/package.json index acf4120..c66ac07 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,9 +11,9 @@ "dependencies": { "highlight.js": "^11.8.0", "lodash": "^4.17.21", - "pinia": "^2.1.3", - "sass": "^1.62.1", - "vue": "^3.2.37", + "pinia": "^2.1.4", + "sass": "^1.63.6", + "vue": "^3.3.4", "vue-i18n": "^9.2.2" }, "devDependencies": { @@ -21,8 +21,8 @@ "naive-ui": "^2.34.4", "prettier": "^2.8.8", "unplugin-auto-import": "^0.16.4", - "unplugin-icons": "^0.16.1", - "unplugin-vue-components": "^0.25.0", - "vite": "^4.3.0" + "unplugin-icons": "^0.16.3", + "unplugin-vue-components": "^0.25.1", + "vite": "^4.3.9" } } diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index eb075a8..3645e52 100755 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -da66eb9d13a7ace25f7f75d36c2510f9 \ No newline at end of file +e8efe46ff15777b1af82225bac5f4626 \ No newline at end of file diff --git a/frontend/src/components/content_value/ContentToolbar.vue b/frontend/src/components/content_value/ContentToolbar.vue index fc41e54..4ee13e9 100644 --- a/frontend/src/components/content_value/ContentToolbar.vue +++ b/frontend/src/components/content_value/ContentToolbar.vue @@ -56,7 +56,7 @@ const onDeleteKey = () => { diff --git a/frontend/src/components/dialogs/ConnectionDialog.vue b/frontend/src/components/dialogs/ConnectionDialog.vue index bf801f4..1051d2c 100644 --- a/frontend/src/components/dialogs/ConnectionDialog.vue +++ b/frontend/src/components/dialogs/ConnectionDialog.vue @@ -152,7 +152,7 @@ const onClose = () => { preset="dialog" transform-origin="center" > - + { preset="dialog" transform-origin="center" > - + -import { h, nextTick, onMounted, reactive, ref, watch } from 'vue' +import { h, nextTick, onMounted, reactive, ref } from 'vue' import { ConnectionType } from '../../consts/connection_type.js' import { NIcon, useDialog, useMessage } from 'naive-ui' import Key from '../icons/Key.vue' import ToggleDb from '../icons/ToggleDb.vue' -import { get, indexOf, size } from 'lodash' +import { indexOf, isEmpty } from 'lodash' import { useI18n } from 'vue-i18n' import Refresh from '../icons/Refresh.vue' import CopyLink from '../icons/CopyLink.vue' @@ -158,7 +158,20 @@ const onUpdateExpanded = (value, option, meta) => { } } -const onUpdateSelectedKeys = (keys, option) => { +const onUpdateSelectedKeys = (keys, options) => { + if (!isEmpty(options)) { + // prevent load duplicate key + for (const node of options) { + if (node.type === ConnectionType.RedisValue) { + const { key, name, db, redisKey } = node + if (indexOf(selectedKeys.value, key) === -1) { + connectionStore.loadKeyValue(name, db, redisKey) + } + break + } + } + } + selectedKeys.value = keys } @@ -210,13 +223,6 @@ const renderSuffix = ({ option }) => { const nodeProps = ({ option }) => { return { - onClick() { - const { key, name, db, type, redisKey } = option - if (option.type === ConnectionType.RedisValue) { - connectionStore.loadKeyValue(name, db, redisKey) - } - // console.log('[click]:' + JSON.stringify(option)) - }, onDblclick: async () => { if (loading.value) { console.warn('TODO: alert to ignore double click when loading') @@ -279,6 +285,12 @@ const handleSelectContextMenu = (key) => { case 'key_newkey': dialogStore.openNewKeyDialog(redisKey, name, db) break + case 'key_reload': + connectionStore.scanKeys(name, db, redisKey) + break + case 'value_reload': + connectionStore.loadKeyValue(name, db, redisKey) + break case 'key_remove': case 'value_remove': confirmDialog.warning(i18n.t('remove_tip', { name: redisKey }), () => { diff --git a/frontend/src/langs/en.json b/frontend/src/langs/en.json index 2dad811..cfddbf5 100644 --- a/frontend/src/langs/en.json +++ b/frontend/src/langs/en.json @@ -11,7 +11,6 @@ "disconnect_all": "Disconnect all connections", "filter": "Filter", "sort_conn": "Resort Connections", - "reload_key": "Reload Current Key", "close_confirm": "Confirm close this tab and connection", "opening_connection": "Opening Connection...", "remove_tip": "{type} \"{name}\" will be deleted", diff --git a/frontend/src/langs/zh-cn.json b/frontend/src/langs/zh-cn.json index 81e4e53..4d18b29 100644 --- a/frontend/src/langs/zh-cn.json +++ b/frontend/src/langs/zh-cn.json @@ -11,7 +11,6 @@ "disconnect_all": "断开所有连接", "filter": "筛选", "sort_conn": "调整连接顺序", - "reload_key": "重新载入此键内容", "close_confirm": "是否关闭当前连接", "opening_connection": "正在打开连接...", "remove_tip": "{type} \"{name}\" 将会被删除", diff --git a/frontend/src/stores/connections.js b/frontend/src/stores/connections.js index 807b574..f6ac0dd 100644 --- a/frontend/src/stores/connections.js +++ b/frontend/src/stores/connections.js @@ -1,5 +1,5 @@ import { defineStore } from 'pinia' -import { get, isEmpty, last, map, remove, size, sortedIndexBy, split, uniq } from 'lodash' +import { findIndex, get, isEmpty, last, map, remove, size, sortedIndexBy, split, uniq } from 'lodash' import { AddHashField, AddListItem, @@ -18,6 +18,7 @@ import { RenameKey, SaveConnection, SaveSortedConnection, + ScanKeys, SetHashValue, SetKeyTTL, SetKeyValue, @@ -389,78 +390,8 @@ const useConnectionStore = defineStore('connections', { return } - // insert child to children list by order - const sortedInsertChild = (childrenList, item) => { - const insertIdx = sortedIndexBy(childrenList, item, 'key') - childrenList.splice(insertIdx, 0, item) - // childrenList.push(item) - } - // update all node item's children num - const updateChildrenNum = (node) => { - let count = 0 - const totalChildren = size(node.children) - if (totalChildren > 0) { - for (const elem of node.children) { - updateChildrenNum(elem) - count += elem.keys - } - } else { - count += 1 - } - node.keys = count - // node.children = sortBy(node.children, 'label') - } - - const keyStruct = [] - const mark = {} - for (const key in keys) { - const keyPart = split(key, separator) - // const prefixLen = size(keyPart) - 1 - const len = size(keyPart) - let handlePath = '' - let ks = keyStruct - for (let i = 0; i < len; i++) { - handlePath += keyPart[i] - if (i !== len - 1) { - // layer - const treeKey = `${handlePath}@${ConnectionType.RedisKey}` - if (!mark.hasOwnProperty(treeKey)) { - mark[treeKey] = { - key: `${connName}/db${db}/${treeKey}`, - label: keyPart[i], - name: connName, - db, - keys: 0, - redisKey: handlePath, - type: ConnectionType.RedisKey, - children: [], - } - sortedInsertChild(ks, mark[treeKey]) - } - ks = mark[treeKey].children - handlePath += separator - } else { - // key - const treeKey = `${handlePath}@${ConnectionType.RedisValue}` - mark[treeKey] = { - key: `${connName}/db${db}/${treeKey}`, - label: keyPart[i], - name: connName, - db, - keys: 0, - redisKey: handlePath, - type: ConnectionType.RedisValue, - } - sortedInsertChild(ks, mark[treeKey]) - } - } - } - // append db node to current connection's children - const dbs = this.databases[connName] - dbs[db].children = keyStruct - dbs[db].opened = true - updateChildrenNum(dbs[db]) + this._updateNodeChildren(connName, db, keys) }, /** @@ -491,7 +422,127 @@ const useConnectionStore = defineStore('connections', { }, /** - * + * scan keys with prefix + * @param {string} connName + * @param {number} db + * @param {string} prefix + * @returns {Promise} + */ + async scanKeys(connName, db, prefix) { + const { data, success, msg } = await ScanKeys(connName, db, prefix) + if (!success) { + throw new Error(msg) + } + // remove current keys below prefix + const prefixPart = split(prefix, separator) + const dbs = this.databases[connName] + let node = dbs[db] + for (const key of prefixPart) { + const idx = findIndex(node.children, { label: key }) + if (idx === -1) { + node = null + break + } + node = node.children[idx] + } + if (node != null) { + node.children = [] + } + + const { keys = [] } = data + this._updateNodeChildren(connName, db, keys) + }, + + /** + * remove keys in db + * @param {string} connName + * @param {number} db + * @param {Object.[]} keys + * @private + */ + _updateNodeChildren(connName, db, keys) { + // find match key node in node list + const findNodeByKey = (nodes, key) => { + const idx = findIndex(nodes, { key }) + return idx !== -1 ? nodes[idx] : null + } + // insert child to children list by order + const sortedInsertChild = (childrenList, item) => { + const insertIdx = sortedIndexBy(childrenList, item, 'key') + childrenList.splice(insertIdx, 0, item) + // childrenList.push(item) + } + // update all node item's children num + const updateChildrenNum = (node) => { + let count = 0 + const totalChildren = size(node.children) + if (totalChildren > 0) { + for (const elem of node.children) { + updateChildrenNum(elem) + count += elem.keys + } + } else { + count += 1 + } + node.keys = count + // node.children = sortBy(node.children, 'label') + } + + const dbs = this.databases[connName] + if (dbs[db].children == null) { + dbs[db].children = [] + } + const keyStruct = dbs[db].children + for (const key in keys) { + const keyPart = split(key, separator) + // const prefixLen = size(keyPart) - 1 + const len = size(keyPart) + let handlePath = '' + let ks = keyStruct + for (let i = 0; i < len; i++) { + handlePath += keyPart[i] + if (i !== len - 1) { + // layer + const curKey = `${connName}/db${db}/${handlePath}@${ConnectionType.RedisKey}` + let selectedNode = findNodeByKey(ks, curKey) + if (selectedNode == null) { + selectedNode = { + key: curKey, + label: keyPart[i], + name: connName, + db, + keys: 0, + redisKey: handlePath, + type: ConnectionType.RedisKey, + children: [], + } + sortedInsertChild(ks, selectedNode) + } + ks = selectedNode.children + handlePath += separator + } else { + // key + const curKey = `${connName}/db${db}/${handlePath}@${ConnectionType.RedisValue}` + const selectedNode = { + key: curKey, + label: keyPart[i], + name: connName, + db, + keys: 0, + redisKey: handlePath, + type: ConnectionType.RedisValue, + } + sortedInsertChild(ks, selectedNode) + } + } + } + + dbs[db].opened = true + updateChildrenNum(dbs[db]) + }, + + /** + * add key to db * @param {string} connName * @param {number} db * @param {string} key @@ -521,9 +572,8 @@ const useConnectionStore = defineStore('connections', { for (let j = 0; j < len + 1; j++) { const treeKey = get(nodeList[j], 'key') const isLast = j >= len - 1 - const currentKey = `${connName}/db${db}/${redisKey}@${ - isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey - }` + const keyType = isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey + const currentKey = `${connName}/db${db}/${redisKey}@${keyType}` if (treeKey > currentKey || isLast) { // out of search range, add new item if (isLastKeyPart) { @@ -981,9 +1031,8 @@ const useConnectionStore = defineStore('connections', { const isLastKeyPart = i === keyLen - 1 for (let j = 0; j < len; j++) { const treeKey = get(nodeList[j], 'key') - const currentKey = `${connName}/db${db}/${redisKey}@${ - isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey - }` + const keyType = isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey + const currentKey = `${connName}/db${db}/${redisKey}@${keyType}` if (treeKey > currentKey) { // out of search range, target not exists forceBreak = true diff --git a/frontend/src/stores/database.js b/frontend/src/stores/database.js deleted file mode 100644 index 75549d8..0000000 --- a/frontend/src/stores/database.js +++ /dev/null @@ -1,857 +0,0 @@ -import { get, isEmpty, last, remove, size, sortedIndexBy, split } from 'lodash' -import { defineStore } from 'pinia' -import { - AddHashField, - AddListItem, - AddZSetValue, - CloseConnection, - GetKeyValue, - OpenConnection, - OpenDatabase, - RemoveKey, - RenameKey, - SetHashValue, - SetKeyTTL, - SetKeyValue, - SetListItem, - SetSetItem, - UpdateSetItem, - UpdateZSetValue, -} from '../../wailsjs/go/services/connectionService.js' -import { ConnectionType } from '../consts/connection_type.js' -import useTabStore from './tab.js' -import useConnectionStore from './connections.js' - -const separator = ':' - -const useDatabaseStore = defineStore('database', { - /** - * @typedef {Object} DatabaseItem - * @property {string} key - * @property {string} label - * @property {string} name - server name, type != ConnectionType.Group only - * @property {number} type - * @property {number} [db] - database index, type == ConnectionType.RedisDB only - * @property {number} keys - * @property {boolean} [connected] - redis server is connected, type == ConnectionType.Server only - * @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only - * @property {boolean} [expanded] - current node is expanded - */ - - /** - * - * @returns {{connections: DatabaseItem[]}} - */ - state: () => ({ - connections: [], // all connections list - databases: {}, // all database group by opened connections - }), - getters: {}, - actions: { - /** - * Open connection - * @param {string} connName - * @returns {Promise} - */ - async openConnection(connName) { - const { data, success, msg } = await OpenConnection(connName) - if (!success) { - throw new Error(msg) - } - // append to db node to current connection - const connStore = useConnectionStore() - const connNode = connStore.getConnection(connName) - if (connNode == null) { - throw new Error('no such connection') - } - const { db } = data - if (isEmpty(db)) { - throw new Error('no db loaded') - } - const children = [] - for (let i = 0; i < db.length; i++) { - children.push({ - key: `${connName}/${db[i].name}`, - label: db[i].name, - name: connName, - keys: db[i].keys, - db: i, - type: ConnectionType.RedisDB, - // isLeaf: false, - }) - } - connNode.children = children - connNode.connected = true - }, - - /** - * Close connection - * @param {string} connName - * @returns {Promise} - */ - async closeConnection(connName) { - const { success, msg } = await CloseConnection(connName) - if (!success) { - // throw new Error(msg) - return false - } - - // successful close connection, remove all children - const connNode = this.getConnection(connName) - if (connNode == null) { - // throw new Error('no such connection') - return false - } - connNode.children = undefined - connNode.isLeaf = undefined - connNode.connected = false - connNode.expanded = false - return true - }, - - /** - * Get Connection by path name - * @param {string} connName - * @returns - */ - getConnection(connName) { - const conn = this.connections - for (let i = 0; i < conn.length; i++) { - if (conn[i].type === ConnectionType.Server && conn[i].key === connName) { - return conn[i] - } else if (conn[i].type === ConnectionType.Group) { - const children = conn[i].children - for (let j = 0; j < children.length; j++) { - if (children[j].type === ConnectionType.Server && conn[i].key === connName) { - return children[j] - } - } - } - } - return null - }, - - /** - * Open database and load all keys - * @param connName - * @param db - * @returns {Promise} - */ - async openDatabase(connName, db) { - const { data, success, msg } = await OpenDatabase(connName, db) - if (!success) { - throw new Error(msg) - } - const { keys = [] } = data - if (isEmpty(keys)) { - const connNode = this.getConnection(connName) - const { children = [] } = connNode - children[db].children = [] - children[db].opened = true - return - } - - // insert child to children list by order - const sortedInsertChild = (childrenList, item) => { - const insertIdx = sortedIndexBy(childrenList, item, 'key') - childrenList.splice(insertIdx, 0, item) - // childrenList.push(item) - } - // update all node item's children num - const updateChildrenNum = (node) => { - let count = 0 - const totalChildren = size(node.children) - if (totalChildren > 0) { - for (const elem of node.children) { - updateChildrenNum(elem) - count += elem.keys - } - } else { - count += 1 - } - node.keys = count - // node.children = sortBy(node.children, 'label') - } - - const keyStruct = [] - const mark = {} - for (const key in keys) { - const keyPart = split(key, separator) - // const prefixLen = size(keyPart) - 1 - const len = size(keyPart) - let handlePath = '' - let ks = keyStruct - for (let i = 0; i < len; i++) { - handlePath += keyPart[i] - if (i !== len - 1) { - // layer - const treeKey = `${handlePath}@${ConnectionType.RedisKey}` - if (!mark.hasOwnProperty(treeKey)) { - mark[treeKey] = { - key: `${connName}/db${db}/${treeKey}`, - label: keyPart[i], - name: connName, - db, - keys: 0, - redisKey: handlePath, - type: ConnectionType.RedisKey, - children: [], - } - sortedInsertChild(ks, mark[treeKey]) - } - ks = mark[treeKey].children - handlePath += separator - } else { - // key - const treeKey = `${handlePath}@${ConnectionType.RedisValue}` - mark[treeKey] = { - key: `${connName}/db${db}/${treeKey}`, - label: keyPart[i], - name: connName, - db, - keys: 0, - redisKey: handlePath, - type: ConnectionType.RedisValue, - } - sortedInsertChild(ks, mark[treeKey]) - } - } - } - - // append db node to current connection's children - const connNode = this.getConnection(connName) - const { children = [] } = connNode - children[db].children = keyStruct - children[db].opened = true - updateChildrenNum(children[db]) - }, - - /** - * select node - * @param key - * @param name - * @param db - * @param type - * @param redisKey - */ - select({ key, name, db, type, redisKey }) { - if (type === ConnectionType.RedisValue) { - console.log(`[click]key:${key} db: ${db} redis key: ${redisKey}`) - - // async get value for key - this.loadKeyValue(name, db, redisKey).then(() => {}) - } - }, - - /** - * load redis key - * @param server - * @param db - * @param key - */ - async loadKeyValue(server, db, key) { - try { - const { data, success, msg } = await GetKeyValue(server, db, key) - if (success) { - const { type, ttl, value } = data - const tab = useTabStore() - tab.upsertTab({ - server, - db, - type, - ttl, - key, - value, - }) - } else { - console.warn('TODO: handle get key fail') - } - } finally { - } - }, - - /** - * - * @param {string} connName - * @param {number} db - * @param {string} key - * @private - */ - _addKey(connName, db, key) { - const connNode = this.getConnection(connName) - const { children: dbs = [] } = connNode - const dbDetail = get(dbs, db, {}) - - if (dbDetail == null) { - return - } - - const descendantChain = [dbDetail] - - const keyPart = split(key, separator) - let redisKey = '' - const keyLen = size(keyPart) - let added = false - for (let i = 0; i < keyLen; i++) { - redisKey += keyPart[i] - - const node = last(descendantChain) - const nodeList = get(node, 'children', []) - const len = size(nodeList) - const isLastKeyPart = i === keyLen - 1 - for (let j = 0; j < len + 1; j++) { - const treeKey = get(nodeList[j], 'key') - const isLast = j >= len - 1 - const currentKey = `${connName}/db${db}/${redisKey}@${ - isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey - }` - if (treeKey > currentKey || isLast) { - // out of search range, add new item - if (isLastKeyPart) { - // key not exists, add new one - const item = { - key: currentKey, - label: keyPart[i], - name: connName, - db, - keys: 1, - redisKey, - type: ConnectionType.RedisValue, - } - if (isLast) { - nodeList.push(item) - } else { - nodeList.splice(j, 0, item) - } - added = true - } else { - // layer not exists, add new one - const item = { - key: currentKey, - label: keyPart[i], - name: connName, - db, - keys: 0, - redisKey, - type: ConnectionType.RedisKey, - children: [], - } - if (isLast) { - nodeList.push(item) - descendantChain.push(last(nodeList)) - } else { - nodeList.splice(j, 0, item) - descendantChain.push(nodeList[j]) - } - redisKey += separator - added = true - } - break - } else if (treeKey === currentKey) { - if (isLastKeyPart) { - // same key exists, do nothing - console.log('TODO: same key exist, do nothing now, should replace value later') - } else { - // same group exists, find into it's children - descendantChain.push(nodeList[j]) - redisKey += separator - } - break - } - } - } - - // update ancestor node's info - if (added) { - const desLen = size(descendantChain) - for (let i = 0; i < desLen; i++) { - const children = get(descendantChain[i], 'children', []) - let keys = 0 - for (const child of children) { - if (child.type === ConnectionType.RedisKey) { - keys += get(child, 'keys', 1) - } else if (child.type === ConnectionType.RedisValue) { - keys += get(child, 'keys', 0) - } - } - descendantChain[i].keys = keys - } - } - }, - - /** - * set redis key - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {number} keyType - * @param {any} value - * @param {number} ttl - * @returns {Promise<{[msg]: string, success: boolean}>} - */ - async setKey(connName, db, key, keyType, value, ttl) { - try { - const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl) - if (success) { - // update tree view data - this._addKey(connName, db, key) - return { success } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * update hash field - * when field is set, newField is null, delete field - * when field is null, newField is set, add new field - * when both field and newField are set, and field === newField, update field - * when both field and newField are set, and field !== newField, delete field and add newField - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {string} field - * @param {string} newField - * @param {string} value - * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} - */ - async setHash(connName, db, key, field, newField, value) { - try { - const { data, success, msg } = await SetHashValue(connName, db, key, field, newField || '', value || '') - if (success) { - const { updated = {} } = data - return { success, updated } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * insert or update hash field item - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {number }action 0:ignore duplicated fields 1:overwrite duplicated fields - * @param {string[]} fieldItems field1, value1, filed2, value2... - * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} - */ - async addHashField(connName, db, key, action, fieldItems) { - try { - const { data, success, msg } = await AddHashField(connName, db, key, action, fieldItems) - if (success) { - const { updated = {} } = data - return { success, updated } - } else { - return { success: false, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * remove hash field - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {string} field - * @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>} - */ - async removeHashField(connName, db, key, field) { - try { - const { data, success, msg } = await SetHashValue(connName, db, key, field, '', '') - if (success) { - const { removed = [] } = data - return { success, removed } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * insert list item - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {int} action 0: push to head, 1: push to tail - * @param {string[]}values - * @returns {Promise<*|{msg, success: boolean}>} - */ - async addListItem(connName, db, key, action, values) { - try { - return AddListItem(connName, db, key, action, values) - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * prepend item to head of list - * @param connName - * @param db - * @param key - * @param values - * @returns {Promise<[msg]: string, success: boolean, [item]: []>} - */ - async prependListItem(connName, db, key, values) { - try { - const { data, success, msg } = await AddListItem(connName, db, key, 0, values) - if (success) { - const { left = [] } = data - return { success, item: left } - } else { - return { success: false, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * append item to tail of list - * @param connName - * @param db - * @param key - * @param values - * @returns {Promise<[msg]: string, success: boolean, [item]: any[]>} - */ - async appendListItem(connName, db, key, values) { - try { - const { data, success, msg } = await AddListItem(connName, db, key, 1, values) - if (success) { - const { right = [] } = data - return { success, item: right } - } else { - return { success: false, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * update value of list item by index - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {number} index - * @param {string} value - * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} - */ - async updateListItem(connName, db, key, index, value) { - try { - const { data, success, msg } = await SetListItem(connName, db, key, index, value) - if (success) { - const { updated = {} } = data - return { success, updated } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * remove list item - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {number} index - * @returns {Promise<{[msg]: string, success: boolean, [removed]: string[]}>} - */ - async removeListItem(connName, db, key, index) { - try { - const { data, success, msg } = await SetListItem(connName, db, key, index, '') - if (success) { - const { removed = [] } = data - return { success, removed } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * add item to set - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {string} value - * @returns {Promise<{[msg]: string, success: boolean}>} - */ - async addSetItem(connName, db, key, value) { - try { - const { success, msg } = await SetSetItem(connName, db, key, false, [value]) - if (success) { - return { success } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * update value of set item - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {string} value - * @param {string} newValue - * @returns {Promise<{[msg]: string, success: boolean}>} - */ - async updateSetItem(connName, db, key, value, newValue) { - try { - const { success, msg } = await UpdateSetItem(connName, db, key, value, newValue) - if (success) { - return { success: true } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * remove item from set - * @param connName - * @param db - * @param key - * @param value - * @returns {Promise<{[msg]: string, success: boolean}>} - */ - async removeSetItem(connName, db, key, value) { - try { - const { success, msg } = await SetSetItem(connName, db, key, true, [value]) - if (success) { - return { success } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * add item to sorted set - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {number} action - * @param {Object.} vs value: score - * @returns {Promise<{[msg]: string, success: boolean}>} - */ - async addZSetItem(connName, db, key, action, vs) { - try { - const { success, msg } = await AddZSetValue(connName, db, key, action, vs) - if (success) { - return { success } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * update item of sorted set - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {string} value - * @param {string} newValue - * @param {number} score - * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}, [removed]: []}>} - */ - async updateZSetItem(connName, db, key, value, newValue, score) { - try { - const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, newValue, score) - if (success) { - const { updated, removed } = data - return { success, updated, removed } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * remove item from sorted set - * @param {string} connName - * @param {number} db - * @param key - * @param {string} value - * @returns {Promise<{[msg]: string, success: boolean, [removed]: []}>} - */ - async removeZSetItem(connName, db, key, value) { - try { - const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, '', 0) - if (success) { - const { removed } = data - return { success, removed } - } else { - return { success, msg } - } - } catch (e) { - return { success: false, msg: e.message } - } - }, - - /** - * reset key's ttl - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {number} ttl - * @returns {Promise} - */ - async setTTL(connName, db, key, ttl) { - try { - const { success, msg } = await SetKeyTTL(connName, db, key, ttl) - return success === true - } catch (e) { - return false - } - }, - - /** - * - * @param {string} connName - * @param {number} db - * @param {string} key - * @private - */ - _removeKey(connName, db, key) { - const connNode = this.getConnection(connName) - const { children: dbs = [] } = connNode - const dbDetail = get(dbs, db, {}) - - if (dbDetail == null) { - return - } - - const descendantChain = [dbDetail] - const keyPart = split(key, separator) - let redisKey = '' - const keyLen = size(keyPart) - let deleted = false - let forceBreak = false - for (let i = 0; i < keyLen && !forceBreak; i++) { - redisKey += keyPart[i] - - const node = last(descendantChain) - const nodeList = get(node, 'children', []) - const len = size(nodeList) - const isLastKeyPart = i === keyLen - 1 - for (let j = 0; j < len; j++) { - const treeKey = get(nodeList[j], 'key') - const currentKey = `${connName}/db${db}/${redisKey}@${ - isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey - }` - if (treeKey > currentKey) { - // out of search range, target not exists - forceBreak = true - break - } else if (treeKey === currentKey) { - if (isLastKeyPart) { - // find target - nodeList.splice(j, 1) - node.keys -= 1 - deleted = true - forceBreak = true - } else { - // find into it's children - descendantChain.push(nodeList[j]) - redisKey += separator - } - break - } - } - - if (forceBreak) { - break - } - } - // console.log(JSON.stringify(descendantChain)) - - // update ancestor node's info - if (deleted) { - const desLen = size(descendantChain) - for (let i = desLen - 1; i > 0; i--) { - const children = get(descendantChain[i], 'children', []) - const parent = descendantChain[i - 1] - if (isEmpty(children)) { - const parentChildren = get(parent, 'children', []) - const k = get(descendantChain[i], 'key') - remove(parentChildren, (item) => item.key === k) - } - parent.keys -= 1 - } - } - }, - - /** - * remove redis key - * @param {string} connName - * @param {number} db - * @param {string} key - * @returns {Promise} - */ - async removeKey(connName, db, key) { - try { - const { data, success, msg } = await RemoveKey(connName, db, key) - if (success) { - // update tree view data - this._removeKey(connName, db, key) - - // set tab content empty - const tab = useTabStore() - tab.emptyTab(connName) - return true - } - } finally { - } - return false - }, - - /** - * rename key - * @param {string} connName - * @param {number} db - * @param {string} key - * @param {string} newKey - * @returns {Promise<{[msg]: string, success: boolean}>} - */ - async renameKey(connName, db, key, newKey) { - const { success = false, msg } = await RenameKey(connName, db, key, newKey) - if (success) { - // delete old key and add new key struct - this._removeKey(connName, db, key) - this._addKey(connName, db, newKey) - return { success: true } - } else { - return { success: false, msg } - } - }, - }, -}) - -export default useDatabaseStore diff --git a/go.mod b/go.mod index 8ef2ca9..9dace25 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module tinyrdm go 1.20 require ( - github.com/bytedance/sonic v1.9.1 + github.com/bytedance/sonic v1.9.2 github.com/google/go-cmp v0.5.9 github.com/redis/go-redis/v9 v9.0.5 github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68 @@ -22,7 +22,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/labstack/echo/v4 v4.10.2 // indirect github.com/labstack/gommon v0.4.0 // indirect - github.com/leaanthony/go-ansi-parser v1.6.0 // indirect + github.com/leaanthony/go-ansi-parser v1.6.1 // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.6.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -38,11 +38,11 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.9.0 // indirect - golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/crypto v0.10.0 // indirect + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect + golang.org/x/net v0.11.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect ) // replace github.com/wailsapp/wails/v2 v2.5.1 => /Users/lykin/go/pkg/mod diff --git a/go.sum b/go.sum index 1214053..f23eb44 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3IS github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM= +github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= @@ -32,8 +32,8 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= -github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg= -github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= +github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A= +github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ= github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= @@ -88,13 +88,13 @@ github.com/wailsapp/wails/v2 v2.5.1/go.mod h1:jbOZbcr/zm79PxXxAjP8UoVlDd9wLW3uDs golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -106,12 +106,12 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=