From 817407c69e8b7988c6c747fc5c3c8d59f8017de5 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:06:14 +0300 Subject: [PATCH] Prepare for the v0.9.0 release (#503) * Prepare for the v0.9.0 release * Further small tweaks to the limitations text * Generate docs links and fix indexmap link in docs * Clarified ron restrictions --- CHANGELOG.md | 41 ++++++++++++++-------- Cargo.toml | 6 +++- README.md | 92 ++++++++++++++++++++++++++++++------------------- docs/grammar.md | 5 ++- src/lib.rs | 2 +- 5 files changed, 93 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b6e4de8..5f982607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,33 +6,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [0.9.0] - 2023-09-?? + +### API Changes + - Add `ron::value::RawValue` helper type which can (de)serialize any valid RON ([#407](https://github.com/ron-rs/ron/pull/407)) - Add `escape_strings` option to `PrettyConfig` to allow serialising with or without escaping ([#426](https://github.com/ron-rs/ron/pull/426)) -- Add CIFuzz GitHub action ([#429](https://github.com/ron-rs/ron/pull/429)) - Add `compact_maps` and `compact_structs` options to `PrettyConfig` to allow serialising maps and structs on a single line ([#448](https://github.com/ron-rs/ron/pull/448)) - Add minimal support for `#[serde(flatten)]` with roundtripping through RON maps ([#455](https://github.com/ron-rs/ron/pull/455)) - Add minimal roundtripping support for `#[serde(tag = "tag")]`, `#[serde(tag = "tag", content = "content")]`, and `#[serde(untagged)]` enums ([#451](https://github.com/ron-rs/ron/pull/451)) +- Breaking: Expand the `value::Number` enum to explicitly encode all possible number types ([#479](https://github.com/ron-rs/ron/pull/479)) +- Add `number_suffixes` option to `PrettyConfig` to allow serialising numbers with their explicit type suffix, e.g. `42i32` ([#481](https://github.com/ron-rs/ron/pull/481)) +- Allow `ron::value::RawValue` to capture any whitespace to the left and right of a ron value ([#487](https://github.com/ron-rs/ron/pull/487)) +- Breaking: Enforce that ron always writes valid UTF-8 ([#488](https://github.com/ron-rs/ron/pull/488)) +- Add convenient `Value::from` impls ([#498](https://github.com/ron-rs/ron/pull/498)) + +### Format Changes + +- [Non-API] Breaking: Treat `Some` like a newtype variant with `unwrap_variant_newtypes` ([#465](https://github.com/ron-rs/ron/pull/465)) +- Allow parsing floating point literals with underscores ([#481](https://github.com/ron-rs/ron/pull/481)) +- **Format-Breaking:** Switch from base64-encoded to Rusty byte strings, still allow base64 deserialising for now ([#438](https://github.com/ron-rs/ron/pull/438)) +- Fix issue [#241](https://github.com/ron-rs/ron/issues/241) and allow parsing numbers with explicit type suffixes, e.g. `1u8` or `-1f32` ([#481](https://github.com/ron-rs/ron/pull/481)) +- Add support for byte literals as strongly typed unsigned 8-bit integers ([#438](https://github.com/ron-rs/ron/pull/438)) +- Fix issue [#321](https://github.com/ron-rs/ron/issues/321) and allow parsing UTF-8 identifiers ([#488](https://github.com/ron-rs/ron/pull/488)) + +### Bug Fixes + - Fix parsing `r` as a self-describing struct or variant name (and not the start of a raw string) ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix serialising raw strings containing a literal backslash ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix serialising `None` inside a stack of nested `Option`s with `#![enable(implicit_some)]` enabled ([#465](https://github.com/ron-rs/ron/pull/465)) -- [Non-API] Breaking: Treat `Some` like a newtype variant with `unwrap_variant_newtypes` ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix deserialising deserialising `A('/')` into a `ron::Value` ([#465](https://github.com/ron-rs/ron/pull/465)) -- Update the arbitrary fuzzer to check arbitrary serde data types, values, and `ron::ser::PrettyConfig`s ([#465](https://github.com/ron-rs/ron/pull/465)) -- Add a benchmark for PRs that runs over the latest fuzzer corpus ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix issue [#445](https://github.com/ron-rs/ron/issues/445) and allow parsing `+unsigned` as an unsigned int ([#479](https://github.com/ron-rs/ron/pull/479)) -- Breaking: Expand the `value::Number` enum to explicitly encode all possible number types ([#479](https://github.com/ron-rs/ron/pull/479)) -- Allow parsing floating point literals with underscores ([#481](https://github.com/ron-rs/ron/pull/481)) -- Fix issue [#241](https://github.com/ron-rs/ron/issues/241) and allow parsing numbers with explicit type suffixes, e.g. `1u8` or `-1f32` ([#481](https://github.com/ron-rs/ron/pull/481)) -- Add `number_suffixes` option to `PrettyConfig` to allow serialising numbers with their explicit type suffix, e.g. `42i32` ([#481](https://github.com/ron-rs/ron/pull/481)) -- Allow `ron::value::RawValue` to capture any whitespace to the left and right of a ron value ([#487](https://github.com/ron-rs/ron/pull/487)) - Fix serialising reserved identifiers `true`, `false`, `Some`, `None`, `inf`[`f32`|`f64`], and `Nan`[`f32`|`f64`] ([#487](https://github.com/ron-rs/ron/pull/487)) - Disallow unclosed line comments at the end of `ron::value::RawValue` ([#489](https://github.com/ron-rs/ron/pull/489)) -- **Format-Breaking:** Switch from base64-encoded to Rusty byte strings, still allow base64 deserialising for now ([#438](https://github.com/ron-rs/ron/pull/438)) -- Add support for byte literals as strongly typed unsigned 8-bit integers ([#438](https://github.com/ron-rs/ron/pull/438)) -- Fix issue [#321](https://github.com/ron-rs/ron/issues/321) and allow parsing UTF-8 identifiers ([#488](https://github.com/ron-rs/ron/pull/488)) -- Breaking: Enforce that ron always writes valid UTF-8 ([#488](https://github.com/ron-rs/ron/pull/488)) - Fix parsing of struct/variant names starting in `None`, `Some`, `true`, or `false` ([#499](https://github.com/ron-rs/ron/pull/499)) -- Add convenient `Value::from` impls ([#498](https://github.com/ron-rs/ron/pull/498)) + +### Miscellaneous + +- Add CIFuzz GitHub action ([#429](https://github.com/ron-rs/ron/pull/429)) +- Update the arbitrary fuzzer to check arbitrary serde data types, values, and `ron::ser::PrettyConfig`s ([#465](https://github.com/ron-rs/ron/pull/465)) +- Add a benchmark for PRs that runs over the latest fuzzer corpus ([#465](https://github.com/ron-rs/ron/pull/465)) ## [0.8.1] - 2023-08-17 diff --git a/Cargo.toml b/Cargo.toml index 14ff17fb..34401e27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ron" # Memo: update version in src/lib.rs too (doc link) -version = "0.8.1" +version = "0.9.0" license = "MIT OR Apache-2.0" keywords = ["parser", "serde", "serialization"] authors = [ @@ -40,3 +40,7 @@ serde_json = "1.0" option_set = "0.2" typetag = "0.2" bytes = { version = "1.3", features = ["serde"] } + +[package.metadata.docs.rs] +features = ["integer128", "indexmap"] +rustdoc-args = ["--generate-link-to-definition"] diff --git a/README.md b/README.md index a9222492..d3f9eadd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # Rusty Object Notation -[![CI](https://github.com/ron-rs/ron/actions/workflows/ci.yaml/badge.svg)](https://github.com/ron-rs/ron/actions/workflows/ci.yaml) -[![Coverage](https://img.shields.io/endpoint?url=https%3A%2F%2Fron-rs.github.io%2Fron%2Fcoverage%2Fcoverage.json)](https://ron-rs.github.io/ron/coverage/) -[![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron) [![MSRV](https://img.shields.io/badge/MSRV-1.64.0-orange)](https://github.com/ron-rs/ron) +[![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron) [![Docs](https://docs.rs/ron/badge.svg)](https://docs.rs/ron) + +[![CI](https://github.com/ron-rs/ron/actions/workflows/ci.yaml/badge.svg)](https://github.com/ron-rs/ron/actions/workflows/ci.yaml) +[![Coverage](https://img.shields.io/endpoint?url=https%3A%2F%2Fron-rs.github.io%2Fron%2Fcoverage%2Fcoverage.json)](https://ron-rs.github.io/ron/coverage/) +[![Fuzzing](https://oss-fuzz-build-logs.storage.googleapis.com/badges/ron.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:ron) + [![Matrix](https://img.shields.io/matrix/ron-rs:matrix.org.svg)](https://matrix.to/#/#ron-rs:matrix.org) RON is a simple readable data serialization format that looks similar to Rust syntax. @@ -42,6 +45,30 @@ GameConfig( // optional struct name ) ``` +## RON syntax overview + +* Numbers: `42`, `3.14`, `0xFF`, `0b0110` +* Strings: `"Hello"`, `"with\\escapes\n"`, `r#"raw string, great for regex\."#` +* Byte Strings: `b"Hello"`, `b"with \x65\x73\x63\x61\x70\x65\x73\n"`, `br#"raw, too"#` +* Booleans: `true`, `false` +* Chars: `'e'`, `'\n'` +* Optionals: `Some("string")`, `Some(Some(1.34))`, `None` +* Tuples: `("abc", 1.23, true)`, `()` +* Lists: `["abc", "def"]` +* Structs: `( foo: 1.0, bar: ( baz: "I'm nested" ) )` +* Maps: `{ "arbitrary": "keys", "are": "allowed" }` + +> **Note:** Serde's data model represents fixed-size Rust arrays as tuple (instead of as list) + +RON also supports several extensions, which are documented [here](docs/extensions.md). + +## Specification + +RON's formal and complete grammar is available [here](docs/grammar.md). + +There also is a very basic, work in progress specification available on +[the wiki page](https://github.com/ron-rs/ron/wiki/Specification). + ## Why RON? ### Example in JSON @@ -102,35 +129,6 @@ Note the following advantages of RON over JSON: * optional struct names improve readability * enums are supported (and less verbose than their JSON representation) -## Limitations - -RON is not designed to be a fully self-describing format (unlike JSON) and is thus not guaranteed to work when [`deserialize_any`](https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_any) is used instead of its typed alternatives. In particular, the following Serde attributes only have limited support: - -- `#[serde(tag = "tag")]`, i.e. internally tagged enums [^serde-enum-hack] -- `#[serde(tag = "tag", content = "content")]`, i.e. adjacently tagged enums [^serde-enum-hack] -- `#[serde(untagged)]`, i.e. untagged enums [^serde-enum-hack] -- `#[serde(flatten)]`, i.e. flattening of structs into maps [^serde-flatten-hack] - -While data structures with any of these attributes should roundtrip through RON, their textual representation may not always match your expectation. For instance, flattened structs are only serialised as maps and deserialised from maps. - -[^serde-enum-hack]: Deserialising an internally, adjacently, or un-tagged enum requires detecting `serde`'s internal `serde::__private::de::content::Content` content type so that RON can describe the deserialised data structure in serde's internal JSON-like format. This detection only works for the automatically-derived [`Deserialize`](https://docs.rs/serde/latest/serde/de/trait.Deserialize.html) impls on enums. See [#451](https://github.com/ron-rs/ron/pull/451) for more details. -[^serde-flatten-hack]: Deserialising a flattened struct from a map requires that the struct's [`Visitor::expecting`](https://docs.rs/serde/latest/serde/de/trait.Visitor.html#tymethod.expecting) implementation formats a string starting with `"struct "`. This is the case for automatically-derived [`Deserialize`](https://docs.rs/serde/latest/serde/de/trait.Deserialize.html) impls on structs. See [#455](https://github.com/ron-rs/ron/pull/455) for more details. - -## RON syntax overview - -* Numbers: `42`, `3.14`, `0xFF`, `0b0110` -* Strings: `"Hello"`, `"with\\escapes\n"`, `r#"raw string, great for regex\."#` -* Byte Strings: `b"Hello"`, `b"with \x65\x73\x63\x61\x70\x65\x73\n"`, `br#"raw, too"#` -* Booleans: `true`, `false` -* Chars: `'e'`, `'\n'` -* Optionals: `Some("string")`, `Some(Some(1.34))`, `None` -* Tuples: `("abc", 1.23, true)`, `()` -* Lists: `["abc", "def"]` -* Structs: `( foo: 1.0, bar: ( baz: "I'm nested" ) )` -* Maps: `{ "arbitrary": "keys", "are": "allowed" }` - -> **Note:** Serde's data model represents fixed-size Rust arrays as tuple (instead of as list) - ## Quickstart ### `Cargo.toml` @@ -176,12 +174,34 @@ fn main() { [emacs-ron]: https://chiselapp.com/user/Hutzdog/repository/ron-mode/home -## Specification +## Limitations -There is a very basic, work in progress specification available on -[the wiki page](https://github.com/ron-rs/ron/wiki/Specification). -A more formal and complete grammar is available [here](docs/grammar.md). +RON is not designed to be a fully self-describing format (unlike JSON) and is thus not guaranteed to work when [`deserialize_any`](https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_any) is used instead of its typed alternatives. In particular, the following Serde attributes only have limited support: + +- `#[serde(tag = "tag")]`, i.e. internally tagged enums [^serde-enum-hack] +- `#[serde(tag = "tag", content = "content")]`, i.e. adjacently tagged enums [^serde-enum-hack] +- `#[serde(untagged)]`, i.e. untagged enums [^serde-enum-hack] +- `#[serde(flatten)]`, i.e. flattening of structs into maps [^serde-flatten-hack] + +While data structures with any of these attributes should generally roundtrip through RON, some restrictions apply [^serde-restrictions] and their textual representation may not always match your expectation: + +- flattened structs are only serialised as maps and deserialised from maps +- struct names inside an internally (or adjacently) tagged or untagged enum, e.g. by enabling the `PrettyConfig::struct_types` setting, are not supported +- enabling the `#![enable(implicit_some)]` extension on a document with internally (or adjacently) tagged or untagged enums is not supported +- untagged tuple / struct variants with no fields are not supported +- untagged tuple variants with just one field (that are not newtype variants) are not supported when the `#![enable(unwrap_variant_newtypes)]` extension is enabled +- internally tagged newtype variants must not contain a unit / unit struct inside an untagged newtype variant, or an untagged unit variant +- serde does not yet support `i128` and `u128` inside internally (or adjacently) tagged or untagged enums +- newtypes and zero-length arrays / tuples / tuple structs / structs / tuple variants / struct variants are not supported inside internally (or adjacently) tagged or untagged enums +- externally tagged tuple variants with just one field (that are not newtype variants) are not supported inside internally (or adjacently) tagged or untagged enums + +Please file a [new issue](https://github.com/ron-rs/ron/issues/new) if you come across a use case which is not listed among the above restrictions but still breaks. + +[^serde-enum-hack]: Deserialising an internally, adjacently, or un-tagged enum requires detecting `serde`'s internal `serde::__private::de::content::Content` content type so that RON can describe the deserialised data structure in serde's internal JSON-like format. This detection only works for the automatically-derived [`Deserialize`](https://docs.rs/serde/latest/serde/de/trait.Deserialize.html) impls on enums. See [#451](https://github.com/ron-rs/ron/pull/451) for more details. + +[^serde-flatten-hack]: Deserialising a flattened struct from a map requires that the struct's [`Visitor::expecting`](https://docs.rs/serde/latest/serde/de/trait.Visitor.html#tymethod.expecting) implementation formats a string starting with `"struct "`. This is the case for automatically-derived [`Deserialize`](https://docs.rs/serde/latest/serde/de/trait.Deserialize.html) impls on structs. See [#455](https://github.com/ron-rs/ron/pull/455) for more details. +[^serde-restrictions]: Most of these restrictions are currently blocked on [serde#1183](https://github.com/serde-rs/serde/issues/1183), which limits non-self-describing formats from roundtripping format-specific information through internally (or adjacently) tagged or untagged enums. ## License diff --git a/docs/grammar.md b/docs/grammar.md index 30b250cd..1d0e6cc5 100644 --- a/docs/grammar.md +++ b/docs/grammar.md @@ -60,7 +60,8 @@ unsigned_octal = "0o", digit_octal, { digit_octal | "_" }; unsigned_hexadecimal = "0x", digit_hexadecimal, { digit_hexadecimal | "_" }; unsigned_decimal = digit, { digit | "_" }; -byte = ascii | ("\\", (escape_ascii | escape_byte)); +byte = "b", "'", byte_content, "'"; +byte_content = ascii | ("\\", (escape_ascii | escape_byte)); float = ["+" | "-"], ("inf" | "NaN" | float_num), [float_suffix]; float_num = (float_int | float_std | float_frac), [float_exp]; @@ -71,6 +72,8 @@ float_exp = ("e" | "E"), ["+" | "-"], { digit | "_" }, digit, { digit | "_" }; float_suffix = "f", ("32", "64"); ``` +> Note: `ascii` refers to any ASCII character, i.e. any byte in range `0x00 ..= 0x7F`. + ## String ```ebnf diff --git a/src/lib.rs b/src/lib.rs index 18bc78cf..cb50da52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ #![deny(unsafe_code)] #![allow(clippy::missing_errors_doc)] // FIXME #![doc = include_str!("../README.md")] -#![doc(html_root_url = "https://docs.rs/ron/0.8.1")] +#![doc(html_root_url = "https://docs.rs/ron/0.9.0")] pub mod de; pub mod ser;