diff --git a/js-server/package.json b/js-server/package.json index 9b06379..0fae244 100644 --- a/js-server/package.json +++ b/js-server/package.json @@ -11,13 +11,15 @@ "license": "ISC", "packageManager": "pnpm@10.30.3", "dependencies": { - "@types/adm-zip": "^0.5.7", + "@fastify/mongodb": "^10.0.0", + "@fastify/static": "^9.0.0", "adm-zip": "^0.5.16", "dotenv": "^17.3.1", "fastify": "^5.7.4", "mongodb": "^7.1.0" }, "devDependencies": { + "@types/adm-zip": "^0.5.7", "@types/node": "^24.10.13", "tsx": "^4.21.0", "typescript": "~5.8.3" diff --git a/js-server/src/server.ts b/js-server/src/server.ts index e69de29..82890b6 100644 --- a/js-server/src/server.ts +++ b/js-server/src/server.ts @@ -0,0 +1,27 @@ +import fastify from "fastify"; +import { readdirSync } from "fs"; +import path from "path"; + +import fastifyStatic from "@fastify/static"; + +const app = fastify({ + logger: true, +}); + +const YOMITAN_PATH = path.join(__dirname, "../src-tauri/resources/yomitan/ja"); + +app.register(fastifyStatic, { + root: YOMITAN_PATH, + prefix: "/api/files/yomitan/ja", +}); + +app.get("/api/yomitan/ja/list", async () => { + return { + list: readdirSync(YOMITAN_PATH).filter((p) => p.endsWith(".zip")), + }; +}); + +app.listen({ port: 3000 }).catch((e) => { + app.log.error(e); + process.exit(1); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d70bd0..3a4a4c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,12 @@ importers: js-server: dependencies: - '@types/adm-zip': - specifier: ^0.5.7 - version: 0.5.7 + '@fastify/mongodb': + specifier: ^10.0.0 + version: 10.0.0 + '@fastify/static': + specifier: ^9.0.0 + version: 9.0.0 adm-zip: specifier: ^0.5.16 version: 0.5.16 @@ -30,6 +33,9 @@ importers: specifier: ^7.1.0 version: 7.1.0 devDependencies: + '@types/adm-zip': + specifier: ^0.5.7 + version: 0.5.7 '@types/node': specifier: ^24.10.13 version: 24.10.13 @@ -397,6 +403,9 @@ packages: cpu: [x64] os: [win32] + '@fastify/accept-negotiator@2.0.1': + resolution: {integrity: sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==} + '@fastify/ajv-compiler@4.0.5': resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} @@ -412,9 +421,18 @@ packages: '@fastify/merge-json-schemas@0.2.1': resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} + '@fastify/mongodb@10.0.0': + resolution: {integrity: sha512-BBkX8i3XjeQ2XJLLozVfYb2KF6boFqQf6UeX6f+LUWqbpnJbhD0euMqbTUiZkswgeyoydF09fq3eaCle6bQpIA==} + '@fastify/proxy-addr@5.1.0': resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} + '@fastify/send@4.1.0': + resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==} + + '@fastify/static@9.0.0': + resolution: {integrity: sha512-r64H8Woe/vfilg5RTy7lwWlE8ZZcTrc3kebYFMEUBrMqlydhQyoiExQXdYAy2REVpST/G35+stAM8WYp1WGmMA==} + '@floating-ui/core@1.7.4': resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} @@ -446,6 +464,10 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@lukeed/ms@2.0.2': + resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} + engines: {node: '>=8'} + '@mongodb-js/saslprep@1.4.6': resolution: {integrity: sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==} @@ -989,11 +1011,19 @@ packages: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + baseline-browser-mapping@2.10.0: resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} engines: {node: '>=6.0.0'} hasBin: true + brace-expansion@5.0.4: + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} + browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1010,6 +1040,10 @@ packages: caniuse-lite@1.0.30001770: resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} + content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} + convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -1036,6 +1070,10 @@ packages: supports-color: optional: true + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1059,6 +1097,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1078,6 +1119,9 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastify-plugin@5.1.0: + resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==} + fastify@5.7.4: resolution: {integrity: sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA==} @@ -1115,6 +1159,10 @@ packages: get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -1122,10 +1170,17 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ipaddr.js@2.3.0: resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} @@ -1165,12 +1220,29 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lru-cache@11.2.6: + resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + mongodb-connection-string-url@7.0.1: resolution: {integrity: sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==} engines: {node: '>=20.19.0'} @@ -1234,6 +1306,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -1363,6 +1439,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -1381,6 +1460,10 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} @@ -1400,6 +1483,10 @@ packages: resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} engines: {node: '>=12'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} @@ -1851,6 +1938,8 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true + '@fastify/accept-negotiator@2.0.1': {} + '@fastify/ajv-compiler@4.0.5': dependencies: ajv: 8.18.0 @@ -1869,11 +1958,41 @@ snapshots: dependencies: dequal: 2.0.3 + '@fastify/mongodb@10.0.0': + dependencies: + fastify-plugin: 5.1.0 + mongodb: 7.1.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + '@fastify/proxy-addr@5.1.0': dependencies: '@fastify/forwarded': 3.0.1 ipaddr.js: 2.3.0 + '@fastify/send@4.1.0': + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.1 + mime: 3.0.0 + + '@fastify/static@9.0.0': + dependencies: + '@fastify/accept-negotiator': 2.0.1 + '@fastify/send': 4.1.0 + content-disposition: 1.0.1 + fastify-plugin: 5.1.0 + fastq: 1.20.1 + glob: 13.0.6 + '@floating-ui/core@1.7.4': dependencies: '@floating-ui/utils': 0.2.10 @@ -1912,6 +2031,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@lukeed/ms@2.0.2': {} + '@mongodb-js/saslprep@1.4.6': dependencies: sparse-bitfield: 3.0.3 @@ -2705,8 +2826,14 @@ snapshots: cosmiconfig: 7.1.0 resolve: 1.22.11 + balanced-match@4.0.4: {} + baseline-browser-mapping@2.10.0: {} + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.10.0 @@ -2721,6 +2848,8 @@ snapshots: caniuse-lite@1.0.30001770: {} + content-disposition@1.0.1: {} + convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} @@ -2741,6 +2870,8 @@ snapshots: dependencies: ms: 2.1.3 + depd@2.0.0: {} + dequal@2.0.3: {} dotenv@17.3.1: {} @@ -2782,6 +2913,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} fast-decode-uri-component@1.0.1: {} @@ -2803,6 +2936,8 @@ snapshots: fast-uri@3.1.0: {} + fastify-plugin@5.1.0: {} + fastify@5.7.4: dependencies: '@fastify/ajv-compiler': 4.0.5 @@ -2848,6 +2983,12 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob@13.0.6: + dependencies: + minimatch: 10.2.4 + minipass: 7.1.3 + path-scurry: 2.0.2 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -2856,11 +2997,21 @@ snapshots: dependencies: react-is: 16.13.1 + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 + inherits@2.0.4: {} + ipaddr.js@2.3.0: {} is-arrayish@0.2.1: {} @@ -2891,12 +3042,22 @@ snapshots: lines-and-columns@1.2.4: {} + lru-cache@11.2.6: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 memory-pager@1.5.0: {} + mime@3.0.0: {} + + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.4 + + minipass@7.1.3: {} + mongodb-connection-string-url@7.0.1: dependencies: '@types/whatwg-url': 13.0.0 @@ -2934,6 +3095,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.2.6 + minipass: 7.1.3 + path-type@4.0.0: {} perfect-freehand@1.2.3: {} @@ -3064,6 +3230,8 @@ snapshots: set-cookie-parser@2.7.2: {} + setprototypeof@1.2.0: {} + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -3078,6 +3246,8 @@ snapshots: split2@4.2.0: {} + statuses@2.0.2: {} + stylis@4.2.0: {} supports-preserve-symlinks-flag@1.0.0: {} @@ -3093,6 +3263,8 @@ snapshots: toad-cache@3.7.0: {} + toidentifier@1.0.1: {} + tr46@5.1.1: dependencies: punycode: 2.3.1 diff --git a/src-core/examples/add_yomitan_dict_folder.rs b/src-core/examples/add_yomitan_dict_folder.rs index 9d4fb40..96e5af0 100644 --- a/src-core/examples/add_yomitan_dict_folder.rs +++ b/src-core/examples/add_yomitan_dict_folder.rs @@ -1,11 +1,10 @@ use std::{ fs::read_dir, path::{Path, absolute}, - time::Instant, }; use anyhow::{Context, Ok}; -use cjdic2_core::AppService; +use cjdic2_core::{AppService, Timer}; mod common; use common::get_db_dir; @@ -24,16 +23,14 @@ fn main() -> Result<(), anyhow::Error> { let p = e.path(); if p.extension().and_then(|s| s.to_str()) == Some("zip") { - let start = Instant::now(); + let _timer = Timer::new(format!("zip_file: {:?}", p)); - println!("zip_file: {:?}", p); println!( "{:?}", AppService::import_yomitan_zip_file(&mut writer, &p, "ja", |progress| { println!("{:?}", progress); })?, ); - println!("[{:.2?}]", start.elapsed()); } else { println!("not zip_file: {:?}", p); } diff --git a/src-core/examples/remove_one_yomitan_dict.rs b/src-core/examples/remove_one_yomitan_dict.rs index cd861e9..698d154 100644 --- a/src-core/examples/remove_one_yomitan_dict.rs +++ b/src-core/examples/remove_one_yomitan_dict.rs @@ -1,7 +1,5 @@ -use std::time::Instant; - use anyhow::Ok; -use cjdic2_core::AppService; +use cjdic2_core::{AppService, Timer}; mod common; use common::get_db_dir; @@ -9,10 +7,11 @@ use common::get_db_dir; fn main() -> Result<(), anyhow::Error> { let service = AppService::new(get_db_dir())?; - let start = Instant::now(); - let mut writer = service.get_yomitan_writer()?; - AppService::remove_yomitan_dictionary(&mut writer, "JMnedict")?; - println!("[{:.2?}]", start.elapsed()); + { + let _timer = Timer::new("remove one".to_string()); + let mut writer = service.get_yomitan_writer()?; + AppService::remove_yomitan_dictionary(&mut writer, "JMnedict")?; + } Ok(()) } diff --git a/src-core/examples/search_yomitan.rs b/src-core/examples/search_yomitan.rs index 5fa9e1a..12ea1b7 100644 --- a/src-core/examples/search_yomitan.rs +++ b/src-core/examples/search_yomitan.rs @@ -1,7 +1,5 @@ -use std::time::Instant; - use anyhow::Ok; -use cjdic2_core::AppService; +use cjdic2_core::{AppService, Timer}; mod common; use common::get_db_dir; @@ -9,11 +7,12 @@ use common::get_db_dir; fn main() -> Result<(), anyhow::Error> { let service = AppService::new(get_db_dir())?; - let start = Instant::now(); - let rs = service.search_yomitan("擦る", "する", 10, 0)?; - println!("{:#?}", rs); - println!("n={}", rs.len()); - println!("[{:.2?}]", start.elapsed()); + { + let _timer = Timer::new("Search".to_string()); + let rs = service.search_yomitan("擦る", "する", 10, 0)?; + println!("{:#?}", rs); + println!("n={}", rs.len()); + } Ok(()) } diff --git a/src-core/examples/yomitan_update_schema.rs b/src-core/examples/yomitan_update_schema.rs index 2e2d858..5e4683b 100644 --- a/src-core/examples/yomitan_update_schema.rs +++ b/src-core/examples/yomitan_update_schema.rs @@ -1,9 +1,5 @@ - - -use std::time::Instant; - use anyhow::Ok; -use cjdic2_core::AppService; +use cjdic2_core::{AppService, Timer}; mod common; use common::get_db_dir; @@ -11,9 +7,10 @@ use common::get_db_dir; fn main() -> Result<(), anyhow::Error> { let service = AppService::new(get_db_dir())?; - let start = Instant::now(); - service.get_yomitan_writer()?; - println!("[{:.2?}]", start.elapsed()); + { + let _timer = Timer::new("update schema".to_string()); + service.get_yomitan_writer()?; + } Ok(()) } diff --git a/src-core/src/db.rs b/src-core/src/db.rs index 0a7e396..5f4f7d2 100644 --- a/src-core/src/db.rs +++ b/src-core/src/db.rs @@ -38,6 +38,7 @@ impl Database { PRAGMA jornal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON; + PRAGMA cache_size = -131072; ", )?; @@ -81,6 +82,13 @@ impl Database { let uri = format!("file:{}?mode=ro&immutable=1", path); conn.execute("ATTACH DATABASE ?1 AS yomitan", [uri])?; + + conn.execute_batch( + r" + PRAGMA yomitan.mmap_size = 3000000000; -- 3_000_000_000 + ", + )?; + self.yomitan_attached.store(true, Ordering::Release); } diff --git a/src-core/src/db/yomitan.rs b/src-core/src/db/yomitan.rs index 058e811..c495a86 100644 --- a/src-core/src/db/yomitan.rs +++ b/src-core/src/db/yomitan.rs @@ -2,7 +2,7 @@ use rusqlite::{Result, params}; use serde::Serialize; use crate::{ - CJDicError, + CJDicError, Timer, db::{Database, yomitan_writer::normalize_term}, }; @@ -35,6 +35,8 @@ impl YomitanDatabase { limit: u32, offset: u32, ) -> Result, CJDicError> { + let _timer = Timer::new(format!("search_yomitan: {} {}", q_term, q_reading)); + let and_or = if q_term == q_reading { "OR" } else { "AND" }; let mut new_t = String::new(); diff --git a/src-core/src/db/yomitan_writer.rs b/src-core/src/db/yomitan_writer.rs index 43fe454..c3713af 100644 --- a/src-core/src/db/yomitan_writer.rs +++ b/src-core/src/db/yomitan_writer.rs @@ -7,11 +7,10 @@ use std::{ collections::HashMap, io::{Cursor, Read}, path::{Path, PathBuf}, - time::Instant, }; use zip::ZipArchive; -use crate::CJDicError; +use crate::{CJDicError, Timer}; fn blake3_hex(s: &str) -> String { format!("{}", hash(s.as_bytes())) @@ -274,8 +273,8 @@ impl YomitanWriter { Callback: Fn(YomitanZipImportProgress), { let bundle_name = zip_file.file_name(); + let _timer = Timer::new(format!("Importing {}", bundle_name)); - let start_time = Instant::now(); self.conn.execute_batch("PRAGMA foreign_keys = off")?; let cursor = Cursor::new(zip_file.bytes()?); @@ -593,12 +592,6 @@ impl YomitanWriter { tx.commit()?; - println!( - "Importing {} takes {:.2?}", - zip_file.file_name(), - start_time.elapsed() - ); - self.conn.execute_batch( " PRAGMA foreign_keys = on; diff --git a/src-core/src/lib.rs b/src-core/src/lib.rs index f330686..cb368c1 100644 --- a/src-core/src/lib.rs +++ b/src-core/src/lib.rs @@ -1,10 +1,12 @@ mod db; mod error; mod service; +mod util; pub use db::{YomitanRow, YomitanZipImportProgress, YomitanZipImportResult, ZipSource}; pub use error::CJDicError; pub use service::AppService; +pub use util::Timer; pub fn add(left: u64, right: u64) -> u64 { left + right diff --git a/src-core/src/util.rs b/src-core/src/util.rs new file mode 100644 index 0000000..e596615 --- /dev/null +++ b/src-core/src/util.rs @@ -0,0 +1,35 @@ +#[cfg(debug_assertions)] +use std::time::Instant; + +#[cfg(debug_assertions)] +pub struct Timer { + start: Instant, + label: String, +} + +#[cfg(debug_assertions)] +impl Timer { + pub fn new(label: String) -> Self { + Self { + start: Instant::now(), + label, + } + } +} + +#[cfg(debug_assertions)] +impl Drop for Timer { + fn drop(&mut self) { + println!("[{}] {:.2?}", self.label, self.start.elapsed()) + } +} + +#[cfg(not(debug_assertions))] +pub struct Timer; + +#[cfg(not(debug_assertions))] +impl Timer { + pub fn new(_label: String) -> Self { + Self + } +} diff --git a/src-react/src/pages/SearchPage.tsx b/src-react/src/pages/SearchPage.tsx index 9fc9b04..ce95179 100644 --- a/src-react/src/pages/SearchPage.tsx +++ b/src-react/src/pages/SearchPage.tsx @@ -45,6 +45,19 @@ function SearchPage() { const lang = "ja-JP"; + const isInit = useRef(false); + useEffect(() => { + if (isInit.current) return; + isInit.current = true; + + invoke("search_yomitan", { + qTerm: "", + qReading: "", + limit: 1, + offset: 0, + }); + }); + useEffect(() => { trySearch(); }, [q]); @@ -72,6 +85,12 @@ function SearchPage() { } async function runSearch(isNew = true) { + let norm_q = q.trim(); + if (isAutoKana) { + norm_q = norm_q.replace(/n$/i, "ん").replace(/[a-z]$/i, ""); + } + if (!norm_q) return; + const ender = /\p{Z}$/u.test(q) ? "" : "*"; const re = /\<(.+?)\>\[(.+?)\]/g; @@ -79,11 +98,6 @@ function SearchPage() { let qTerm = ""; let qReading = ""; - let norm_q = q.trim(); - if (isAutoKana) { - norm_q = norm_q.replace(/n$/i, "ん").replace(/[a-z]$/i, ""); - } - norm_q.split(re).map((s, i) => { switch (i % 3) { case 1: