Iteration 5: export / import commands

Implements the `export` and `import` app-level commands per
ADR-0015 §11 + ADR-0007 amendment 1.

- `export [<path>]` writes a zip of project.yaml + data/ to
  <data-root>/YYYYMMDD-<projectname>-export-NN.zip by default,
  preserving the project's directory name as the single
  top-level folder inside the archive.
- `import <zip> [as <target>]` extracts an exported zip into
  a new named project and switches to it. Target name is
  derived from the zip's top-level folder by default; on
  collision the destination auto-suffixes -02, -03, ... up
  to -99 instead of refusing (deviates from §2's refuse-on-
  collision rule for save/save as; recorded as an amendment
  to ADR-0015 §11).
- Excludes playground.db and history.log from the zip.
- Path-traversal protection via zip::enclosed_name + post-
  resolution check that the extraction path stays inside
  the target directory.

Adds the zip = "5" dep with default-features = false +
features = ["deflate"] to keep the binary-size cost modest.

Test baseline: 370 passing, 0 failing, 0 skipped.
This commit is contained in:
claude@clouddev1
2026-05-08 08:24:45 +00:00
parent ca71184678
commit c6cf3df6dc
11 changed files with 1419 additions and 15 deletions
+23
View File
@@ -59,4 +59,27 @@ pub enum Action {
NewProject {
source: String,
},
/// Export the current project to a zip file. `target` is
/// `None` for the default filename
/// (`YYYYMMDD-<projectname>-export-NN.zip`) under the
/// active data root, or `Some(path)` for an explicit
/// target. Relative paths resolve under
/// `<data-root>/`. Per ADR-0015 §11 the zip excludes
/// `playground.db` and `history.log`.
Export {
target: Option<String>,
source: String,
},
/// Import a previously-exported zip and switch to the
/// resulting project. `zip_path` is the user-typed path to
/// the source archive (relative to CWD or absolute).
/// `as_target` is the optional user-supplied destination
/// name; when `None`, the destination is derived from the
/// zip's top-level folder. Collisions auto-suffix `-NN`
/// (ADR-0015 §11 amendment).
Import {
zip_path: String,
as_target: Option<String>,
source: String,
},
}