Skip to content

Commit 84050d1

Browse files
committed
Add validated atomic commits for XLSX path inserts
1 parent e616c76 commit 84050d1

10 files changed

Lines changed: 750 additions & 31 deletions

File tree

‎Cargo.lock‎

Lines changed: 120 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ repository = "https://github.com/mini-software/MiniExcel-Rust"
1111
homepage = "https://github.com/mini-software/MiniExcel-Rust"
1212

1313
[workspace.dependencies]
14+
atomicwrites = "0.4.4"
1415
calamine = { version = "0.35.0", features = ["chrono"] }
1516
chrono = { version = "0.4.42", features = ["serde"] }
1617
clap = { version = "4.6.6", features = ["derive"] }

‎docs/insert-existing-workbook-plan.md‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -239,19 +239,21 @@ Acceptance:
239239

240240
Depends on Task 4.
241241

242-
- [ ] Write path inserts to a uniquely named sibling temporary file opened with `create_new`.
243-
- [ ] Finish the ZIP central directory, flush, sync the temporary file, and reopen it for structural validation.
244-
- [ ] Validate workbook/rels/content-types consistency, unique IDs/targets, worksheet availability, and at least one visible worksheet.
245-
- [ ] Commit with a safe cross-platform replacement primitive. Workspace code must remain `unsafe`-free; evaluate the existing `tempfile` API first and add a narrowly scoped dependency only if required.
246-
- [ ] Preserve original file permissions where supported.
247-
- [ ] Clean temporary package and row-spool files on all error paths.
248-
- [ ] Add failure injection at preflight, row generation, ZIP copy, ZIP finish, validation, and commit.
242+
- [x] Write path inserts to a uniquely named sibling temporary file opened with `create_new`.
243+
- [x] Finish the ZIP central directory, flush, sync the temporary file, and reopen it for structural validation.
244+
- [x] Validate workbook/rels/content-types consistency, unique IDs/targets, worksheet availability, and at least one visible worksheet.
245+
- [x] Commit with a safe cross-platform replacement primitive. Workspace code must remain `unsafe`-free; evaluate the existing `tempfile` API first and add a narrowly scoped dependency only if required.
246+
- [x] Preserve original file permissions where supported.
247+
- [x] Clean temporary package and row-spool files on all error paths.
248+
- [x] Add failure injection at preflight, row generation, ZIP copy, ZIP finish, validation, and commit.
249+
250+
Completed on 2026-08-24. Focused test: `cargo +1.85.0 test -p miniexcel insert::atomic::tests --lib --locked`. Windows replacement uses the safe `atomicwrites` wrapper so staged attributes are preserved; Unix mode-bit coverage runs under `cfg(unix)` in CI.
249251

250252
Acceptance:
251253

252254
- The original workbook hash remains unchanged for every injected pre-commit failure.
253255
- Successful replacement works on Linux and Windows CI.
254-
- Focused command: `cargo +1.85.0 test -p miniexcel --test insert atomic_commit --locked`.
256+
- Focused command: `cargo +1.85.0 test -p miniexcel insert::atomic::tests --lib --locked`.
255257

256258
### Task 6: Public Append API
257259

‎docs/insert-existing-workbook-plan.zh-CN.md‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -239,19 +239,21 @@ Donor-workbook 方案复用经过测试的 `XlsxWriter`,避免重复实现 ser
239239

240240
依赖 Task 4。
241241

242-
- [ ] Path insert 写入同目录、使用 `create_new` 打开的唯一临时文件。
243-
- [ ] 完成 ZIP central directory、flush、sync 临时文件,并重新打开做结构验证。
244-
- [ ] 验证 workbook/rels/content-types 一致性、ID/target 唯一、worksheet 可访问,且至少一个 visible worksheet。
245-
- [ ] 使用安全的跨平台 replacement primitive。Workspace code 必须保持无 `unsafe`;先评估现有 `tempfile` API,只在必要时增加范围很窄的 dependency。
246-
- [ ] 可行时保留 source permission。
247-
- [ ] 所有 error path 清理临时 package 和 row spool。
248-
- [ ] 在 preflight、row generation、ZIP copy、ZIP finish、validation 和 commit 阶段加入 failure injection。
242+
- [x] Path insert 写入同目录、使用 `create_new` 打开的唯一临时文件。
243+
- [x] 完成 ZIP central directory、flush、sync 临时文件,并重新打开做结构验证。
244+
- [x] 验证 workbook/rels/content-types 一致性、ID/target 唯一、worksheet 可访问,且至少一个 visible worksheet。
245+
- [x] 使用安全的跨平台 replacement primitive。Workspace code 必须保持无 `unsafe`;先评估现有 `tempfile` API,只在必要时增加范围很窄的 dependency。
246+
- [x] 可行时保留 source permission。
247+
- [x] 所有 error path 清理临时 package 和 row spool。
248+
- [x] 在 preflight、row generation、ZIP copy、ZIP finish、validation 和 commit 阶段加入 failure injection。
249+
250+
已于 2026-08-24 完成。聚焦测试:`cargo +1.85.0 test -p miniexcel insert::atomic::tests --lib --locked`。Windows replacement 使用安全的 `atomicwrites` wrapper 以保留 staged attribute;Unix mode-bit 覆盖在 CI 的 `cfg(unix)` 下执行。
249251

250252
验收:
251253

252254
- 每个 commit 前注入失败都保持原 workbook hash 不变。
253255
- Linux 与 Windows CI 上成功 replacement。
254-
- 聚焦命令:`cargo +1.85.0 test -p miniexcel --test insert atomic_commit --locked`。
256+
- 聚焦命令:`cargo +1.85.0 test -p miniexcel insert::atomic::tests --lib --locked`。
255257

256258
### Task 6:公共 Append API
257259

‎miniexcel/Cargo.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ zip.workspace = true
2727
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
2828
tempfile.workspace = true
2929

30+
[target.'cfg(windows)'.dependencies]
31+
atomicwrites.workspace = true
32+
3033
[dev-dependencies]
3134
serde_json.workspace = true
3235
tempfile.workspace = true

‎miniexcel/src/error.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ enum ErrorKind {
7070
ExistingWorksheet(String),
7171

7272
#[error("atomic workbook commit failed: {0}")]
73+
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
7374
AtomicCommit(String),
7475

7576
#[error("failed to deserialize worksheet '{sheet}' at Excel row {row}: {source}")]
@@ -166,7 +167,7 @@ impl Error {
166167
ErrorKind::ExistingWorksheet(sheet_name.into()).into()
167168
}
168169

169-
#[allow(dead_code)]
170+
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
170171
pub(crate) fn atomic_commit(message: impl Into<String>) -> Self {
171172
ErrorKind::AtomicCommit(message.into()).into()
172173
}

0 commit comments

Comments
 (0)