Skip to content

Commit 97bcade

Browse files
committed
Add atomic worksheet renaming with .NET parity
1 parent fcc2a1a commit 97bcade

11 files changed

Lines changed: 485 additions & 22 deletions

File tree

‎.github/workflows/rust.yml‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,37 @@ jobs:
225225
--filter "FullyQualifiedName~MiniExcelOpenXmlImporterAsyncTests"
226226
--verbosity minimal
227227
228+
rename-sheet-parity:
229+
strategy:
230+
matrix:
231+
os: [ubuntu-latest, windows-latest]
232+
runs-on: ${{ matrix.os }}
233+
steps:
234+
- uses: actions/checkout@v4
235+
- name: Checkout MiniExcel .NET
236+
uses: actions/checkout@v4
237+
with:
238+
repository: mini-software/MiniExcel
239+
ref: b9a76d7af62142e0e38545b6905b01a06e8d160e
240+
path: dotnet-source
241+
- name: Install Rust 1.85.0
242+
uses: dtolnay/rust-toolchain@1.85.0
243+
- uses: Swatinem/rust-cache@v2
244+
with:
245+
key: rename-sheet-parity-${{ matrix.os }}
246+
- name: Setup .NET
247+
uses: actions/setup-dotnet@v4
248+
with:
249+
dotnet-version: 10.0.x
250+
- name: Run Rust worksheet rename tests
251+
run: cargo +1.85.0 test -p miniexcel --test rename_sheet --locked
252+
- name: Run MiniExcel .NET AlterSheet baseline tests
253+
run: >-
254+
dotnet test dotnet-source/tests/MiniExcel.OpenXml.Tests/MiniExcel.OpenXml.Tests.csproj
255+
--framework net10.0
256+
--filter "FullyQualifiedName~MiniExcelAlterSheetTests|FullyQualifiedName~MiniExcelAlterSheetsTestAsync"
257+
--verbosity minimal
258+
228259
msrv:
229260
runs-on: ubuntu-latest
230261
steps:

‎README.md‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,19 @@ Insert holds a cross-process advisory lock and verifies a source SHA-256 fingerp
419419
concurrent writers or external source changes return a deterministic conflict instead of silently
420420
overwriting newer content.
421421

422+
Rename an existing worksheet without changing its package identity or position:
423+
424+
```rust
425+
MiniExcel::rename_sheet("book.xlsx", "Sheet1", "Archive")?;
426+
```
427+
428+
Source matching is case-insensitive, including case-only renames. The path update is atomic and
429+
uses the same lock, fingerprint, package validation, permission preservation, and raw ZIP copy
430+
pipeline as Insert. Duplicate and invalid target names are rejected before commit. Worksheet
431+
relationships, IDs, order, visibility, active state, formulas, and defined names are preserved.
432+
Like .NET `AlterSheet`, formula and defined-name text that references the old sheet name is not
433+
rewritten; callers must update those references separately.
434+
422435
Enable the optional `async` feature to feed an existing-workbook Insert from a runtime-neutral
423436
`Stream<Item = miniexcel::Result<DynamicRow>>`:
424437

@@ -517,10 +530,10 @@ Version 1 does not implement `@group`, `@if`, parametrized sheet cloning, `$=` f
517530
- Grouped analytics retain state proportional to distinct groups and stop at `max_groups`.
518531
- RAG exports never recalculate formulas and reject hidden sheets unless explicitly allowed.
519532
- Synchronous streaming queries use one worker thread per active query. Optional async query and Insert APIs use bounded channels around blocking XLSX workers; ZIP/XML/filesystem work is not async I/O.
520-
- Save creates new workbooks and refuses existing target paths by default. `MiniExcel::insert*()` atomically appends or strictly replaces a worksheet in an existing `.xlsx` path, or creates a workbook when the path is missing.
533+
- Save creates new workbooks and refuses existing target paths by default. `MiniExcel::insert*()` atomically appends or strictly replaces a worksheet in an existing `.xlsx` path, or creates a workbook when the path is missing. `rename_sheet()` atomically changes only existing workbook sheet metadata.
521534

522535
## Not Supported
523536

524-
`.xls`, `.xlsb`, `.ods`, advanced template directives, macros, images, merged-cell operations, formula authoring, a general style system, and arbitrary worksheet copying/renaming/reordering are not currently supported.
537+
`.xls`, `.xlsb`, `.ods`, advanced template directives, macros, images, merged-cell operations, formula authoring, a general style system, and arbitrary worksheet copying/reordering/visibility mutation are not currently supported.
525538

