Files
2026-09-22 19:28:51 +08:00

8.3 KiB
Raw Permalink Blame History

Excel Keyword Search Tool — Rust 版

跨 Excel / Word / CSV / 文本 文件批量关键字检索工具的 Rust 实现,包含两个交付物:

  • excel-search-rust.exe — 命令行搜索器(对标 Go 版 cmd/searcher),追求极致的单二进制体积与启动速度;
  • excel-search-rust-desktop.exe — 桌面版(对标 Go 版 cmd/desktop): 原生 egui/eframe 图形界面,单 exe 直接出窗口。不沿用 Go 版"HTTP 服务 + Edge 应用窗口" 的前后端分离架构——那是受 Go 生态限制的妥协,Rust 有更好选择: 关窗即退出(无心跳判活 hack)、搜索跑后台线程 UI 全程可交互(无 30s 服务端超时截断)、 结果表原生虚拟滚动(万级命中不卡顿,无分页条)。

WebUI(独立浏览器标签页)形态仍由 Go 版提供(见 ../go/README.md)。

特性

  • 并发扫描:rayon 线程池按文件并行解析,--workers 可配(默认 8,可用环境变量 EXCEL_SEARCH_WORKERS 覆盖)。
  • 匹配语义与 Go 版逐字节对齐:
    • 关键字与文本均做 Unicode NFC 规范化(cafe\u{301} == café);
    • 默认不区分大小写(Unicode 折叠);
    • 完整单词匹配默认关闭(模糊子串匹配);开启后要求关键字前后均为单词边界(字母/数字/组合标记/下划线之外)。
  • 文件格式:
    • Excel:.xlsx/.xlsm/.xltx/.xltm,并借助 calamine 额外支持 .xls/.xlsb/.ods(Go 版不含这三种);
    • Word:.docx/.docm(解析 word/document.xml,按段落定位 Paragraph N,表格段落一并覆盖);
    • 文本:.txt/.csv(按行定位 Line N,CSV 不解析字段,整行视为纯文本)。
  • 文件级缓存:与 Go 版相同的 JSON 结构({"version":2,"cache":{...}}), 按“路径 + 关键字 + ModTime + Size”判定命中;同一搜索根目录的缓存文件 (<cache_dir>/cache_kw_<sha256(绝对路径)前16字节>.json)可与 Go 版互换读写。 缓存目录由 EXCEL_SEARCH_CACHE_DIR 指定,默认运行目录下 .cache/。
  • 结果导出 CSV(桌面版):状态栏「📤 导出CSV」按钮弹出系统保存对话框选择位置; 所有列统一双引号包裹(RFC 4180,字段内 " 转义为 ""),文件带 UTF-8 BOM, Excel 直接打开中文不乱码。
  • 健壮性:单文件解析失败仅向 stderr 记 warn: 告警并跳过,不中断整次搜索; stdout 只输出结果,--json 可安全被管道消费。

目录结构

rust/
├── Cargo.toml               # 包定义:两个 [[bin]](CLI + desktop)+ 依赖声明(calamine/rayon/eframe/rfd 等)
├── Cargo.lock               # 依赖版本锁定(勿手改)
├── build.bat                # Windows 一键构建脚本(自动探测 mise/PATH 工具链与 gnu+mingw 免 VS 路径)
├── src/
│   ├── lib.rs               # 库入口(engine + error + history 模块导出)
│   ├── main.rs              # CLI(clap):--dir/--keyword/--json 等参数与结果打印
│   ├── error.rs             # 统一错误类型
│   ├── history.rs             # 搜索历史 LRU(与 Go 桌面版同格式共用文件)
│   ├── desktop/
│   │   └── main.rs            # 桌面版:原生 egui GUI(eframe);含 export_csv/quote_csv_field 导出逻辑与单测
│   └── engine/
│       ├── mod.rs           # 搜索编排:并行调度 + 缓存合并 + 选项/结果类型
│       ├── matcher.rs       # NFC + 大小写折叠 + 完整单词边界
│       ├── walker.rs        # 目录遍历与文件类型过滤
│       ├── parser.rs        # Excel(calamine) / Word(zip+quick-xml) / 文本 解析器
│       └── cache.rs         # 文件级 JSON 缓存(与 Go 版 schema 兼容)
└── tests/
    ├── fixtures/search_sample.xlsx   # 真实 xlsx 夹具(Go excelize 生成)
    └── search.rs                     # 集成测试(与 Go scanner_test 断言语义对应)

构建与测试

一键构建(Windows)

双击 rust\build.bat,或命令行:

build.bat            :: release 构建(默认)
build.bat debug      :: debug 构建
build.bat test       :: 运行全部测试

脚本自动探测工具链(优先 mise 托管的 cargo/rustup → 系统 PATH), 存在 GNU 工具链 + winlibs mingw 时自动走无 MSVC 编译路径,否则回退默认工具链并给出安装指引。

手动构建

cd rust
cargo test            # 单元(lib 11 + 桌面 3,含 CSV 引号包裹回归)+ 集成(引擎端到端 10)
cargo build --release # 产物(一次构建出两个 exe;LTO 全量构建约 2-20 分钟):
#   target/release/excel-search-rust.exe          命令行版 ≈1.3MB
#   target/release/excel-search-rust-desktop.exe  桌面版(原生 egui GUI)≈9.6MB

Windows 工具链说明:本机若无 Visual Studio Build Tools / Windows SDK, 默认 x86_64-pc-windows-msvc 目标无法链接,请改用 GNU 目标(winlibs 便携版即可,免安装):

rustup target add x86_64-pc-windows-gnu
# 将 winlibs-x86_64-posix-seh-*-ucrt 解压后将 mingw64\bin 加入 PATH,然后:
cargo test  --target x86_64-pc-windows-gnu
cargo build --release --target x86_64-pc-windows-gnu
# 产物:target/x86_64-pc-windows-gnu/release/ 下的两个 exe

用法

# 基本用法(默认:不区分大小写 + 模糊匹配 + 递归子目录 + 全部文件类型)
excel-search-rust --dir <目录> --keyword <关键字>
excel-search-rust <目录> <关键字>          # 等价的位置参数写法

# 输出 JSON(便于脚本处理)
excel-search-rust --dir ./data --keyword 关键词 --json

# 区分大小写
excel-search-rust --dir ./data --keyword Phone --case-sensitive=true

# 完整单词匹配(默认关闭,开启后要求关键字前后均为单词边界)
excel-search-rust --dir ./data --keyword 手机 --whole-word

# 仅搜索根目录
excel-search-rust --dir ./data --keyword 关键词 --include-subdirectories=false

# 文件类型过滤:all | excel | word | csv
excel-search-rust --dir ./data --keyword 关键词 --file-type excel

# 并发 worker 数(或环境变量 EXCEL_SEARCH_WORKERS)
excel-search-rust --dir ./data --keyword 关键词 --workers 16

完整单词边界提示:汉字属于字母,因此开启完整单词后搜「手机」不会命中 「苹果手机壳」这类内部子串;默认(关闭)为模糊匹配则会命中。

桌面版用法

# 双击运行,直接弹出原生窗口
excel-search-rust-desktop.exe

窗口布局与 Go 桌面版一致:目录/关键字(含历史下拉)、四个搜索选项、结果表格 (虚拟滚动、列宽可拖拽、右键复制/打开文件)、状态栏「导出CSV」按钮。差异是架构性的、也是更好的:

  • 关窗即退出:原生窗口生命周期与进程绑定,无心跳检测;
  • 搜索不阻塞界面:搜索在后台线程执行,进行中可继续拖动窗口/改表单; 慢目录不再被 30s 服务端超时掐断(Go 版行为);
  • 中文字体:运行时加载系统 SimHei/楷体/仿宋(epaint 不支持 .ttc 集合字体)。
环境变量 默认值 说明
EXCEL_SEARCH_WORKERS 8 并发搜索线程数
EXCEL_SEARCH_CACHE_DIR .cache 搜索缓存目录

搜索历史写入 .cache/search_history.json(与 Go 桌面版同格式,可共用同一份历史)。

与 Go 版的差异

维度 Go 版 Rust 版
交付形态 searcher CLI + WebUI + 桌面版(HTTP+Edge 窗口) CLI + 桌面版(原生 egui,无 WebUI)
Excel 格式 xlsx/xlsm/xltx/xltm 额外支持 xls/xlsb/ods
搜索取消/超时 context 全链路(服务端 600s 超时) CLI:Ctrl+C 直接终止;桌面:后台线程不阻塞 UI,无超时截断
缓存 JSON,版本 2 同 schema,条目可跨版本命中(已验证)
日志 slog(text/json,含请求日志中间件) CLI stderr warn: 行

设计文档(历史)

原始设计指南(体积优化 profile、calamine/rayon 选型)已随实现落地, 关键决策保留在上文;[profile.release] 的 opt-level="z"、lto、strip 配置见 Cargo.toml。