* Add craft-ecproj-format: the envelope of EffectCraft project files
An .ecproj file is one JSON object: savedBy, then the saved view's
members, then the project's members, written with two-space indentation.
The crate defines that envelope with the view and the project as type
parameters, so the project model stays in EffectCraft:
- SCHEMA_VERSION (2) and check_schema, which refuses a newer schema
with EffectCraft's own message.
- File { saved_by, view, project }, serialized with both parts
flattened, and File::to_json.
- Head, which reads savedBy and schema from a JSON object, and
saved_by, which reads savedBy alone.
The tests write synthetic projects and compare the text with JSON
written by hand in the shape EffectCraft's writer gives. The workflow
runs fmt, clippy, the tests and a wasm32-unknown-unknown check.
* ci: do not keep the checkout token in the ecproj-format workflow
The checkout step sets persist-credentials: false, as jpeg.yml and media.yml do. No later step in the job uses the token.
* ecproj-format: write compact files, read savedBy as EffectCraft opens it
EffectCraft's Save Compact Project Files setting (effectcraft 07639b5f)
writes .ecproj files on one line with serde_json::to_string.
File::to_json_compact writes the same text, and the docs say the crate
writes and reads both forms.
Head now reads every file EffectCraft's Project::from_json opens: a
savedBy that is not a string, or appears more than once, reads as None,
and a project written as a JSON array reads with its first element as
the schema. saved_by also gives None for a repeated savedBy, as
EffectCraft's saved_by does. File's docs say the crate does not check
values and that serde_json writes NaN and infinity as null.
* ecproj-format: skip a non-string savedBy without keeping it
Head::read and saved_by read savedBy into a serde_json::Value before
dropping any value that is not a string, so a wide or deeply nested
savedBy was held in memory, and one nested past serde_json's recursion
limit made Head::read fail. A non-string savedBy, and every repeated
savedBy, is now skipped element by element with IgnoredAny, as other
members are. It still reads as None.
* ecproj-format: Head::read refuses a newer schema and reads objects only
Head::read is the one reader of a project file's envelope. It returns
ReadError: Json when the text is not one JSON object with a u32 schema
(the array form included), or TooNew when the schema is newer than
SCHEMA_VERSION, holding the file's savedBy so a message can name the
version that wrote it. TooNew reads as before. saved_by() is removed,
and Head no longer implements Deserialize.
TooNew, ReadError and ReadError::TooNew are #[non_exhaustive].
2.5 KiB
craft-ecproj-format
The envelope of EffectCraft .ecproj project files: the members around the project that
EffectCraft writes and other apps read. The project model, the saved view and the schema 1
conversion stay in EffectCraft. No unsafe, no panics, and it builds for
wasm32-unknown-unknown. The dependencies are serde, serde_json and thiserror.
craft-ecproj-format = { git = "https://github.com/storytold/craft-libs", rev = "<commit>" }
The file
A project file is one UTF-8 JSON object with no newline at the end: savedBy, then the saved
view's members (activeComp, openComps, compTimes, each left out when empty), then the
project's members, starting with schema. Projects saved before savedBy was added start with
schema. EffectCraft indents the object with two spaces, or writes it on one line when Settings ▸
Project ▸ Save Compact Project Files is on. The crate writes and reads both forms.
Use
use craft_ecproj_format::{File, Head, check_schema};
// Writing
check_schema(project.schema)?;
let text = File::new(APP_VERSION, &view, &project).to_json()?;
// Reading
let head = Head::read(&text)?;
SCHEMA_VERSIONis the newest project schema, 2.check_schema(schema) -> Result<(), TooNew>refuses a schema newer thanSCHEMA_VERSION.TooNewreads "project schema 3 is newer than this version supports (2)". It is#[non_exhaustive]: other crates read itsschemaandsupportedfields.File { saved_by, view, project }serializes as one object, withviewandprojectflattened into it.to_json()gives the file's text indented with two spaces, andto_json_compact()gives it on one line. Neither checks the values:serde_jsonwrites NaN and infinity asnull, so the caller checks its values before saving.Head::read(text) -> Result<Head, ReadError>readssavedByandschemafrom one JSON object, and skips other members.savedByisNonewhen it is absent, is not a string, or appears more than once. Text that is not a JSON object, has noschemathat is au32, or has twoschemamembers givesReadError::Json. A schema newer thanSCHEMA_VERSIONgivesReadError::TooNew { too_new, saved_by, .. }, which reads asTooNewdoes and holds the file'ssavedBy, so a message can name the version that wrote it.ReadErrorand itsTooNewvariant are#[non_exhaustive]: match them with a wildcard arm and...
.ecprojx files hold the same object encoded as XML, which this crate does not read or write.