526539
See the [compatibility matrix](docs/compatibility.md) for the current support scope and the [MiniExcel v1 Insert migration guide](docs/insert-v1-migration.md) for deliberate differences.

‎README.zh-CN.md‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,18 @@ XML depth 与 relationship count;同时拒绝不安全或别名 part path、
413413
lock,并在 commit 前校验 source SHA-256 fingerprint;并发 writer 或外部 source 变化会返回
414414
确定性 conflict,不会静默覆盖更新后的内容。
415415

416+
可以在不改变 package identity 或位置的情况下重命名现有 worksheet:
417+
418+
```rust
419+
MiniExcel::rename_sheet("book.xlsx", "Sheet1", "Archive")?;
420+
```
421+
422+
Source 按大小写不敏感匹配,也支持只改变大小写。Path 更新具备原子性,并与 Insert 共用
423+
lock、fingerprint、package validation、permission preservation 和 raw ZIP copy pipeline。
424+
Duplicate/非法 target name 会在 commit 前被拒绝。Worksheet relationship、ID、顺序、
425+
visibility、active state、formula 和 defined name 均保留。与 .NET `AlterSheet` 一样,引用旧
426+
sheet name 的 formula/defined-name 文本不会自动重写;调用方必须另行更新这些引用。
427+
416428
启用可选 `async` feature 后,可通过 runtime-neutral
417429
`Stream<Item = miniexcel::Result<DynamicRow>>` 为现有 workbook 的 Insert 提供 row:
418430

