Some documentation; wrapper functions moved into own module
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.vscode/
|
||||
@@ -1,6 +1,8 @@
|
||||
# corona
|
||||
|
||||
Makes Serde serialize your data to the Rust source code.
|
||||

|
||||
|
||||
Makes [Serde](http://serde.rs) serialize your data to the Rust source code.
|
||||
|
||||
### Why?
|
||||
|
||||
@@ -8,22 +10,46 @@ This crate was inspired by the [Stack Overflow question](https://stackoverflow.c
|
||||
|
||||
### How to?
|
||||
|
||||
This crate is intended to be used from the build script. It will serialize anything you provide to it to any path you provide (or to the arbitrary [`io::Write`](https://doc.rust-lang.org/stable/std/io/trait.Write.html) implementation, if you want to). Then, you'll [`include!`](https://doc.rust-lang.org/stable/std/macro.include.html) the generated file wherever you want to use it.
|
||||
This crate is intended to be used from the build script. It will serialize anything you provide to it to any path you provide (or to the arbitrary [`io::Write`](https://doc.rust-lang.org/stable/std/io/trait.Write.html) implementation, or into `String`, if you want to). Then, you'll [`include!`](https://doc.rust-lang.org/stable/std/macro.include.html) the generated file wherever you want to use it.
|
||||
|
||||
### Is it really that simple?
|
||||
|
||||
Well... not. There are several limitations.
|
||||
|
||||
1. All the types used in the serialized struct must be in scope on the include site. Serde doesn't provide the qualified name (i.e. path) to the serializer, only the direct name. The probably easiest way is to use the serialized data as following:
|
||||
1. All the types used in the serialized struct must be in scope on the include site. Serde doesn't provide the qualified name (i.e. path) to the serializer, only the "last" name. The probably easiest way is to use the serialized data as following:
|
||||
```rust
|
||||
let value = {
|
||||
let value: MainType = {
|
||||
use ::path::to::Type1;
|
||||
// ...and other types
|
||||
include!("path/to/file.rs")
|
||||
}
|
||||
```
|
||||
or the similar construction using [`lazy_static`](http://crates.io/crates/lazy_static).
|
||||
|
||||
2. As a consequence, all the types used by the serialized one must have distinct names (or they'll clash with each other).
|
||||
3. Deserializer isn't implemented. This is intentional, since this crate isn't really intended for runtime usage.
|
||||
4. This serializer is intended to use with derived implementation. It may return bogus results when used with customized `Serialize`.
|
||||
5. It is impossible to serialize the struct with private fields outside from the module it is defined in. In fact, to be able to serialize this type at all, you'll have to distribute two copies of your crate, one of which would only export the definition with derived `Serialize` to be used by this crate during the build-time of the second copy. (Isn't this a bit too complex?)
|
||||
3. Deserializer isn't implemented. This is intentional, since this crate isn't really intended for runtime usage. Well, in fact, the deserializer *is* implemented - it's just the Rust compiler itself.
|
||||
4. This serializer is intended for use with derived implementation. It may return bogus results when used with customized `Serialize`.
|
||||
5. It is impossible to serialize the struct with private fields outside from the module it is defined in. In fact, to be able to serialize this type at all, you'll have to distribute two copies of your crate, one of which would only export the definition with derived `Serialize` to be used by this crate during the build-time of the second copy. (Isn't this a bit too complex?)
|
||||
6. It is impossible to use empty tuple structs (i.e. `Empty()`). From the Serde's point of view, they are indistinguishable from unit structs (i.e. `Unit`), but the same Rust syntax can't be used for both, and, since ordinary unit structs are much more common, it was decided to abandon the empty tuples.
|
||||
|
||||
If you find any other case where this doesn't work, feel free to open an issue - we'll either fix the code or document the newly-found limitation.
|
||||
|
||||
### Testing
|
||||
|
||||
This crate uses [`batch_run`](https://crates.io/crates/batch_run) to run its tests.
|
||||
|
||||
The common structure of test cases is like following:
|
||||
- File named `definition.rs` contains the necessary types.
|
||||
- File named `{test_name}-main.rs` includes `definition.rs` as module. It contains the `main` function, which creates an instance of some type from `definition.rs`, generates the corresponding Rust code in `generated.rs` and launches `{test_name}-user.rs` through `batch_run`.
|
||||
- File named `{test_name}-user.rs` includes `definition.rs` as module and `generated.rs` through call to `include!`. It checks that the generated code indeed creates the data equal to what was created initially.
|
||||
|
||||
Testing data itself is defined in [test_fixtures/data.toml], and is in the following format:
|
||||
- Section name in TOML corresponds to the name of test case. Note that this is not the Cargo test, but the item in the `batch_run`'s batch.
|
||||
- Field `main_type` corresponds to the type which serialization is being tested.
|
||||
- If there are several types (for example, in the nested struct), all other types except for main one should be listed under `support_types` as a comma-separated list. These, together with the `main_type`, will be included in `{test_name}-user.rs` as imports.
|
||||
- Field `definition` is literally copied into the `definition.rs`. It's necessary to derive `Debug`, `Serialize` and `PartialEq` on all the types there, since these traits are used during test entry run.
|
||||
- Field `value` is literally copied in two places: first, the `{test_name}-main.rs`, where the code is generated; second, in `{test_name}-user.rs`, where test checks two values for equality.
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg>
|
||||
<defs
|
||||
id="defs12">
|
||||
<linearGradient
|
||||
id="linearGradient827-3">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0"
|
||||
offset="0"
|
||||
id="stop823" />
|
||||
<stop
|
||||
id="stop838"
|
||||
offset="0.5529865"
|
||||
style="stop-color:#fffba3;stop-opacity:0" />
|
||||
<stop
|
||||
style="stop-color:#fffba4;stop-opacity:0.72375691"
|
||||
offset="0.61849707"
|
||||
id="stop840" />
|
||||
<stop
|
||||
id="stop842"
|
||||
offset="0.73410404"
|
||||
style="stop-color:#fff746;stop-opacity:0.97237569" />
|
||||
<stop
|
||||
style="stop-color:#fff522;stop-opacity:0.97237569"
|
||||
offset="0.87668586"
|
||||
id="stop846" />
|
||||
<stop
|
||||
style="stop-color:#fff401;stop-opacity:0.77348065"
|
||||
offset="0.94604999"
|
||||
id="stop844" />
|
||||
<stop
|
||||
style="stop-color:#fff400;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop825" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient827-3"
|
||||
id="radialGradient831"
|
||||
cx="71.837257"
|
||||
cy="70.976242"
|
||||
fx="71.837257"
|
||||
fy="70.976242"
|
||||
r="57.599239"
|
||||
gradientTransform="matrix(1,0,0,0.97191015,0,1.9937123)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<path
|
||||
d="m71.05 23.68c-26.06 0-47.27 21.22-47.27 47.27s21.22 47.27 47.27 47.27 47.27-21.22 47.27-47.27-21.22-47.27-47.27-47.27zm-.07 4.2a3.1 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm7.12 5.12a38.27 38.27 0 0 1 26.2 18.66l-3.67 8.28c-.63 1.43.02 3.11 1.44 3.75l7.06 3.13a38.27 38.27 0 0 1 .08 6.64h-3.93c-.39 0-.55.26-.55.64v1.8c0 4.24-2.39 5.17-4.49 5.4-2 .23-4.21-.84-4.49-2.06-1.18-6.63-3.14-8.04-6.24-10.49 3.85-2.44 7.85-6.05 7.85-10.87 0-5.21-3.57-8.49-6-10.1-3.42-2.25-7.2-2.7-8.22-2.7h-40.6a38.27 38.27 0 0 1 21.41-12.08l4.79 5.02c1.08 1.13 2.87 1.18 4 .09zm-44.2 23.02a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm74.15.14a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm-68.29.5h5.42v24.44h-10.94a38.27 38.27 0 0 1 -1.24-14.61l6.7-2.98c1.43-.64 2.08-2.31 1.44-3.74zm22.62.26h12.91c.67 0 4.71.77 4.71 3.8 0 2.51-3.1 3.41-5.65 3.41h-11.98zm0 17.56h9.89c.9 0 4.83.26 6.08 5.28.39 1.54 1.26 6.56 1.85 8.17.59 1.8 2.98 5.4 5.53 5.4h16.14a38.27 38.27 0 0 1 -3.54 4.1l-6.57-1.41c-1.53-.33-3.04.65-3.37 2.18l-1.56 7.28a38.27 38.27 0 0 1 -31.91-.15l-1.56-7.28c-.33-1.53-1.83-2.51-3.36-2.18l-6.43 1.38a38.27 38.27 0 0 1 -3.32-3.92h31.27c.35 0 .59-.06.59-.39v-11.06c0-.32-.24-.39-.59-.39h-9.15zm-14.43 25.33a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm46.05.14a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11z"
|
||||
id="path2" />
|
||||
<path
|
||||
d="m115.68 70.95a44.63 44.63 0 0 1 -44.63 44.63 44.63 44.63 0 0 1 -44.63-44.63 44.63 44.63 0 0 1 44.63-44.63 44.63 44.63 0 0 1 44.63 44.63zm-.84-4.31 6.96 4.31-6.96 4.31 5.98 5.59-7.66 2.87 4.78 6.65-8.09 1.32 3.4 7.46-8.19-.29 1.88 7.98-7.98-1.88.29 8.19-7.46-3.4-1.32 8.09-6.65-4.78-2.87 7.66-5.59-5.98-4.31 6.96-4.31-6.96-5.59 5.98-2.87-7.66-6.65 4.78-1.32-8.09-7.46 3.4.29-8.19-7.98 1.88 1.88-7.98-8.19.29 3.4-7.46-8.09-1.32 4.78-6.65-7.66-2.87 5.98-5.59-6.96-4.31 6.96-4.31-5.98-5.59 7.66-2.87-4.78-6.65 8.09-1.32-3.4-7.46 8.19.29-1.88-7.98 7.98 1.88-.29-8.19 7.46 3.4 1.32-8.09 6.65 4.78 2.87-7.66 5.59 5.98 4.31-6.96 4.31 6.96 5.59-5.98 2.87 7.66 6.65-4.78 1.32 8.09 7.46-3.4-.29 8.19 7.98-1.88-1.88 7.98 8.19-.29-3.4 7.46 8.09 1.32-4.78 6.65 7.66 2.87z"
|
||||
fill-rule="evenodd"
|
||||
stroke="#000"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="3"
|
||||
id="path4" />
|
||||
<ellipse
|
||||
id="path16"
|
||||
cx="71.05"
|
||||
cy="71.05"
|
||||
rx="60"
|
||||
ry="60"
|
||||
style="fill-opacity:1;fill:url(#radialGradient831)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -1,3 +1,5 @@
|
||||
//! Error returned by Corona serializer.
|
||||
|
||||
use serde::ser;
|
||||
use thiserror::Error;
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//! Convenience functions to be used with Corona.
|
||||
|
||||
use crate::ser::{SerResult, Corona};
|
||||
use crate::error::CoronaError;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Write generated Rust code to the provided [`Write`][std::io::Write] implementation.
|
||||
pub fn write(value: impl Serialize, target: impl std::io::Write) -> SerResult {
|
||||
value.serialize(&mut Corona::new(target))
|
||||
}
|
||||
|
||||
/// Writes generated Rust code to file.
|
||||
///
|
||||
/// This is probably the most common way to use `corona`. When Cargo runs your crate's build task,
|
||||
/// it sets the `OUT_DIR` environment variable to the path to build target directory (see
|
||||
/// [Cargo reference](https://doc.rust-lang.org/cargo/reference/environment-variables.html) for more).
|
||||
/// So, you can use it in two steps:
|
||||
/// 1. Generate the Rust code and write it to temporary file:
|
||||
/// ```no_run
|
||||
/// # let value = ();
|
||||
/// let path: std::path::PathBuf = [
|
||||
/// std::env::var("OUT_DIR").expect("No build target path set"),
|
||||
/// "file_name.rs".into()
|
||||
/// ].iter().collect();
|
||||
/// corona::to_file(value, path).expect("Write failed");
|
||||
/// ```
|
||||
/// 2. [Include][include] the generated Rust code wherever it is needed:
|
||||
/// ```ignore
|
||||
/// let value = include!(concat!(env!(OUT_DIR), "/file_name.rs"));
|
||||
/// ```
|
||||
///
|
||||
/// [include]: https://doc.rust-lang.org/stable/std/macro.include.html
|
||||
pub fn to_file(value: impl Serialize, target: impl AsRef<std::path::Path>) -> SerResult {
|
||||
value.serialize(&mut Corona::new(std::fs::File::create(target)?))
|
||||
}
|
||||
|
||||
/// Convenience wrapper around [`to_file`].
|
||||
///
|
||||
/// This function finds out where the output directory is by looking at `OUT_DIR` environment variable
|
||||
/// and creates the file with the provided name there.
|
||||
pub fn to_out_dir(value: impl Serialize, file_name: impl AsRef<str>) -> SerResult {
|
||||
let path: std::path::PathBuf = [
|
||||
std::env::var("OUT_DIR").expect("OUT_DIR not set, check if you're running this from the build script"),
|
||||
file_name.as_ref().into()
|
||||
].iter().collect();
|
||||
value.serialize(&mut Corona::new(std::fs::File::create(path)?))
|
||||
}
|
||||
|
||||
/// Obtain string with generated Rust code.
|
||||
pub fn to_string(value: impl Serialize) -> Result<String, CoronaError> {
|
||||
let mut out = Vec::new();
|
||||
value.serialize(&mut Corona::new(&mut out))?;
|
||||
Ok(String::from_utf8(out)?)
|
||||
}
|
||||
+36
-16
@@ -1,19 +1,39 @@
|
||||
use crate::ser::SerResult;
|
||||
use serde::Serialize;
|
||||
//! Serde serializer generating Rust code.
|
||||
//!
|
||||
//! This crate can be used to "embed" something into code, having only some serialized
|
||||
//! data, like JSON or YAML. This way, you'll mostly escape runtime cost of deserialization,
|
||||
//! nearly as if you've written the same data directly in code by hand.
|
||||
//! Of course, in most cases this cost is already negligible, but for crates which use
|
||||
//! large blobs of data this crate can come in handy, improving startup times.
|
||||
//!
|
||||
//! ## Usage
|
||||
//! In general, to embed some code into crate, you have to use the build script
|
||||
//! and [`include!`][include] macro. Inside the build script, you'll generate
|
||||
//! some code with one of the [functions][funcs] provided by `corona`,
|
||||
//! and then include the generated file, like this:
|
||||
//! ```ignore
|
||||
//! let value = include!(concat!(env!(OUT_DIR), "/file_name.rs"));
|
||||
//! ```
|
||||
//!
|
||||
//! ## Limitations
|
||||
//! There are some cases when `corona` will be unable to generate valid code. Namely:
|
||||
//! 1. Since Serde doesn't provide us the full path to the type in question (and in most cases it's simply unable to),
|
||||
//! all the structs and enums used during value construction must be in scope.
|
||||
//! As a consequence, all of them must have distinct names - otherwise, there will be name clashes.
|
||||
//! 2. This serializer is intended for use with derived implementation. It may return bogus results
|
||||
//! when used with customized `Serialize`.
|
||||
//! 3. It is impossible to consume code for the type with private fields outside from the module it is defined in.
|
||||
//! In fact, to be able to use this type with `corona`, you'll have to distribute two copies of your crate,
|
||||
//! one of which would only export the definition with derived `Serialize` to be used by serializer
|
||||
//! during the build-time of the second copy. (Isn't this a bit too complex?)
|
||||
//! 4. It is impossible to use empty tuple structs (i.e. `Empty()`).
|
||||
//! From the Serde's point of view, they are indistinguishable from unit structs (i.e. `Unit`),
|
||||
//! but the same Rust syntax can't be used for both, and, since ordinary unit structs are much
|
||||
//! more common, it was decided to correctly handle them.
|
||||
//!
|
||||
//! [include]: https://doc.rust-lang.org/stable/std/macro.include.html
|
||||
|
||||
pub mod error;
|
||||
pub mod ser;
|
||||
|
||||
pub fn write(value: impl Serialize, target: impl std::io::Write) -> SerResult {
|
||||
value.serialize(&mut ser::Corona::new(target))
|
||||
}
|
||||
|
||||
pub fn to_file(value: impl Serialize, target: impl AsRef<std::path::Path>) -> SerResult {
|
||||
value.serialize(&mut ser::Corona::new(std::fs::File::create(target)?))
|
||||
}
|
||||
|
||||
pub fn to_string(value: impl Serialize) -> Result<String, error::CoronaError> {
|
||||
let mut out = Vec::new();
|
||||
value.serialize(&mut ser::Corona::new(&mut out))?;
|
||||
Ok(String::from_utf8(out)?)
|
||||
}
|
||||
pub mod funcs;
|
||||
pub use funcs::{to_file, to_out_dir, to_string, write};
|
||||
@@ -1,9 +1,15 @@
|
||||
//! Implementation of the Corona serializer.
|
||||
|
||||
use crate::error::CoronaError;
|
||||
use serde::ser;
|
||||
use std::io::Write;
|
||||
|
||||
pub(crate) type SerResult = Result<(), CoronaError>;
|
||||
|
||||
/// Main serializer implementation.
|
||||
///
|
||||
/// Users are usually encouraged to use [`to_out_dir`][crate::funcs::to_out_dir] or, in special cases,
|
||||
/// [`to_file`][crate::funcs::to_file], [`write`][crate::funcs::write] or [`to_string`][crate::funcs::to_string].
|
||||
pub struct Corona<W: Write> {
|
||||
writer: W,
|
||||
inside: bool,
|
||||
|
||||
Reference in New Issue
Block a user