Asset database file ops + script-file creation helpers

Groundwork for the Stage-10 editor-UX batch (file explorer, New Script
button), all pure logic proven by unit tests:

- `AssetDatabase::move_asset` / `move_folder` / `delete_asset`: perform
  the disk operation and the uid-map update together, so renaming or
  moving an asset (or a whole folder) keeps its uid and every saved
  `AssetRef` resolving. Kinds re-classify from the new path; `..` and
  existing destinations are refused via the new `AssetDbError` enum;
  deleted uids are never reused.
- Editor `assets.rs`: `script_template` (compiles under the sandboxed
  ScriptEngine — covered by a test), `sanitize_script_stem`, and
  `create_script_file` which writes a collision-free
  `assets/scripts/<stem>.rhai` and returns the relative path for
  database registration.
- docs/assets.md: new "File operations" section + the missing Script
  row in the typed-folder table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Homer
2026-07-10 20:29:08 +02:00
parent e88a7e2bb1
commit d6cb9947b2
4 changed files with 430 additions and 3 deletions
+25
View File
@@ -164,10 +164,34 @@ breaking references, since the uid travels with the file in the manifest.
| `Model` | `models/` | `gltf`, `glb`, `obj` |
| `Audio` | `audio/` | `wav`, `ogg`, `mp3`, `flac` |
| `Ui` | `ui/` | (recognised by folder — shares `.ron` with scenes) |
| `Script` | `scripts/` | `rhai` |
`AssetKind::classify(path)` infers the kind: the leading folder wins, with the
file extension as a fallback for files dropped directly in `assets/`.
### File operations — rename, move, delete
The database also *performs* file reorganisation, so the editor's file explorer
(and any tool) can rename or move assets **without breaking saved references**
— the disk operation and the uid map are updated together:
```rust
# use oxide_engine::asset::{AssetDatabase, AssetUid};
# fn demo(db: &mut AssetDatabase, uid: AssetUid) -> Result<(), oxide_engine::asset::AssetDbError> {
db.move_asset(uid, "textures/env/brick.png")?; // rename/move; uid unchanged
db.move_folder("textures/env", "textures/world")?; // every entry under it follows
db.delete_asset(uid)?; // file + entry; uid never reused
db.save()?; // persist the new paths
# Ok(())
# }
```
All three refuse to touch anything outside `assets/` (`..` is rejected) and
refuse to overwrite an existing destination ([`AssetDbError`] has a variant per
failure). Kinds are re-classified from the new path, since a move can change
the typed folder. Deleting drops the entry but never reuses its uid — a
dangling reference stays dangling instead of silently pointing at a new file.
### Asset-reference fields in the inspector
A component points at an asset with an [`AssetRef<T>`] field — **not** a live
@@ -209,6 +233,7 @@ projects are seeded with a bundled default UI font (Inter, SIL OFL) under
`fonts/`, referenced by its project-relative path like any other asset.
[`AssetDatabase`]: ../engine/src/asset/database.rs
[`AssetDbError`]: ../engine/src/asset/database.rs
[`AssetKind`]: ../engine/src/asset/database.rs
[`AssetUid`]: ../engine/src/asset/database.rs
[`AssetRef<T>`]: ../engine/src/asset/database.rs