ce38b26721
Implements Phase 7 of CARD_PLAN.md — the entry point that takes a
user-supplied theme zip archive, validates it end-to-end, and
atomically unpacks it into the per-platform user themes directory.
Public API:
import_theme(zip_path) -> Result<ThemeId, ImportError>
Resolves user_theme_dir() and unpacks into <user>/<id>/.
import_theme_into(zip_path, target_root) -> Result<ThemeId, ImportError>
Test-friendly variant that takes the destination explicitly so
unit tests never touch the global OnceLock override.
Safety guarantees enforced:
- 20 MB hard cap on archive size (read from the central directory
before any extraction).
- Zip-slip path traversal rejected via ZipFile::enclosed_name plus a
Component::Normal-only belt-and-braces check.
- Manifest parsed via ron::de and validated via the existing
ThemeManifest::validate (Phase 2) — surfaces named diagnostics for
missing-of-52, unknown keys, duplicate keys, and meta errors.
- Every referenced face + back rasterised through rasterize_svg as a
structural validity check before any bytes hit the destination.
- Atomic install: writes to <root>/.<id>.tmp/ then std::fs::rename
into place, with a recursive copy + remove fallback for cross-
device renames. Failed extraction wipes the staging dir; the user
themes root is never touched on error.
- Id collision with an existing theme dir rejected up front.
7 new tests covering the happy path plus six failure modes (missing
manifest, missing face, oversized archive, zip-slip, missing-file,
id collision). Tests build zips in tempfile::TempDir so they never
touch the real user themes directory.
Workspace deps: zip 8.6 (default-features off + deflate only),
tempfile 3.27 (dev only).
cargo check --workspace --all-targets / clippy --workspace
--all-targets -- -D warnings clean. cargo test could not be run in
this turn because cc disappeared from the sandbox; tests compile
under cargo check --tests and will run on a normal toolchain.
28 lines
828 B
TOML
28 lines
828 B
TOML
[package]
|
|
name = "solitaire_engine"
|
|
version.workspace = true
|
|
license.workspace = true
|
|
edition.workspace = true
|
|
|
|
[dependencies]
|
|
bevy = { workspace = true }
|
|
kira = { workspace = true }
|
|
solitaire_core = { workspace = true }
|
|
solitaire_data = { workspace = true }
|
|
solitaire_sync = { workspace = true }
|
|
chrono = { workspace = true }
|
|
uuid = { workspace = true }
|
|
tokio = { workspace = true }
|
|
serde = { workspace = true }
|
|
thiserror = { workspace = true }
|
|
usvg = { workspace = true }
|
|
resvg = { workspace = true }
|
|
tiny-skia = { workspace = true }
|
|
ron = { workspace = true }
|
|
dirs = { workspace = true }
|
|
zip = { workspace = true }
|
|
|
|
[dev-dependencies]
|
|
async-trait = { workspace = true }
|
|
tempfile = { workspace = true }
|