@@ -510,10 +522,10 @@ MiniExcel::save_as_template(
510522
- 分组分析保留与不同 group 数量成比例的状态,并在 `max_groups` 停止。
511523
- RAG 导出不会重新计算公式,hidden sheet 未显式允许时会拒绝处理。
512524
- 同步流式 query 每个活动 query 使用一个 worker thread。可选 async query/Insert API 通过 bounded channel 包装 blocking XLSX worker;ZIP/XML/filesystem 工作并不是 async I/O。
513-
- Save 会创建新工作簿,并默认拒绝已有目标路径。`MiniExcel::insert*()` 会向现有 `.xlsx` path 原子 append 或严格 replace worksheet;路径不存在时则创建 workbook。
525+
- Save 会创建新工作簿,并默认拒绝已有目标路径。`MiniExcel::insert*()` 会向现有 `.xlsx` path 原子 append 或严格 replace worksheet;路径不存在时则创建 workbook。`rename_sheet()` 只原子修改现有 workbook 的 sheet metadata。
514526

515527
## 暂不支持
516528

517-
目前不支持 `.xls`、`.xlsb`、`.ods`、高级模板指令、宏、图片、合并单元格操作、公式写入、通用样式系统,以及任意 worksheet copy/rename/reorder。
529+
目前不支持 `.xls`、`.xlsb`、`.ods`、高级模板指令、宏、图片、合并单元格操作、公式写入、通用样式系统,以及任意 worksheet copy/reorder/visibility mutation。
518530

519531
当前支持范围请查看[兼容性矩阵](docs/compatibility.zh-CN.md);有意保留的差异见 [MiniExcel v1 Insert 迁移说明](docs/insert-v1-migration.zh-CN.md)。

‎docs/compatibility.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ The contract covers only the current common surface: dynamic/typed path queries,
187187
| DataReader and broader stream ownership | Deferred | No |
188188
| Append worksheet to existing `.xlsx` workbook | Implemented and atomically committed | Rust tests; shared parity contract not yet extended |
189189
| Strict worksheet replacement | Implemented for plain targets and supported target-owned closures | Rust tests; stale calcChain removed and full recalculation requested |
190+
| Atomic worksheet rename | Implemented for existing `.xlsx` paths | Rust package-preservation tests; pinned .NET `AlterSheet` baseline |
190191
| Legacy `.xls`, `.xlsb`, and `.ods` formats | Deferred | No |
192+
| Worksheet copy/reorder/visibility mutation | Deferred | No |
191193
| Advanced templates, pictures, and merges | Deferred | No |
192194

193195
This matrix is the coverage claim: Rust does not yet provide complete API parity with the current .NET packages.

‎docs/compatibility.zh-CN.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ Rust workflow 会在 Linux 和 Windows 上运行 Rust 契约。其 .NET parity j
187187
| DataReader 与更广泛的 stream ownership | 延后 | 否 |
188188
| 向现有 `.xlsx` workbook 追加 worksheet | 已实现并原子提交 | Rust 测试;共享 parity contract 尚未扩展 |
189189
| 严格 worksheet replacement | 已支持 plain target 与受支持的 target-owned closure | Rust 测试;删除 stale calcChain 并要求 full recalculation |
190+
| 原子 worksheet rename | 已支持现有 `.xlsx` path | Rust package-preservation 测试;固定 .NET `AlterSheet` 基准 |
190191
| 旧式 `.xls`、`.xlsb` 与 `.ods` 格式 | 延后 | 否 |
192+
| Worksheet copy/reorder/visibility mutation | 延后 | 否 |
191193
| 高级 template、picture 与 merge | 延后 | 否 |
192194

193195
此矩阵就是覆盖声明:Rust 目前尚未提供与当前 .NET package 完整的 API 等价性。

‎docs/dotnet-feature-gaps.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This report compares observable public capabilities in the local checkouts below
88

99
| Project | Revision |
1010
| --- | --- |
11-
| MiniExcel-Rust | working tree based on `b166b708606a2046b62eefafa7893944bb3a2251` (`0.3.0`) |
11+
| MiniExcel-Rust | working tree based on `fcc2a1a` (`0.3.0`) |
1212
| MiniExcel .NET | `b9a76d7af62142e0e38545b6905b01a06e8d160e` |
1313

1414
The comparison uses the .NET public APIs, their controlling implementations, and focused tests under the sibling `../MiniExcel` checkout. Rust status is based on the public `MiniExcel` facade, options, integration tests, and [compatibility boundary](compatibility.md).
@@ -30,11 +30,11 @@ Rust already implements dynamic and Serde-typed XLSX path queries, inclusive A1
3030
| --- | --- | --- |
3131
| Named tables | Implemented | Dynamic/typed path queries, byte queries, and borrowed-reader visitors use table metadata headers and bounds with case-insensitive table-name matching. |
3232
| DataReader and DataTable | Different by design | Rust exposes iterators and borrowed visitors instead of .NET tabular interfaces. A Rust-native Arrow/record-batch adapter is deferred until a concrete integration requires it and does not block parity completion. |
33-
| Caller-owned streams | Partial | Borrowed synchronous dynamic/typed/structured visitors, metadata reads, dynamic/schema/typed/multi-sheet writers, and separate reader-to-writer Insert are implemented with leave-open semantics. Borrowed lazy iterators, async streams, and template streams remain unsupported. |
33+
| Caller-owned streams | Partial | Borrowed synchronous dynamic/typed/structured visitors, metadata reads, dynamic/schema/typed/multi-sheet writers, and separate reader-to-writer Insert are implemented with leave-open semantics. Borrowed lazy iterators, borrowed async streams, and template streams remain unsupported. |
3434
| Async and cancellation | Partial | Optional runtime-neutral dynamic/Serde path queries and explicit-schema Insert support bounded streams and cooperative cancellation. Async export/template operations, async write sources, borrowed async readers, and progress callbacks remain unsupported. ZIP and filesystem work remains blocking on dedicated workers. |
3535
| General save inputs | Partial | Export from general objects/enumerables, dictionaries, `DataTable`, `IDataReader`, and async enumerables, with progress. Rust accepts dynamic or same-type Serde slices and reports per-sheet row counts. |
3636
| Multi-sheet export | Partial | Rust creates ordered visible, hidden, and very-hidden worksheets, but does not yet accept heterogeneous Serde row types in one call. |
37-
| Existing-workbook operations | Partial | Rust appends or strictly replaces worksheets through atomic path APIs or separate borrowed streams, preserving unrelated package parts and worksheet identity. Copy-and-add, rename, reorder, and standalone visibility mutation remain unsupported. |
37+
| Existing-workbook operations | Partial | Rust atomically appends, strictly replaces, or renames worksheets while preserving unrelated package parts and worksheet identity. Copy-and-add, reorder, and standalone visibility mutation remain unsupported. Rename deliberately preserves formula/defined-name text without repairing old-name references, matching .NET `AlterSheet`. |
3838
| Templates | Partial | Rust fills path/byte templates with scalar values and single-row arrays while preserving package parts. Streams, grouping, conditions, parametrized sheets, `$=` formulas, formula-reference updates, and calculation-chain handling remain unsupported. |
3939
| Pictures and merge processing | Missing | Add anchored pictures and merge adjacent identical cells through the templater surface. Structured reads do not provide authoring parity. |
4040
| CSV | Implemented | Dynamic/Serde path, byte, and borrowed query/save APIs; column discovery; inferred/explicit-schema append; delimiter/newline/encoding/BOM/null/quoting configuration; and `query-csv` CLI. DataReader/DataTable are replaced by Rust iterators. Async/progress APIs and a one-call CSV/XLSX converter are not exposed. |
@@ -55,7 +55,7 @@ Rust already implements dynamic and Serde-typed XLSX path queries, inclusive A1
5555
| Tables | `MiniExcel::query_table*`; Rust focused tests use the exact `TestQueryTable.xlsx` fixture (SHA-256 `04F719BF9F9E99D9B437A8FB32F8111FD92580A1D29ACAD10B6ED128C0564501`) | `OpenXmlImporter.QueryTableAsync`; `tests/MiniExcel.OpenXml.Tests/Tables/` |
5656
| Comments | `MiniExcel::get_comments*`; Rust focused tests use `TestCommentsAndNotes.xlsx` (SHA-256 `3A855CE896ED62DC27C91797432DD89EE081F07CD03AB05BF1B0CD745543A3FC`) | `OpenXmlImporter.RetrieveCommentsAsync`; `tests/MiniExcel.OpenXml.Tests/Comments/` |
5757
| DataReader/DataTable | Rust iterators and borrowed visitors are the native abstraction; no literal .NET tabular adapter is planned | `OpenXmlImporter.GetDataReader`, `GetAsyncDataReader`, `QueryAsDataTableAsync`; `tests/MiniExcel.OpenXml.Tests/DataReader/` |
58-
| Multi-sheet and workbook edits | Writer creates multiple sheets; existing workbooks support append and strict replacement with package preservation and bounded explicit-schema producers | `OpenXmlExporter.InsertSheetAsync`, `CopyAndAddSheetAsync`, `AlterSheetAsync`; `tests/MiniExcel.OpenXml.Tests/MultipleSheets/` and `AlterSheets/` |
58+
| Multi-sheet and workbook edits | Writer creates multiple sheets; existing workbooks support append, strict replacement, and atomic rename with package preservation | `OpenXmlExporter.InsertSheetAsync`, `CopyAndAddSheetAsync`, `AlterSheetAsync`; `tests/MiniExcel.OpenXml.Tests/MultipleSheets/` and `AlterSheets/` |
5959
| Templates/pictures/merges | Basic template fill implemented; advanced directives and authoring remain deferred | `src/MiniExcel.OpenXml/Api/OpenXmlTemplater.cs`; `tests/MiniExcel.OpenXml.Tests/Templates/` |
6060
| CSV/conversion | `MiniExcel::query_csv*`, `save_csv*`, and `append_csv*`; Rust tests use exact `TestHeader.csv` (`6C2FC27FCA2876F1ECCA17061B8EE23E133ECDB726F8E0B84167E58D86234432`) and GB2312 (`BA8A2505AB271D5575C58CC1FCBE5A5002CEB9E2F43CB95412246E25A50E8B5A`) fixtures | `src/MiniExcel.Csv/Api/`; `src/MiniExcel/MiniExcelConverter.cs`; `tests/MiniExcel.Csv.Tests/` |
6161
| Mapping | Serde mapping only | `src/MiniExcel.Core/Attributes/MiniExcelColumnAttribute.cs`; `src/MiniExcel.OpenXml.FluentMapping/`; mapping tests |
@@ -68,7 +68,7 @@ The .NET APIs marked with `Async` also have generated synchronous counterparts t
6868

6969
1. **Async export/template APIs**: extend runtime-neutral producer/cancellation patterns without presenting blocking ZIP work as async I/O.
7070
2. **Advanced templates and Fluent Mapping**: add grouped/conditional templates, parametrized sheets, and mapping through separate compatibility milestones.
71-
3. **Remaining workbook edits**: copy/add, rename, reorder, and standalone visibility mutation require their own preservation contracts.
71+
3. **Remaining workbook edits**: copy/add, reorder, and standalone visibility mutation require their own preservation contracts.
7272

7373
DataReader/DataTable are .NET ecosystem abstractions and are intentionally not literal Rust parity requirements. A Rust-native record-batch or tabular adapter is appropriate only when a concrete integration requires it.
7474

0 commit comments

Comments
 (0)