Remove split-out Rust implementation
This commit is contained in:
parent
be32f9b7c8
commit
1798e64615
1
Makefile
1
Makefile
|
@ -22,4 +22,3 @@ test-all:
|
|||
(cd implementations/javascript; npm test)
|
||||
(cd implementations/python; make test)
|
||||
(cd implementations/racket/preserves; make testonly)
|
||||
(cd implementations/rust; cargo test)
|
||||
|
|
16
README.md
16
README.md
|
@ -38,14 +38,14 @@ automatic, perfect-fidelity conversion between syntaxes.
|
|||
|
||||
#### Implementations of the data model, plus Preserves textual and binary transfer syntax
|
||||
|
||||
| Language[^pre-alpha-implementations] | Code | Package | Docs |
|
||||
|-----------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------|
|
||||
| Nim | [git.syndicate-lang.org](https://git.syndicate-lang.org/ehmry/preserves-nim) | | |
|
||||
| Python | [preserves.dev]({{page.projecttree}}/implementations/python/) | [`pip install preserves`](https://pypi.org/project/preserves/) | [docs](python/latest/) |
|
||||
| Racket | [preserves.dev]({{page.projecttree}}/implementations/racket/preserves/) | [`raco pkg install preserves`](https://pkgs.racket-lang.org/package/preserves) | |
|
||||
| Rust | [preserves.dev]({{page.projecttree}}/implementations/rust/) | [`cargo add preserves`](https://crates.io/crates/preserves) | [docs](https://docs.rs/preserves/latest/) |
|
||||
| Squeak Smalltalk | [SqueakSource](https://squeaksource.com/Preserves.html) | `Installer ss project: 'Preserves';`<br>` install: 'Preserves'` | |
|
||||
| TypeScript/JavaScript | [preserves.dev]({{page.projecttree}}/implementations/javascript/) | [`yarn add @preserves/core`](https://www.npmjs.com/package/@preserves/core) | |
|
||||
| Language[^pre-alpha-implementations] | Code | Package | Docs |
|
||||
|--------------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------|
|
||||
| Nim | [git.syndicate-lang.org](https://git.syndicate-lang.org/ehmry/preserves-nim) | | |
|
||||
| Python | [preserves.dev]({{page.projecttree}}/implementations/python/) | [`pip install preserves`](https://pypi.org/project/preserves/) | [docs](python/latest/) |
|
||||
| Racket | [preserves.dev]({{page.projecttree}}/implementations/racket/preserves/) | [`raco pkg install preserves`](https://pkgs.racket-lang.org/package/preserves) | |
|
||||
| Rust | [preserves.dev](https://gitlab.com/preserves/preserves-rs/) | [`cargo add preserves`](https://crates.io/crates/preserves) | [docs](https://docs.rs/preserves/latest/) |
|
||||
| Squeak Smalltalk | [SqueakSource](https://squeaksource.com/Preserves.html) | `Installer ss project: 'Preserves';`<br>` install: 'Preserves'` | |
|
||||
| TypeScript/JavaScript | [preserves.dev]({{page.projecttree}}/implementations/javascript/) | [`yarn add @preserves/core`](https://www.npmjs.com/package/@preserves/core) | |
|
||||
|
||||
[^pre-alpha-implementations]: Pre-alpha implementations also exist for
|
||||
[C]({{page.projecttree}}/implementations/c/) and
|
||||
|
|
|
@ -31,7 +31,6 @@ fi
|
|||
# Ensure that various copies of schema.prs, schema.bin, path.bin,
|
||||
# samples.pr and samples.bin are in fact identical.
|
||||
${COMMAND} path/path.bin implementations/python/preserves/path.prb
|
||||
${COMMAND} path/path.bin implementations/rust/preserves-path/path.bin
|
||||
|
||||
${COMMAND} schema/schema.bin implementations/python/preserves/schema.prb
|
||||
${COMMAND} schema/schema.prs implementations/racket/preserves/preserves-schema/schema.prs
|
||||
|
@ -40,11 +39,4 @@ ${COMMAND} tests/samples.bin implementations/python/tests/samples.bin
|
|||
${COMMAND} tests/samples.pr implementations/python/tests/samples.pr
|
||||
${COMMAND} tests/samples.pr implementations/racket/preserves/preserves/tests/samples.pr
|
||||
|
||||
${COMMAND} _includes/what-is-preserves.md implementations/rust/preserves/doc/what-is-preserves.md
|
||||
${COMMAND} _includes/cheatsheet-binary-plaintext.md implementations/rust/preserves/doc/cheatsheet-binary-plaintext.md
|
||||
${COMMAND} _includes/cheatsheet-text-plaintext.md implementations/rust/preserves/doc/cheatsheet-text-plaintext.md
|
||||
${COMMAND} _includes/value-grammar.md implementations/rust/preserves/doc/value-grammar.md
|
||||
|
||||
${COMMAND} _includes/what-is-preserves-schema.md implementations/rust/preserves-schema/doc/what-is-preserves-schema.md
|
||||
|
||||
[ -z "$failed" ]
|
||||
|
|
|
@ -13,9 +13,9 @@ Here you may find:
|
|||
- [racket](racket/), an implementation for Racket 7.x and newer
|
||||
(though older Rackets may also work with it).
|
||||
|
||||
- [rust](rust/), an implementation for Rust that interoperates with
|
||||
serde.
|
||||
|
||||
Other implementations are also available:
|
||||
|
||||
- [Preserves for Rust](https://gitlab.com/preserves/preserves-rs/), an implementation for Rust
|
||||
that interoperates with serde.
|
||||
|
||||
- [Preserves for Squeak Smalltalk](https://squeaksource.com/Preserves.html)
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
Cargo.lock
|
||||
scratch/
|
||||
target/
|
|
@ -1,15 +0,0 @@
|
|||
[workspace]
|
||||
exclude = [
|
||||
"examples/schema-no-build",
|
||||
]
|
||||
|
||||
members = [
|
||||
"preserves",
|
||||
"preserves-path",
|
||||
"preserves-schema",
|
||||
"preserves-schema-macros",
|
||||
"preserves-tools",
|
||||
]
|
||||
|
||||
[profile.bench]
|
||||
debug = true
|
|
@ -1,41 +0,0 @@
|
|||
# Use cargo release to manage publication and versions etc.
|
||||
#
|
||||
# cargo install cargo-release
|
||||
|
||||
all:
|
||||
cargo build --all-targets
|
||||
|
||||
doc:
|
||||
cargo doc --workspace
|
||||
|
||||
x86_64-binary: x86_64-binary-release
|
||||
|
||||
x86_64-binary-release:
|
||||
cross build --target=x86_64-unknown-linux-musl --release --all-targets
|
||||
|
||||
armv7-binary: armv7-binary-release
|
||||
|
||||
armv7-binary-release:
|
||||
cross build --target=armv7-unknown-linux-musleabihf --release --all-targets
|
||||
|
||||
aarch64-binary: aarch64-binary-release
|
||||
|
||||
aarch64-binary-release:
|
||||
cross build --target=aarch64-unknown-linux-musl --release --all-targets
|
||||
|
||||
test:
|
||||
cargo test
|
||||
|
||||
test-all:
|
||||
cargo test --all-targets
|
||||
|
||||
ws-bump:
|
||||
cargo workspaces version \
|
||||
--no-global-tag \
|
||||
--individual-tag-prefix 'rust-%n@' \
|
||||
--allow-branch 'main' \
|
||||
--ignore-changes '../*'
|
||||
|
||||
ws-publish:
|
||||
cargo workspaces publish \
|
||||
--from-git
|
|
@ -0,0 +1,13 @@
|
|||
# Split out to separate repository
|
||||
|
||||
The Rust implementation of Preserves has been split out into a separate git repository,
|
||||
<https://gitlab.com/preserves/preserves-rs>.
|
||||
|
||||
The final released versions that were here were
|
||||
|
||||
- `preserves` v4.992.2,
|
||||
- `preserves-schema` v5.992.0,
|
||||
- `preserves-tools` v4.992.2, and
|
||||
- `preserves-path` v5.992.0.
|
||||
|
||||
Subsequent releases live in the other repository.
|
|
@ -1,10 +0,0 @@
|
|||
[package]
|
||||
name = "schema-no-build"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1"
|
||||
preserves = { path = "../../preserves" }
|
||||
preserves-schema = { path = "../../preserves-schema" }
|
||||
preserves-schema-macros = { path = "../../preserves-schema-macros" }
|
|
@ -1,27 +0,0 @@
|
|||
use preserves::value::IOValue;
|
||||
use preserves_schema_macros::compile_preserves_schemas;
|
||||
|
||||
compile_preserves_schemas!(
|
||||
crate::schemas,
|
||||
load("<CARGO_MANIFEST_DIR>/../../../../path/path.bin"),
|
||||
external_module(EntityRef = crate::demo_entity_ref),
|
||||
);
|
||||
|
||||
pub mod demo_entity_ref {
|
||||
use preserves::value::IOValue;
|
||||
pub type Cap = IOValue;
|
||||
}
|
||||
|
||||
preserves_schema::define_language!(language(): Language<IOValue> {
|
||||
demo: crate::schemas::Language,
|
||||
});
|
||||
|
||||
fn main() {
|
||||
use crate::schemas::path::*;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves_schema::support::Unparse;
|
||||
println!("Hello, world! {:?}", (Filter::Compare {
|
||||
op: Box::new(Comparison::Eq),
|
||||
literal: IOValue::new(123),
|
||||
}).unparse(language()));
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "preserves-path"
|
||||
version = "5.993.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Implementation of preserves-path, a query language for Preserves documents."
|
||||
homepage = "https://preserves.dev/"
|
||||
repository = "https://gitlab.com/preserves/preserves"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[build-dependencies]
|
||||
preserves-schema = { path = "../preserves-schema", version = "5.993.0"}
|
||||
|
||||
[dependencies]
|
||||
preserves = { path = "../preserves", version = "4.993.0"}
|
||||
preserves-schema = { path = "../preserves-schema", version = "5.993.0"}
|
||||
|
||||
num = "0.4"
|
||||
regex = "1.5"
|
||||
thiserror = "1.0"
|
||||
|
||||
[package.metadata.workspaces]
|
||||
independent = true
|
|
@ -1,18 +0,0 @@
|
|||
use preserves_schema::compiler::*;
|
||||
|
||||
use std::io::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let buildroot = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
let mut gen_dir = buildroot.clone();
|
||||
gen_dir.push("src/schemas");
|
||||
|
||||
let mut c = CompilerConfig::new("crate::schemas".to_owned());
|
||||
|
||||
let inputs = expand_inputs(&vec!["path.bin".to_owned()])?;
|
||||
c.load_schemas_and_bundles(&inputs, &vec![])?;
|
||||
|
||||
compile(&c, &mut CodeCollector::files(gen_dir))
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
´³schema·³version°³definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ±descendants´³rec´³lit³descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ±annotations´³rec´³lit³annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„µ±Function´³refµ„³Function„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³
|
||||
Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Function´³rec´³lit³count„´³tupleµ´³named³selector´³refµ„³Selector„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Double´³lit³Double„„µ±
SignedInteger´³lit³
SignedInteger„„µ±String´³lit³String„„µ±
|
||||
ByteString´³lit³
|
||||
ByteString„„µ±Symbol´³lit³Symbol„„µ±Record´³lit³Record„„µ±Sequence´³lit³Sequence„„µ±Set´³lit³Set„„µ±
|
||||
Dictionary´³lit³
|
||||
Dictionary„„µ±Embedded´³lit³Embedded„„„„³
|
||||
Comparison´³orµµ±eq´³lit³eq„„µ±ne´³lit³ne„„µ±lt´³lit³lt„„µ±ge´³lit³ge„„µ±gt´³lit³gt„„µ±le´³lit³le„„„„„³embeddedType€„„
|
|
@ -1,52 +0,0 @@
|
|||
use preserves::value::IOValue;
|
||||
|
||||
use preserves_schema::compiler::load_schema_or_bundle;
|
||||
use preserves_schema::gen::schema::Definition;
|
||||
|
||||
use std::io;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Env(pub preserves_schema::support::interpret::Env<IOValue>);
|
||||
|
||||
pub struct Context<'a> {
|
||||
pub env: &'a Env,
|
||||
path: Vec<IOValue>,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(env: &'a Env) -> Self {
|
||||
Context {
|
||||
env,
|
||||
path: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_path_step<R, F: FnOnce(&mut Self) -> R>(&mut self, v: &IOValue, f: F) -> R {
|
||||
self.path.push(v.clone());
|
||||
let result = f(self);
|
||||
self.path.pop();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn load_bundle(&mut self, filename: &std::path::PathBuf) -> io::Result<bool> {
|
||||
load_schema_or_bundle(&mut self.0, filename)
|
||||
}
|
||||
|
||||
pub fn lookup_definition(
|
||||
&self,
|
||||
module: &Vec<String>,
|
||||
name: &str,
|
||||
) -> Option<&Definition<IOValue>> {
|
||||
self.0.get(module).and_then(|s| s.definitions.0.get(name))
|
||||
}
|
||||
|
||||
pub fn to_context(&self) -> Context {
|
||||
Context::new(self)
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompilationError {
|
||||
#[error(transparent)]
|
||||
IoError(#[from] io::Error),
|
||||
#[error("Cannot mix binary operators")]
|
||||
MixedOperators,
|
||||
#[error("Invalid step")]
|
||||
InvalidStep,
|
||||
#[error("Undefined schema definition name: {0}")]
|
||||
UndefinedSchemaDefinitionName(String),
|
||||
#[error(transparent)]
|
||||
RegexError(#[from] regex::Error),
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
pub mod context;
|
||||
pub mod error;
|
||||
pub mod parse;
|
||||
pub mod predicate;
|
||||
|
||||
pub mod schemas {
|
||||
include!(concat!(env!("OUT_DIR"), "/src/schemas/mod.rs"));
|
||||
}
|
||||
|
||||
pub mod step;
|
||||
|
||||
pub use context::Context;
|
||||
pub use context::Env;
|
||||
|
||||
pub use error::CompilationError;
|
||||
|
||||
pub use parse::parse_predicate;
|
||||
pub use parse::parse_selector;
|
||||
|
||||
pub use schemas::path::Predicate;
|
||||
pub use schemas::path::Selector;
|
||||
pub use schemas::path::Step;
|
||||
|
||||
pub use step::Node;
|
|
@ -1,322 +0,0 @@
|
|||
use crate::context::Env;
|
||||
use crate::schemas::path;
|
||||
use crate::step::Node;
|
||||
use crate::CompilationError;
|
||||
|
||||
use preserves::value::BinarySource;
|
||||
use preserves::value::BytesBinarySource;
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::Reader;
|
||||
|
||||
use std::iter::Iterator;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Binop {
|
||||
Union,
|
||||
Intersection,
|
||||
}
|
||||
|
||||
fn split_values_by_symbol<'a>(tokens: &'a [IOValue], separator: &str) -> Vec<&'a [IOValue]> {
|
||||
tokens
|
||||
.split(|t| matches!(t.value().as_symbol(), Some(s) if s == separator))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn split_binop(tokens: &[IOValue]) -> Result<(Vec<&[IOValue]>, Option<Binop>), CompilationError> {
|
||||
let union_pieces = split_values_by_symbol(&tokens, "+");
|
||||
let intersection_pieces = split_values_by_symbol(&tokens, "&");
|
||||
match (union_pieces.len(), intersection_pieces.len()) {
|
||||
(1, 1) => Ok((union_pieces, None)),
|
||||
(_, 1) => Ok((union_pieces, Some(Binop::Union))),
|
||||
(1, _) => Ok((intersection_pieces, Some(Binop::Intersection))),
|
||||
_ => Err(CompilationError::MixedOperators),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_selector(env: &Env, tokens: &[IOValue]) -> Result<path::Selector, CompilationError> {
|
||||
let mut steps = Vec::new();
|
||||
let mut tokens = tokens;
|
||||
while let Some((s, remaining)) = parse_step(env, tokens)? {
|
||||
steps.push(s);
|
||||
tokens = remaining;
|
||||
}
|
||||
Ok(path::Selector(steps))
|
||||
}
|
||||
|
||||
pub fn parse_predicate(env: &Env, tokens: &[IOValue]) -> Result<path::Predicate, CompilationError> {
|
||||
let (pieces, binop) = split_binop(tokens)?;
|
||||
match binop {
|
||||
None => parse_non_binop(env, &pieces[0]),
|
||||
Some(o) => {
|
||||
let preds = pieces
|
||||
.into_iter()
|
||||
.map(|ts| parse_non_binop(env, &ts))
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(match o {
|
||||
Binop::Union => path::Predicate::Or { preds },
|
||||
Binop::Intersection => path::Predicate::And { preds },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_non_binop(env: &Env, tokens: &[IOValue]) -> Result<path::Predicate, CompilationError> {
|
||||
if !tokens.is_empty() {
|
||||
let t = tokens[0].value();
|
||||
|
||||
if let Some("!") = t.as_symbol().map(|s| s.as_str()) {
|
||||
return Ok(path::Predicate::Not {
|
||||
pred: Box::new(parse_non_binop(env, &tokens[1..])?),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(path::Predicate::Selector(Box::new(parse_selector(
|
||||
env, tokens,
|
||||
)?)))
|
||||
}
|
||||
|
||||
fn parse_schema_definition_name(
|
||||
env: &Env,
|
||||
token: &IOValue,
|
||||
) -> Result<(Vec<String>, String), CompilationError> {
|
||||
let defpath = token
|
||||
.value()
|
||||
.to_symbol()
|
||||
.map_err(|_| CompilationError::InvalidStep)?;
|
||||
let mut module: Vec<String> = defpath.split('.').map(|s| s.to_string()).collect();
|
||||
let name = module
|
||||
.pop()
|
||||
.expect("at least one element in the Schema name");
|
||||
match env.lookup_definition(&module, &name) {
|
||||
Some(_) => Ok((module, name)),
|
||||
None => Err(CompilationError::UndefinedSchemaDefinitionName(format!(
|
||||
"{:?}",
|
||||
token
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_step<'a>(
|
||||
env: &Env,
|
||||
tokens: &'a [IOValue],
|
||||
) -> Result<Option<(path::Step, &'a [IOValue])>, CompilationError> {
|
||||
if tokens.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let remainder = &tokens[1..];
|
||||
|
||||
if tokens[0].value().is_sequence() {
|
||||
return Ok(Some((
|
||||
path::Step::Filter(Box::new(path::Filter::Test {
|
||||
pred: Box::new(parse_predicate(
|
||||
env,
|
||||
tokens[0].value().as_sequence().unwrap(),
|
||||
)?),
|
||||
})),
|
||||
remainder,
|
||||
)));
|
||||
}
|
||||
|
||||
match tokens[0].value().as_record(None) {
|
||||
None => (),
|
||||
Some(r) => match r.label().value().as_symbol() {
|
||||
None => return Err(CompilationError::InvalidStep),
|
||||
Some(t) => match t.as_str() {
|
||||
"count" => {
|
||||
return Ok(Some((
|
||||
path::Step::Function(Box::new(path::Function {
|
||||
selector: parse_selector(env, r.fields())?,
|
||||
})),
|
||||
remainder,
|
||||
)))
|
||||
}
|
||||
_ => return Err(CompilationError::InvalidStep),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
match tokens[0].value().as_symbol() {
|
||||
None => return Err(CompilationError::InvalidStep),
|
||||
Some(t) => match t.as_str() {
|
||||
"/" => Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Values)),
|
||||
remainder,
|
||||
))),
|
||||
"//" => Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Descendants)),
|
||||
remainder,
|
||||
))),
|
||||
"." => {
|
||||
let (key, remainder) = pop_step_arg(remainder)?;
|
||||
Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::At { key })),
|
||||
remainder,
|
||||
)))
|
||||
}
|
||||
".^" => Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Label)),
|
||||
remainder,
|
||||
))),
|
||||
".keys" => Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Keys)),
|
||||
remainder,
|
||||
))),
|
||||
".length" => Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Length)),
|
||||
remainder,
|
||||
))),
|
||||
".annotations" => Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Annotations)),
|
||||
remainder,
|
||||
))),
|
||||
".embedded" => Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Embedded)),
|
||||
remainder,
|
||||
))),
|
||||
"%" => {
|
||||
let (defpath, remainder) = pop_step_arg(remainder)?;
|
||||
let (module, name) = parse_schema_definition_name(env, &defpath)?;
|
||||
Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Parse { module, name })),
|
||||
remainder,
|
||||
)))
|
||||
}
|
||||
"%-" => {
|
||||
let (defpath, remainder) = pop_step_arg(remainder)?;
|
||||
let (module, name) = parse_schema_definition_name(env, &defpath)?;
|
||||
Ok(Some((
|
||||
path::Step::Axis(Box::new(path::Axis::Unparse { module, name })),
|
||||
remainder,
|
||||
)))
|
||||
}
|
||||
"*" => Ok(Some((
|
||||
path::Step::Filter(Box::new(path::Filter::Nop)),
|
||||
remainder,
|
||||
))),
|
||||
"eq" | "=" => parse_comparison(remainder, path::Comparison::Eq),
|
||||
"ne" | "!=" => parse_comparison(remainder, path::Comparison::Ne),
|
||||
"lt" => parse_comparison(remainder, path::Comparison::Lt),
|
||||
"gt" => parse_comparison(remainder, path::Comparison::Gt),
|
||||
"le" => parse_comparison(remainder, path::Comparison::Le),
|
||||
"ge" => parse_comparison(remainder, path::Comparison::Ge),
|
||||
"re" | "=r" => {
|
||||
let (regex_val, remainder) = pop_step_arg(remainder)?;
|
||||
let regex = regex_val
|
||||
.value()
|
||||
.to_string()
|
||||
.map_err(|_| CompilationError::InvalidStep)?
|
||||
.clone();
|
||||
let _ = regex::Regex::new(®ex)?;
|
||||
Ok(Some((
|
||||
path::Step::Filter(Box::new(path::Filter::Regex { regex })),
|
||||
remainder,
|
||||
)))
|
||||
}
|
||||
"^" => {
|
||||
let (literal, remainder) = pop_step_arg(remainder)?;
|
||||
Ok(Some((
|
||||
path::Step::Filter(Box::new(path::Filter::Test {
|
||||
pred: Box::new(path::Predicate::Selector(Box::new(path::Selector(vec![
|
||||
path::Step::Axis(Box::new(path::Axis::Label)),
|
||||
path::Step::Filter(Box::new(path::Filter::Compare {
|
||||
op: Box::new(path::Comparison::Eq),
|
||||
literal,
|
||||
})),
|
||||
])))),
|
||||
})),
|
||||
remainder,
|
||||
)))
|
||||
}
|
||||
|
||||
"~real" => Ok(Some((
|
||||
path::Step::Filter(Box::new(path::Filter::Real)),
|
||||
remainder,
|
||||
))),
|
||||
"~int" => Ok(Some((
|
||||
path::Step::Filter(Box::new(path::Filter::Int)),
|
||||
remainder,
|
||||
))),
|
||||
|
||||
"bool" => Ok(Some((
|
||||
path::Step::from(path::ValueKind::Boolean),
|
||||
remainder,
|
||||
))),
|
||||
"double" => Ok(Some((path::Step::from(path::ValueKind::Double), remainder))),
|
||||
"int" => Ok(Some((
|
||||
path::Step::from(path::ValueKind::SignedInteger),
|
||||
remainder,
|
||||
))),
|
||||
"string" => Ok(Some((path::Step::from(path::ValueKind::String), remainder))),
|
||||
"bytes" => Ok(Some((
|
||||
path::Step::from(path::ValueKind::ByteString),
|
||||
remainder,
|
||||
))),
|
||||
"symbol" => Ok(Some((path::Step::from(path::ValueKind::Symbol), remainder))),
|
||||
"rec" => Ok(Some((path::Step::from(path::ValueKind::Record), remainder))),
|
||||
"seq" => Ok(Some((
|
||||
path::Step::from(path::ValueKind::Sequence),
|
||||
remainder,
|
||||
))),
|
||||
"set" => Ok(Some((path::Step::from(path::ValueKind::Set), remainder))),
|
||||
"dict" => Ok(Some((
|
||||
path::Step::from(path::ValueKind::Dictionary),
|
||||
remainder,
|
||||
))),
|
||||
"embedded" => Ok(Some((
|
||||
path::Step::from(path::ValueKind::Embedded),
|
||||
remainder,
|
||||
))),
|
||||
|
||||
_ => Err(CompilationError::InvalidStep),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl From<path::ValueKind> for path::Step {
|
||||
fn from(k: path::ValueKind) -> Self {
|
||||
path::Step::Filter(Box::new(path::Filter::Kind { kind: Box::new(k) }))
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_step_arg(tokens: &[IOValue]) -> Result<(IOValue, &[IOValue]), CompilationError> {
|
||||
if tokens.is_empty() {
|
||||
return Err(CompilationError::InvalidStep);
|
||||
}
|
||||
Ok((tokens[0].clone(), &tokens[1..]))
|
||||
}
|
||||
|
||||
fn parse_comparison(
|
||||
tokens: &[IOValue],
|
||||
op: path::Comparison,
|
||||
) -> Result<Option<(path::Step, &[IOValue])>, CompilationError> {
|
||||
let (literal, remainder) = pop_step_arg(tokens)?;
|
||||
Ok(Some((
|
||||
path::Step::Filter(Box::new(path::Filter::Compare {
|
||||
op: Box::new(op),
|
||||
literal,
|
||||
})),
|
||||
remainder,
|
||||
)))
|
||||
}
|
||||
|
||||
impl path::Selector {
|
||||
pub fn from_str(env: &Env, s: &str) -> Result<Self, CompilationError> {
|
||||
parse_selector(
|
||||
env,
|
||||
&(BytesBinarySource::new(s.as_bytes())
|
||||
.text_iovalues()
|
||||
.configured(false)
|
||||
.collect::<Result<Vec<_>, _>>()?),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn from_str(env: &Env, s: &str) -> Result<Self, CompilationError> {
|
||||
let expr = path::Selector::from_str(env, s)?;
|
||||
expr.compile()
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
use crate::context::Context;
|
||||
use crate::schemas::path;
|
||||
use crate::step::BoolCollector;
|
||||
use crate::step::Node;
|
||||
use crate::step::StepMaker;
|
||||
use crate::CompilationError;
|
||||
|
||||
use preserves::value::IOValue;
|
||||
|
||||
pub trait Predicate: std::fmt::Debug {
|
||||
fn test(&mut self, ctxt: &mut Context, value: &IOValue) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompiledPredicate {
|
||||
Selector(Node),
|
||||
Not(Box<CompiledPredicate>),
|
||||
Or(Vec<CompiledPredicate>),
|
||||
And(Vec<CompiledPredicate>),
|
||||
}
|
||||
|
||||
impl path::Predicate {
|
||||
pub fn compile(&self) -> Result<CompiledPredicate, CompilationError> {
|
||||
match self {
|
||||
path::Predicate::Selector(b) => Ok(CompiledPredicate::Selector(
|
||||
(&**b).connect(BoolCollector::new())?,
|
||||
)),
|
||||
path::Predicate::Not { pred } => {
|
||||
Ok(CompiledPredicate::Not(Box::new((&**pred).compile()?)))
|
||||
}
|
||||
path::Predicate::Or { preds } => Ok(CompiledPredicate::Or(
|
||||
preds.iter().map(Self::compile).collect::<Result<_, _>>()?,
|
||||
)),
|
||||
path::Predicate::And { preds } => Ok(CompiledPredicate::And(
|
||||
preds.iter().map(Self::compile).collect::<Result<_, _>>()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec(&self, ctxt: &mut Context, value: &IOValue) -> Result<bool, CompilationError> {
|
||||
Ok(self.compile()?.test(ctxt, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate for CompiledPredicate {
|
||||
fn test(&mut self, ctxt: &mut Context, value: &IOValue) -> bool {
|
||||
match self {
|
||||
CompiledPredicate::Selector(n) => n.test(ctxt, value),
|
||||
CompiledPredicate::Not(p) => !p.test(ctxt, value),
|
||||
CompiledPredicate::Or(ps) => {
|
||||
for p in ps.iter_mut() {
|
||||
if p.test(ctxt, value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
CompiledPredicate::And(ps) => {
|
||||
for p in ps.iter_mut() {
|
||||
if !p.test(ctxt, value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,504 +0,0 @@
|
|||
// Selectors operate on IOValues because the AST includes keys of IOValue type.
|
||||
// If we could make Schemas produce generics...
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::predicate::CompiledPredicate;
|
||||
use crate::predicate::Predicate;
|
||||
use crate::schemas::path;
|
||||
use crate::CompilationError;
|
||||
|
||||
use num::bigint::BigInt;
|
||||
use num::traits::cast::FromPrimitive;
|
||||
use num::traits::cast::ToPrimitive;
|
||||
|
||||
use preserves::value::AtomClass;
|
||||
use preserves::value::CompoundClass;
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::Value;
|
||||
use preserves::value::ValueClass;
|
||||
|
||||
use preserves_schema::support::interpret;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::iter::Iterator;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait StepMaker {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError>;
|
||||
}
|
||||
|
||||
pub trait Step: std::fmt::Debug {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue);
|
||||
fn finish(&mut self);
|
||||
fn reset(&mut self) -> Vec<IOValue>;
|
||||
}
|
||||
|
||||
macro_rules! delegate_finish_and_reset {
|
||||
($self:ident, $target:expr) => {
|
||||
fn finish(&mut $self) { $target.finish() }
|
||||
fn reset(&mut $self) -> Vec<IOValue> { $target.reset() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Node(pub Rc<RefCell<dyn Step>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AxisStep {
|
||||
step: Node,
|
||||
axis: path::Axis,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CompareStep {
|
||||
op: path::Comparison,
|
||||
literal: IOValue,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RegexStep {
|
||||
regex: regex::Regex,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestStep {
|
||||
pred: CompiledPredicate,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RealStep {
|
||||
step: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IntStep {
|
||||
step: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VecCollector {
|
||||
accumulator: Vec<IOValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoolCollector {
|
||||
seen_value: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct KindStep {
|
||||
kind: ValueClass,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CountCollector {
|
||||
count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CountStep {
|
||||
step: Node,
|
||||
counter: Node,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn new<S: Step + 'static>(s: S) -> Self {
|
||||
Node(Rc::new(RefCell::new(s)))
|
||||
}
|
||||
|
||||
pub fn test(&self, ctxt: &mut Context, value: &IOValue) -> bool {
|
||||
!self.exec(ctxt, value).is_empty()
|
||||
}
|
||||
|
||||
pub fn accept(&self, ctxt: &mut Context, value: &IOValue) {
|
||||
self.0.borrow_mut().accept(ctxt, value)
|
||||
}
|
||||
|
||||
pub fn finish(&self) {
|
||||
self.0.borrow_mut().finish()
|
||||
}
|
||||
|
||||
pub fn reset(&self) -> Vec<IOValue> {
|
||||
self.0.borrow_mut().reset()
|
||||
}
|
||||
|
||||
pub fn exec(&self, ctxt: &mut Context, value: &IOValue) -> Vec<IOValue> {
|
||||
self.accept(ctxt, value);
|
||||
self.finish();
|
||||
self.reset()
|
||||
}
|
||||
}
|
||||
|
||||
impl StepMaker for path::Selector {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
self.0.connect(step)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: StepMaker> StepMaker for Vec<S> {
|
||||
fn connect(&self, mut step: Node) -> Result<Node, CompilationError> {
|
||||
for s in self.iter().rev() {
|
||||
step = s.connect(step)?;
|
||||
}
|
||||
Ok(step)
|
||||
}
|
||||
}
|
||||
|
||||
impl StepMaker for path::Step {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
match self {
|
||||
path::Step::Axis(b) => (&**b).connect(step),
|
||||
path::Step::Filter(b) => (&**b).connect(step),
|
||||
path::Step::Function(b) => (&**b).connect(step),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StepMaker for path::Axis {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
Ok(Node::new(AxisStep {
|
||||
step,
|
||||
axis: self.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn descendants(ctxt: &mut Context, step: &mut Node, v: &IOValue) {
|
||||
step.accept(ctxt, v);
|
||||
for c in v.value().children() {
|
||||
ctxt.with_path_step(&c, |ctxt| descendants(ctxt, step, &c));
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for AxisStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
ctxt.with_path_step(value, |ctxt| match &self.axis {
|
||||
path::Axis::Values => {
|
||||
for c in value.value().children() {
|
||||
self.step.accept(ctxt, &c)
|
||||
}
|
||||
}
|
||||
path::Axis::Descendants => descendants(ctxt, &mut self.step, value),
|
||||
path::Axis::At { key } => match value.value() {
|
||||
Value::String(s) | Value::Symbol(s) => step_index(
|
||||
ctxt,
|
||||
s.chars(),
|
||||
&key,
|
||||
|c| IOValue::new(String::from(c)),
|
||||
&mut self.step,
|
||||
),
|
||||
Value::Record(r) => {
|
||||
step_index(ctxt, r.fields().iter(), &key, |v| v.clone(), &mut self.step)
|
||||
}
|
||||
Value::Sequence(vs) => {
|
||||
step_index(ctxt, vs.iter(), &key, |v| v.clone(), &mut self.step)
|
||||
}
|
||||
Value::Dictionary(d) => {
|
||||
if let Some(v) = d.get(&key) {
|
||||
self.step.accept(ctxt, v)
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
path::Axis::Label => {
|
||||
if let Some(r) = value.value().as_record(None) {
|
||||
self.step.accept(ctxt, r.label())
|
||||
}
|
||||
}
|
||||
path::Axis::Keys => match value.value() {
|
||||
Value::String(s) | Value::Symbol(s) => step_keys(ctxt, s.len(), &mut self.step),
|
||||
Value::ByteString(bs) => step_keys(ctxt, bs.len(), &mut self.step),
|
||||
Value::Record(r) => step_keys(ctxt, r.arity(), &mut self.step),
|
||||
Value::Sequence(vs) => step_keys(ctxt, vs.len(), &mut self.step),
|
||||
Value::Dictionary(d) => {
|
||||
for k in d.keys() {
|
||||
self.step.accept(ctxt, k)
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
path::Axis::Length => match value.value() {
|
||||
Value::String(s) | Value::Symbol(s) => {
|
||||
self.step.accept(ctxt, &IOValue::new(s.len()))
|
||||
}
|
||||
Value::ByteString(bs) => self.step.accept(ctxt, &IOValue::new(bs.len())),
|
||||
Value::Record(r) => self.step.accept(ctxt, &IOValue::new(r.arity())),
|
||||
Value::Sequence(vs) => self.step.accept(ctxt, &IOValue::new(vs.len())),
|
||||
Value::Dictionary(d) => self.step.accept(ctxt, &IOValue::new(d.len())),
|
||||
_ => self.step.accept(ctxt, &IOValue::new(0)),
|
||||
},
|
||||
path::Axis::Annotations => {
|
||||
for c in value.annotations().slice() {
|
||||
self.step.accept(ctxt, &c)
|
||||
}
|
||||
}
|
||||
path::Axis::Embedded => {
|
||||
if let Some(d) = value.value().as_embedded() {
|
||||
self.step.accept(ctxt, d)
|
||||
}
|
||||
}
|
||||
path::Axis::Parse { module, name } => {
|
||||
if let Some(p) =
|
||||
interpret::Context::new(&ctxt.env.0).dynamic_parse(module, name, value)
|
||||
{
|
||||
self.step.accept(ctxt, &p)
|
||||
}
|
||||
}
|
||||
path::Axis::Unparse { module, name } => {
|
||||
if let Some(p) =
|
||||
interpret::Context::new(&ctxt.env.0).dynamic_unparse(module, name, value)
|
||||
{
|
||||
self.step.accept(ctxt, &p)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
fn step_index<T, Ts: Iterator<Item = T>, F: FnOnce(T) -> IOValue>(
|
||||
ctxt: &mut Context,
|
||||
mut vs: Ts,
|
||||
key: &IOValue,
|
||||
f: F,
|
||||
step: &mut Node,
|
||||
) {
|
||||
if let Some(i) = key.value().as_usize() {
|
||||
match vs.nth(i) {
|
||||
None => (),
|
||||
Some(v) => step.accept(ctxt, &f(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn step_keys(ctxt: &mut Context, count: usize, step: &mut Node) {
|
||||
for i in 0..count {
|
||||
step.accept(ctxt, &IOValue::new(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl StepMaker for path::Filter {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
match self {
|
||||
path::Filter::Nop => Ok(step),
|
||||
path::Filter::Compare { op, literal } => Ok(Node::new(CompareStep {
|
||||
op: (**op).clone(),
|
||||
literal: literal.clone(),
|
||||
step,
|
||||
})),
|
||||
path::Filter::Regex { regex } => Ok(Node::new(RegexStep {
|
||||
regex: regex::Regex::new(regex)?,
|
||||
step,
|
||||
})),
|
||||
path::Filter::Test { pred } => Ok(Node::new(TestStep {
|
||||
pred: (&**pred).compile()?,
|
||||
step,
|
||||
})),
|
||||
path::Filter::Real => Ok(Node::new(RealStep { step })),
|
||||
path::Filter::Int => Ok(Node::new(IntStep { step })),
|
||||
path::Filter::Kind { kind } => Ok(Node::new(KindStep {
|
||||
kind: match &**kind {
|
||||
path::ValueKind::Boolean => ValueClass::Atomic(AtomClass::Boolean),
|
||||
path::ValueKind::Double => ValueClass::Atomic(AtomClass::Double),
|
||||
path::ValueKind::SignedInteger => ValueClass::Atomic(AtomClass::SignedInteger),
|
||||
path::ValueKind::String => ValueClass::Atomic(AtomClass::String),
|
||||
path::ValueKind::ByteString => ValueClass::Atomic(AtomClass::ByteString),
|
||||
path::ValueKind::Symbol => ValueClass::Atomic(AtomClass::Symbol),
|
||||
path::ValueKind::Record => ValueClass::Compound(CompoundClass::Record),
|
||||
path::ValueKind::Sequence => ValueClass::Compound(CompoundClass::Sequence),
|
||||
path::ValueKind::Set => ValueClass::Compound(CompoundClass::Set),
|
||||
path::ValueKind::Dictionary => ValueClass::Compound(CompoundClass::Dictionary),
|
||||
path::ValueKind::Embedded => ValueClass::Embedded,
|
||||
},
|
||||
step,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for CompareStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
if match self.op {
|
||||
path::Comparison::Eq => value == &self.literal,
|
||||
path::Comparison::Ne => value != &self.literal,
|
||||
path::Comparison::Lt => value < &self.literal,
|
||||
path::Comparison::Ge => value >= &self.literal,
|
||||
path::Comparison::Gt => value > &self.literal,
|
||||
path::Comparison::Le => value <= &self.literal,
|
||||
} {
|
||||
self.step.accept(ctxt, value)
|
||||
}
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
impl Step for RegexStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
match value.value() {
|
||||
Value::String(s) | Value::Symbol(s) => {
|
||||
if self.regex.is_match(s) {
|
||||
self.step.accept(ctxt, value)
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
impl Step for TestStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
if self.pred.test(ctxt, value) {
|
||||
self.step.accept(ctxt, value)
|
||||
}
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
impl Step for RealStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
match value.value() {
|
||||
Value::SignedInteger(i) => {
|
||||
if let Some(r) = BigInt::from(i).to_f64() {
|
||||
self.step.accept(ctxt, &IOValue::new(r))
|
||||
}
|
||||
}
|
||||
Value::Double(_) => self.step.accept(ctxt, value),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
impl Step for IntStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
match value.value() {
|
||||
Value::SignedInteger(_) => self.step.accept(ctxt, value),
|
||||
Value::Double(d) => {
|
||||
if let Some(i) = BigInt::from_f64(f64::from(*d)) {
|
||||
self.step.accept(ctxt, &IOValue::new(i))
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
impl VecCollector {
|
||||
fn new() -> Node {
|
||||
Node::new(VecCollector {
|
||||
accumulator: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for VecCollector {
|
||||
fn accept(&mut self, _ctxt: &mut Context, value: &IOValue) {
|
||||
self.accumulator.push(value.clone())
|
||||
}
|
||||
|
||||
fn finish(&mut self) {}
|
||||
|
||||
fn reset(&mut self) -> Vec<IOValue> {
|
||||
std::mem::take(&mut self.accumulator)
|
||||
}
|
||||
}
|
||||
|
||||
impl BoolCollector {
|
||||
pub fn new() -> Node {
|
||||
Node::new(BoolCollector { seen_value: false })
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for BoolCollector {
|
||||
fn accept(&mut self, _ctxt: &mut Context, _value: &IOValue) {
|
||||
self.seen_value = true
|
||||
}
|
||||
|
||||
fn finish(&mut self) {}
|
||||
|
||||
fn reset(&mut self) -> Vec<IOValue> {
|
||||
let result = if self.seen_value {
|
||||
vec![IOValue::new(true)]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
self.seen_value = false;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for KindStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
if value.value_class() == self.kind {
|
||||
self.step.accept(ctxt, value)
|
||||
}
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
impl path::Selector {
|
||||
pub fn compile(&self) -> Result<Node, CompilationError> {
|
||||
self.connect(VecCollector::new())
|
||||
}
|
||||
|
||||
pub fn exec(
|
||||
&self,
|
||||
ctxt: &mut Context,
|
||||
value: &IOValue,
|
||||
) -> Result<Vec<IOValue>, CompilationError> {
|
||||
Ok(self.compile()?.exec(ctxt, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl StepMaker for path::Function {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
// For now, there's just one function: `count`.
|
||||
Ok(Node::new(CountStep {
|
||||
step,
|
||||
counter: self.selector.connect(CountCollector::new())?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl CountCollector {
|
||||
pub fn new() -> Node {
|
||||
Node::new(CountCollector { count: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for CountCollector {
|
||||
fn accept(&mut self, _ctxt: &mut Context, _value: &IOValue) {
|
||||
self.count += 1
|
||||
}
|
||||
|
||||
fn finish(&mut self) {}
|
||||
|
||||
fn reset(&mut self) -> Vec<IOValue> {
|
||||
let result = vec![IOValue::new(self.count)];
|
||||
self.count = 0;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for CountStep {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
let count = &self.counter.exec(ctxt, value)[0];
|
||||
self.step.accept(ctxt, count)
|
||||
}
|
||||
|
||||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "preserves-schema-macros"
|
||||
version = "0.993.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Implementation of Preserves Schema code generation macros for Rust."
|
||||
homepage = "https://preserves.dev/"
|
||||
repository = "https://gitlab.com/preserves/preserves"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
preserves = { path = "../preserves", version = "4.993.0" }
|
||||
preserves-schema = { path = "../preserves-schema", version = "5.993.0" }
|
||||
|
||||
proc-macro2 = { version = "1", features = ["span-locations"] }
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["parsing", "extra-traits", "full"] }
|
||||
|
||||
[package.metadata.workspaces]
|
||||
independent = true
|
|
@ -1,227 +0,0 @@
|
|||
use preserves::value::Map;
|
||||
use preserves_schema::compiler::*;
|
||||
use preserves_schema::compiler::types::Purpose;
|
||||
use preserves_schema::gen::schema::Schema;
|
||||
use proc_macro2::Span;
|
||||
use quote::ToTokens;
|
||||
use quote::quote;
|
||||
use std::fmt::Display;
|
||||
use syn::LitStr;
|
||||
use syn::Token;
|
||||
use syn::parenthesized;
|
||||
use syn::parse::Parser;
|
||||
use syn::punctuated::Punctuated;
|
||||
|
||||
mod kw {
|
||||
use syn::custom_keyword;
|
||||
custom_keyword!(load);
|
||||
custom_keyword!(cross_reference);
|
||||
custom_keyword!(external_module);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Instruction {
|
||||
Namespace(String),
|
||||
Load(LitStr),
|
||||
CrossReference {
|
||||
namespace: String,
|
||||
bundle_path: LitStr,
|
||||
},
|
||||
ExternalModule {
|
||||
module_path: ModulePath,
|
||||
namespace: String,
|
||||
}
|
||||
}
|
||||
|
||||
fn syn_path_string(p: syn::Path) -> String {
|
||||
p.to_token_stream().to_string().replace(&[' ', '\t', '\n', '\r'], "")
|
||||
}
|
||||
|
||||
fn syn_litstr_resolve(s: &syn::LitStr) -> String {
|
||||
let s: String = s.value();
|
||||
match s.chars().nth(0) {
|
||||
Some('/') => s.into(),
|
||||
Some('<') => match &s[1..].split_once('>') {
|
||||
Some((envvar, remainder)) => match std::env::var(envvar) {
|
||||
Ok(p) => p + "/" + remainder,
|
||||
Err(_) => panic!("No such environment variable: {:?}", s),
|
||||
}
|
||||
None => panic!("Invalid relative path syntax: {:?}", s),
|
||||
},
|
||||
_ => panic!("Invalid path syntax: {:?}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Instruction {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::load) {
|
||||
let _: kw::load = input.parse()?;
|
||||
let content;
|
||||
let _ = parenthesized!(content in input);
|
||||
let bundle_path: syn::LitStr = content.parse()?;
|
||||
Ok(Instruction::Load(bundle_path))
|
||||
} else if lookahead.peek(kw::cross_reference) {
|
||||
let _: kw::cross_reference = input.parse()?;
|
||||
let content;
|
||||
let _ = parenthesized!(content in input);
|
||||
let namespace: syn::Path = content.parse()?;
|
||||
let _: Token![=] = content.parse()?;
|
||||
let bundle_path: syn::LitStr = content.parse()?;
|
||||
Ok(Instruction::CrossReference {
|
||||
namespace: syn_path_string(namespace),
|
||||
bundle_path,
|
||||
})
|
||||
} else if lookahead.peek(kw::external_module) {
|
||||
let _: kw::external_module = input.parse()?;
|
||||
let content;
|
||||
let _ = parenthesized!(content in input);
|
||||
let module_path = Punctuated::<syn::Ident, syn::Token![.]>::parse_separated_nonempty(&content)?;
|
||||
let _: Token![=] = content.parse()?;
|
||||
let namespace: syn::Path = content.parse()?;
|
||||
Ok(Instruction::ExternalModule {
|
||||
module_path: module_path.into_iter().map(|p| p.to_string()).collect(),
|
||||
namespace: syn_path_string(namespace),
|
||||
})
|
||||
} else {
|
||||
let ns: syn::Path = input.parse()?;
|
||||
Ok(Instruction::Namespace(syn_path_string(ns)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ModuleTree {
|
||||
own_body: String,
|
||||
children: Map<String, ModuleTree>,
|
||||
}
|
||||
|
||||
impl Default for ModuleTree {
|
||||
fn default() -> Self {
|
||||
ModuleTree {
|
||||
own_body: String::new(),
|
||||
children: Map::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleTree {
|
||||
fn build(outputs: Map<Option<ModulePath>, String>) -> Self {
|
||||
let mut mt = ModuleTree::default();
|
||||
for (p, c) in outputs.into_iter() {
|
||||
match p {
|
||||
None => mt.own_body = c,
|
||||
Some(k) => {
|
||||
let mut r = &mut mt;
|
||||
for e in k { r = mt.children.entry(names::render_modname(&e)).or_default(); }
|
||||
r.own_body = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
mt
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModuleTree {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.own_body)?;
|
||||
for (label, mt) in self.children.iter() {
|
||||
write!(f, "\npub mod {} {{ ", label)?;
|
||||
mt.fmt(f)?;
|
||||
write!(f, "}}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn compile_preserves_schemas(src: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let instructions = Punctuated::<Instruction, syn::Token![,]>::parse_terminated
|
||||
.parse(src)
|
||||
.expect("valid sequence of compile_preserves_schemas instructions");
|
||||
|
||||
let mut namespace = None::<String>;
|
||||
let mut bundles_to_load = Vec::<LitStr>::new();
|
||||
let mut bundles_to_xref = Vec::<LitStr>::new();
|
||||
let mut external_modules = Vec::<ExternalModule>::new();
|
||||
|
||||
for i in instructions.into_iter() {
|
||||
match i {
|
||||
Instruction::Namespace(n) => {
|
||||
if namespace.is_some() {
|
||||
panic!("Only one namespace is permitted")
|
||||
}
|
||||
namespace = Some(n)
|
||||
}
|
||||
Instruction::Load(p) => bundles_to_load.push(p),
|
||||
Instruction::ExternalModule { module_path, namespace } => {
|
||||
external_modules.push(ExternalModule::new(module_path, &namespace));
|
||||
}
|
||||
Instruction::CrossReference { namespace, bundle_path } => {
|
||||
let mut bundle = Map::<ModulePath, Schema>::new();
|
||||
let is_schema = load_schema_or_bundle(&mut bundle, &syn_litstr_resolve(&bundle_path).into())
|
||||
.expect("Invalid schema/bundle binary");
|
||||
bundles_to_xref.push(bundle_path);
|
||||
for (k, _v) in bundle.into_iter() {
|
||||
external_modules.push(if is_schema {
|
||||
ExternalModule::new(k, &namespace)
|
||||
} else {
|
||||
let ns = namespace.clone();
|
||||
let mut pieces = vec![ns.clone()];
|
||||
pieces.extend(
|
||||
k.iter().map(|p| names::render_modname(&p)).collect::<Vec<_>>());
|
||||
ExternalModule::new(k, &pieces.join("::"))
|
||||
.set_fallback_language_types(
|
||||
move |v| vec![format!("{}::Language<{}>", ns, v)].into_iter().collect())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let namespace = namespace.expect("Missing namespace");
|
||||
|
||||
let mut dependency_paths = Vec::<syn::LitStr>::new();
|
||||
|
||||
let mut c = CompilerConfig::new(namespace.clone());
|
||||
for b in bundles_to_load.into_iter() {
|
||||
dependency_paths.push(syn::LitStr::new(&syn_litstr_resolve(&b), Span::call_site()));
|
||||
load_schema_or_bundle_with_purpose(&mut c.bundle, &syn_litstr_resolve(&b).into(), Purpose::Codegen)
|
||||
.expect(&b.value());
|
||||
}
|
||||
for b in bundles_to_xref.into_iter() {
|
||||
dependency_paths.push(syn::LitStr::new(&syn_litstr_resolve(&b), Span::call_site()));
|
||||
load_schema_or_bundle_with_purpose(&mut c.bundle, &syn_litstr_resolve(&b).into(), Purpose::Xref)
|
||||
.expect(&b.value());
|
||||
}
|
||||
for m in external_modules.into_iter() {
|
||||
c.add_external_module(m);
|
||||
}
|
||||
|
||||
let mut outputs = Map::<Option<ModulePath>, String>::new();
|
||||
let mut collector = CodeCollector {
|
||||
emit_mod_declarations: false,
|
||||
collect_module: CodeModuleCollector::Custom {
|
||||
collect_output: &mut |p, c| {
|
||||
outputs.insert(p.cloned(), c.to_owned());
|
||||
Ok(())
|
||||
},
|
||||
},
|
||||
};
|
||||
compile(&c, &mut collector).expect("Compilation failed");
|
||||
|
||||
let top_module_source = format!(
|
||||
"pub mod {} {{ {} }}",
|
||||
names::render_modname(namespace.split("::").last().unwrap()),
|
||||
ModuleTree::build(outputs));
|
||||
let top_module: syn::Item = syn::parse_str(&top_module_source)
|
||||
.expect("Invalid generated code");
|
||||
|
||||
quote!{
|
||||
// TODO: this is ugly, but makes the code depend on the actual schema bundle:
|
||||
// See https://doc.rust-lang.org/nightly/proc_macro/tracked_path/fn.path.html
|
||||
// and https://github.com/rust-lang/rust/issues/99515
|
||||
#( const _: &'static [u8] = include_bytes!(#dependency_paths); )*
|
||||
|
||||
#top_module
|
||||
}.into()
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
[package]
|
||||
name = "preserves-schema"
|
||||
version = "5.993.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Implementation of Preserves Schema code generation and support for Rust."
|
||||
homepage = "https://preserves.dev/"
|
||||
repository = "https://gitlab.com/preserves/preserves"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
preserves = { path = "../preserves", version = "4.993.0"}
|
||||
|
||||
convert_case = "0.4.0"
|
||||
glob = "0.3.0"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.5"
|
||||
structopt = "0.3.14"
|
||||
thiserror = "1.0"
|
||||
|
||||
[package.metadata.workspaces]
|
||||
independent = true
|
|
@ -1,10 +0,0 @@
|
|||
all:
|
||||
@echo please use cargo
|
||||
|
||||
regenerate:
|
||||
cargo run -- \
|
||||
--prefix crate::gen \
|
||||
--support-crate crate \
|
||||
--rustfmt-skip \
|
||||
-o $(CURDIR)/src/gen \
|
||||
../../../schema/schema.bin
|
|
@ -1,6 +0,0 @@
|
|||
```shell
|
||||
cargo add preserves preserves-schema
|
||||
```
|
||||
|
||||
This crate ([`preserves-schema` on crates.io](https://crates.io/crates/preserves-schema)) is an
|
||||
implementation of [Preserves Schema](https://preserves.dev/preserves-schema.html) for Rust.
|
|
@ -1,112 +0,0 @@
|
|||
# Example
|
||||
|
||||
[preserves-schemac]: https://preserves.dev/doc/preserves-schemac.html
|
||||
[preserves-schema-rs]: https://preserves.dev/doc/preserves-schema-rs.html
|
||||
|
||||
Preserves schemas are written in a syntax that (ab)uses [Preserves text
|
||||
syntax][preserves::value::text] as a kind of S-expression. Schema source code looks like this:
|
||||
|
||||
```preserves-schema
|
||||
version 1 .
|
||||
Present = <Present @username string> .
|
||||
Says = <Says @who string @what string> .
|
||||
UserStatus = <Status @username string @status Status> .
|
||||
Status = =here / <away @since TimeStamp> .
|
||||
TimeStamp = string .
|
||||
```
|
||||
|
||||
Conventionally, schema source code is stored in `*.prs` files. In this example, the source code
|
||||
above is placed in `simpleChatProtocol.prs`.
|
||||
|
||||
The Rust code generator for schemas requires not source code, but instances of the [Preserves
|
||||
metaschema](https://preserves.dev/preserves-schema.html#appendix-metaschema). To compile schema
|
||||
source code to metaschema instances, use [preserves-schemac][]:
|
||||
|
||||
```shell
|
||||
yarn global add @preserves/schema
|
||||
preserves-schemac .:simpleChatProtocol.prs > simpleChatProtocol.prb
|
||||
```
|
||||
|
||||
Binary-syntax metaschema instances are conventionally stored in `*.prb` files. If you have a
|
||||
whole directory tree of `*.prs` files, you can supply just "`.`" without the "`:`"-prefixed
|
||||
fileglob part.[^converting-metaschema-to-text] See the [preserves-schemac documentation][preserves-schemac].
|
||||
|
||||
[^converting-metaschema-to-text]:
|
||||
Converting the `simpleChatProtocol.prb` file to Preserves text syntax lets us read the
|
||||
metaschema instance corresponding to the source code:
|
||||
```shell
|
||||
cat simpleChatProtocol.prb | preserves-tool convert
|
||||
```
|
||||
The result:
|
||||
```preserves
|
||||
<bundle {
|
||||
[
|
||||
simpleChatProtocol
|
||||
]: <schema {
|
||||
definitions: {
|
||||
Present: <rec <lit Present> <tuple [
|
||||
<named username <atom String>>
|
||||
]>>
|
||||
Says: <rec <lit Says> <tuple [
|
||||
<named who <atom String>>
|
||||
<named what <atom String>>
|
||||
]>>
|
||||
Status: <or [
|
||||
[
|
||||
"here"
|
||||
<lit here>
|
||||
]
|
||||
[
|
||||
"away"
|
||||
<rec <lit away> <tuple [
|
||||
<named since <ref [] TimeStamp>>
|
||||
]>>
|
||||
]
|
||||
]>
|
||||
TimeStamp: <atom String>
|
||||
UserStatus: <rec <lit Status> <tuple [
|
||||
<named username <atom String>>
|
||||
<named status <ref [] Status>>
|
||||
]>>
|
||||
}
|
||||
embeddedType: #f
|
||||
version: 1
|
||||
}>
|
||||
}>
|
||||
```
|
||||
|
||||
#### Generating Rust code from a schema
|
||||
|
||||
Generate Rust definitions corresponding to a metaschema instance with [preserves-schema-rs][].
|
||||
The best way to use it is to integrate it into your `build.rs` (see [the
|
||||
docs][preserves-schema-rs]), but you can also use it as a standalone command-line tool.
|
||||
|
||||
The following command generates a directory `./rs/chat` containing rust sources for a module
|
||||
that expects to be called `chat` in Rust code:
|
||||
|
||||
```shell
|
||||
preserves-schema-rs --output-dir rs/chat --prefix chat simpleChatProtocol.prb
|
||||
```
|
||||
|
||||
Representative excerpts from one of the generated files, `./rs/chat/simple_chat_protocol.rs`:
|
||||
|
||||
```rust,noplayground
|
||||
pub struct Present {
|
||||
pub username: std::string::String
|
||||
}
|
||||
pub struct Says {
|
||||
pub who: std::string::String,
|
||||
pub what: std::string::String
|
||||
}
|
||||
pub struct UserStatus {
|
||||
pub username: std::string::String,
|
||||
pub status: Status
|
||||
}
|
||||
pub enum Status {
|
||||
Here,
|
||||
Away {
|
||||
since: std::boxed::Box<TimeStamp>
|
||||
}
|
||||
}
|
||||
pub struct TimeStamp(pub std::string::String);
|
||||
```
|
|
@ -1,16 +0,0 @@
|
|||
A Preserves schema connects Preserves `Value`s to host-language data
|
||||
structures. Each definition within a schema can be processed by a
|
||||
compiler to produce
|
||||
|
||||
- a simple host-language *type definition*;
|
||||
|
||||
- a partial *parsing* function from `Value`s to instances of the
|
||||
produced type; and
|
||||
|
||||
- a total *serialization* function from instances of the type to
|
||||
`Value`s.
|
||||
|
||||
Every parsed `Value` retains enough information to always be able to
|
||||
be serialized again, and every instance of a host-language data
|
||||
structure contains, by construction, enough information to be
|
||||
successfully serialized.
|
|
@ -1,64 +0,0 @@
|
|||
//! Command-line Rust code generator for Preserves Schema. See the documentation at
|
||||
//! <https://preserves.dev/doc/preserves-schema-rs.html>.
|
||||
|
||||
use std::io::Error;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use preserves_schema::compiler::CodeCollector;
|
||||
use preserves_schema::compiler::CompilerConfig;
|
||||
use preserves_schema::compiler::ExternalModule;
|
||||
use preserves_schema::compiler::compile;
|
||||
use preserves_schema::compiler::expand_inputs;
|
||||
|
||||
#[derive(Clone, StructOpt, Debug)]
|
||||
struct CommandLine {
|
||||
#[structopt(short, long)]
|
||||
output_dir: PathBuf,
|
||||
|
||||
#[structopt(short, long)]
|
||||
prefix: String,
|
||||
|
||||
#[structopt(long)]
|
||||
support_crate: Option<String>,
|
||||
|
||||
#[structopt(long)]
|
||||
module: Vec<String>,
|
||||
|
||||
#[structopt(long)]
|
||||
xref: Vec<String>,
|
||||
|
||||
#[structopt(long)]
|
||||
rustfmt_skip: bool,
|
||||
|
||||
input_glob: Vec<String>,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let args = CommandLine::from_args();
|
||||
let mut config = CompilerConfig::new(args.prefix);
|
||||
for alias in args.module {
|
||||
let (modulepath_str, target) = {
|
||||
let pieces: Vec<&str> = alias.split('=').collect();
|
||||
if pieces.len() != 2 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
format!("Invalid module alias: {:?}", alias),
|
||||
));
|
||||
}
|
||||
(pieces[0], pieces[1])
|
||||
};
|
||||
let modulepath: Vec<String> = modulepath_str.split('.').map(str::to_owned).collect();
|
||||
config.add_external_module(ExternalModule::new(modulepath, target));
|
||||
}
|
||||
if let Some(c) = args.support_crate {
|
||||
config.support_crate = c;
|
||||
}
|
||||
config.rustfmt_skip = args.rustfmt_skip;
|
||||
config.load_schemas_and_bundles(
|
||||
&expand_inputs(&args.input_glob)?,
|
||||
&expand_inputs(&args.xref)?,
|
||||
)?;
|
||||
compile(&config, &mut CodeCollector::files(args.output_dir))
|
||||
}
|
|
@ -1,377 +0,0 @@
|
|||
use crate::gen::schema::*;
|
||||
use crate::syntax::block::constructors::*;
|
||||
use crate::syntax::block::escape_string;
|
||||
use crate::syntax::block::Item;
|
||||
use crate::*;
|
||||
|
||||
use convert_case::{Case, Casing};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::Map;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::Value;
|
||||
|
||||
use super::names;
|
||||
use super::types;
|
||||
use super::types::Purpose;
|
||||
use super::CompilerConfig;
|
||||
|
||||
pub struct BundleContext<'b> {
|
||||
pub config: &'b CompilerConfig,
|
||||
pub types: Map<Ref, types::TDefinition>,
|
||||
pub literals: Map<IOValue, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub enum ModuleContextMode {
|
||||
TargetModule,
|
||||
TargetToplevel,
|
||||
TargetGeneric,
|
||||
}
|
||||
|
||||
pub struct ModuleContext<'m, 'b> {
|
||||
pub bundle: &'m mut BundleContext<'b>,
|
||||
pub module_path: ModulePath,
|
||||
pub schema: &'m Schema,
|
||||
pub typedefs: Vec<Item>,
|
||||
pub functiondefs: Vec<Item>,
|
||||
pub mode: ModuleContextMode,
|
||||
}
|
||||
|
||||
pub struct FunctionContext<'a, 'm, 'b> {
|
||||
pub error_context: String,
|
||||
pub m: &'a mut ModuleContext<'m, 'b>,
|
||||
pub temp_counter: usize,
|
||||
pub captures: Vec<Capture>,
|
||||
pub capture_mode: CaptureMode,
|
||||
}
|
||||
|
||||
pub struct Capture {
|
||||
pub field_name: String,
|
||||
pub ty: types::TField,
|
||||
pub source_expr: String,
|
||||
}
|
||||
|
||||
pub enum CaptureMode {
|
||||
Definite,
|
||||
Indefinite(Vec<Item>),
|
||||
}
|
||||
|
||||
pub enum RefRenderStyle {
|
||||
Bare,
|
||||
Qualified,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ID_RE: regex::Regex = regex::Regex::new(r"^[a-zA-Z][a-zA-Z_0-9]*$").unwrap();
|
||||
}
|
||||
|
||||
impl<'b> BundleContext<'b> {
|
||||
pub fn new(config: &'b CompilerConfig) -> Self {
|
||||
BundleContext {
|
||||
config,
|
||||
types: config.build_type_cache(),
|
||||
literals: Map::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn any_type(&self) -> &'static str {
|
||||
"_Value"
|
||||
}
|
||||
|
||||
pub fn lookup_definition(&self, r: &Ref) -> Option<(&Definition, Purpose)> {
|
||||
self.config
|
||||
.bundle
|
||||
.get(&r.module.0)
|
||||
.and_then(|s| s.0.definitions.0.get(&r.name).map(|d| (d, s.1)))
|
||||
}
|
||||
|
||||
pub fn type_for_name(&self, r: &Ref) -> Option<&types::TDefinition> {
|
||||
if r.module.0.is_empty() {
|
||||
panic!(
|
||||
"BundleContext::type_for_name with module-relative ref {:?}",
|
||||
r
|
||||
);
|
||||
}
|
||||
let result = self.types.get(r);
|
||||
if result.is_none() && !self.config.external_modules.contains_key(&r.module.0) {
|
||||
panic!("Attempted to lookup unknown type {:?}", r)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn define_literal(&mut self, v: &IOValue) -> String {
|
||||
let prefix = format!("LIT_{}", self.literals.len());
|
||||
let next_id = match v.value() {
|
||||
Value::Boolean(b) => prefix + "_" + &b.to_string(),
|
||||
Value::Symbol(s) => {
|
||||
if ID_RE.is_match(&s) {
|
||||
prefix + "_" + s
|
||||
} else {
|
||||
prefix
|
||||
}
|
||||
}
|
||||
Value::String(s) => {
|
||||
if ID_RE.is_match(&s) {
|
||||
prefix + "_" + s
|
||||
} else {
|
||||
prefix
|
||||
}
|
||||
}
|
||||
Value::SignedInteger(n) => prefix + "_" + &n.to_string(),
|
||||
_ => prefix,
|
||||
};
|
||||
let next_id = next_id.to_case(Case::UpperSnake);
|
||||
format!(
|
||||
"&<_L as Into<&'a {}>>::into(_ctxt).{}",
|
||||
self.language_type(),
|
||||
self.literals.entry(v.clone()).or_insert(next_id)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn generate_module<F: FnOnce(&mut ModuleContext)>(
|
||||
&mut self,
|
||||
path: &Vec<String>,
|
||||
schema: &Schema,
|
||||
mode: ModuleContextMode,
|
||||
items: &mut Map<ModuleContextMode, Vec<Item>>,
|
||||
f: F,
|
||||
) {
|
||||
let mut m = ModuleContext::new(self, &ModulePath(path.clone()), schema, mode);
|
||||
f(&mut m);
|
||||
items.entry(mode).or_default().extend(m.extract());
|
||||
}
|
||||
|
||||
pub fn language_struct_name(&self) -> &'static str {
|
||||
"Language"
|
||||
}
|
||||
|
||||
pub fn language_type_base(&self) -> String {
|
||||
format!(
|
||||
"{}::{}",
|
||||
self.config.fully_qualified_module_prefix.clone(),
|
||||
self.language_struct_name()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn language_type(&self) -> String {
|
||||
format!("{}<{}>", self.language_type_base(), self.any_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m, 'b> ModuleContext<'m, 'b> {
|
||||
pub fn new(
|
||||
bundle: &'m mut BundleContext<'b>,
|
||||
module_path: &ModulePath,
|
||||
schema: &'m Schema,
|
||||
mode: ModuleContextMode,
|
||||
) -> Self {
|
||||
ModuleContext {
|
||||
bundle,
|
||||
module_path: module_path.to_owned(),
|
||||
schema,
|
||||
typedefs: Vec::new(),
|
||||
functiondefs: Vec::new(),
|
||||
mode,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn any_type(&self) -> &'static str {
|
||||
self.bundle.any_type()
|
||||
}
|
||||
|
||||
pub fn reset_mode(&mut self) {
|
||||
self.mode = ModuleContextMode::TargetToplevel;
|
||||
}
|
||||
|
||||
pub fn define_literal(&mut self, v: &IOValue) -> String {
|
||||
self.bundle.define_literal(v)
|
||||
}
|
||||
|
||||
pub fn define_type(&mut self, i: Item) {
|
||||
self.typedefs.push(i)
|
||||
}
|
||||
|
||||
pub fn define_function<F: FnOnce(FunctionContext) -> Item>(
|
||||
&mut self,
|
||||
error_context: &str,
|
||||
f: F,
|
||||
) {
|
||||
let i = f(FunctionContext::new(self, error_context));
|
||||
self.functiondefs.push(i)
|
||||
}
|
||||
|
||||
pub fn render_ref(&self, r: &Ref, style: RefRenderStyle) -> Item {
|
||||
let base = match self.bundle.config.external_modules.get(&r.module.0) {
|
||||
None => {
|
||||
if r.module.0.is_empty() {
|
||||
item(names::render_constructor(&r.name))
|
||||
} else {
|
||||
let mut items = Vec::new();
|
||||
items.push(item(
|
||||
self.bundle.config.fully_qualified_module_prefix.to_owned(),
|
||||
));
|
||||
for p in &r.module.0 {
|
||||
items.push(item(names::render_modname(p)))
|
||||
}
|
||||
items.push(item(names::render_constructor(&r.name)));
|
||||
item(name(items))
|
||||
}
|
||||
}
|
||||
Some(xm) => item(name![
|
||||
xm.rust_namespace.clone(),
|
||||
names::render_constructor(&r.name)
|
||||
]),
|
||||
};
|
||||
let q = self.ref_has_embedded(r);
|
||||
match style {
|
||||
RefRenderStyle::Bare => base,
|
||||
RefRenderStyle::Qualified => {
|
||||
if q {
|
||||
item(seq![base, anglebrackets![self.any_type()]])
|
||||
} else {
|
||||
base
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ref_has_embedded(&self, r: &Ref) -> bool {
|
||||
let r = r.qualify(&self.module_path);
|
||||
self.bundle
|
||||
.type_for_name(&r)
|
||||
.map(|ty| ty.has_embedded(self.bundle))
|
||||
.unwrap_or(false)
|
||||
// ^ TODO: should the "false" be configurable?
|
||||
}
|
||||
|
||||
pub fn parse_unparse_generic_decls(&self, ty: &types::TDefinition) -> Item {
|
||||
let mut lts = ty.language_types(self.bundle);
|
||||
lts.insert(self.bundle.language_type());
|
||||
item(anglebrackets![
|
||||
"'a",
|
||||
seq![
|
||||
"_L: Copy",
|
||||
seq(lts
|
||||
.into_iter()
|
||||
.map(|t| item(seq![" + Into<&'a ", t, ">"]))
|
||||
.collect())
|
||||
],
|
||||
seq![self.any_type(), ": preserves::value::NestedValue + 'a"]
|
||||
])
|
||||
}
|
||||
|
||||
pub fn extract(&mut self) -> Vec<Item> {
|
||||
let mut items = std::mem::take(&mut self.typedefs);
|
||||
items.extend(std::mem::take(&mut self.functiondefs));
|
||||
items
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'm, 'b> FunctionContext<'a, 'm, 'b> {
|
||||
pub fn new(m: &'a mut ModuleContext<'m, 'b>, error_context: &str) -> Self {
|
||||
FunctionContext {
|
||||
error_context: error_context.to_owned(),
|
||||
m,
|
||||
temp_counter: 0,
|
||||
captures: Vec::new(),
|
||||
capture_mode: CaptureMode::Definite,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn capture(&mut self, field_name: String, ty: types::TField, source_expr: String) {
|
||||
self.captures.push(Capture {
|
||||
field_name,
|
||||
ty,
|
||||
source_expr: match self.capture_mode {
|
||||
CaptureMode::Definite => source_expr,
|
||||
CaptureMode::Indefinite(_) => format!(
|
||||
"{}.ok_or_else(|| {:?})?",
|
||||
source_expr,
|
||||
self.conformance_err_code()
|
||||
),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lookup_capture(&self, field_name: &str) -> &Capture {
|
||||
for c in &self.captures {
|
||||
if c.field_name == field_name {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
panic!("No capture for field {:?} available", field_name)
|
||||
}
|
||||
|
||||
pub fn gentempname(&mut self) -> String {
|
||||
let i = self.temp_counter;
|
||||
self.temp_counter += 1;
|
||||
format!("_tmp{}", i)
|
||||
}
|
||||
|
||||
pub fn declare_compound(&self, body: &mut Vec<Item>, name: &str, init_expr: Item) {
|
||||
body.push(item(seq![
|
||||
"let mut ",
|
||||
name.to_owned(),
|
||||
" = ",
|
||||
init_expr,
|
||||
";"
|
||||
]));
|
||||
}
|
||||
|
||||
pub fn define_atom(&mut self, body: &mut Vec<Item>, name: &str, val_expr: Item) {
|
||||
body.push(match &mut self.capture_mode {
|
||||
CaptureMode::Definite => item(seq!["let ", name.to_owned(), " = ", val_expr, ";"]),
|
||||
CaptureMode::Indefinite(items) => {
|
||||
items.push(item(seq!["let mut ", name.to_owned(), " = None;"]));
|
||||
item(seq![name.to_owned(), " = Some(", val_expr, ");"])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_definite_mode<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
|
||||
let saved_mode = std::mem::replace(&mut self.capture_mode, CaptureMode::Definite);
|
||||
let result = f(self);
|
||||
match std::mem::replace(&mut self.capture_mode, saved_mode) {
|
||||
CaptureMode::Definite => (),
|
||||
CaptureMode::Indefinite(_) => panic!("corrupt capture_mode"),
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn with_indefinite_mode<F: FnOnce(&mut Self) -> ()>(&mut self, f: F) -> Vec<Item> {
|
||||
let saved_mode =
|
||||
std::mem::replace(&mut self.capture_mode, CaptureMode::Indefinite(Vec::new()));
|
||||
f(self);
|
||||
match std::mem::replace(&mut self.capture_mode, saved_mode) {
|
||||
CaptureMode::Definite => panic!("corrupt capture_mode"),
|
||||
CaptureMode::Indefinite(declarations) => declarations,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn branch<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
|
||||
let saved_temp_counter = self.temp_counter;
|
||||
let saved_capture_count = self.captures.len();
|
||||
let result = f(self);
|
||||
self.temp_counter = saved_temp_counter;
|
||||
self.captures.truncate(saved_capture_count);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn err_code(&self) -> Item {
|
||||
return item(seq!["Err", parens![self.conformance_err_code()]]);
|
||||
}
|
||||
|
||||
pub fn fully_qualified_error_context(&self) -> String {
|
||||
self.m.module_path.0.join(".") + "." + &self.error_context
|
||||
}
|
||||
|
||||
pub fn conformance_err_code(&self) -> Item {
|
||||
return item(seq![
|
||||
"_support::ParseError::conformance_error",
|
||||
parens![escape_string(&self.fully_qualified_error_context())]
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
use preserves::value::Set;
|
||||
|
||||
use crate::gen::schema::ModulePath;
|
||||
use crate::gen::schema::Ref;
|
||||
|
||||
pub struct WalkState<T> {
|
||||
pub context: T,
|
||||
pub module_path: ModulePath,
|
||||
seen: Set<Ref>,
|
||||
}
|
||||
|
||||
impl<T> WalkState<T> {
|
||||
pub fn new(context: T, module_path: ModulePath) -> Self {
|
||||
WalkState {
|
||||
context,
|
||||
module_path,
|
||||
seen: Set::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle_check<
|
||||
E,
|
||||
F: Fn(&T, &Ref) -> Option<E>,
|
||||
R,
|
||||
Ks: FnOnce(&mut Self, Option<E>) -> R,
|
||||
Kf: FnOnce() -> R,
|
||||
>(
|
||||
&mut self,
|
||||
r: &Ref,
|
||||
step: F,
|
||||
ks: Ks,
|
||||
kf: Kf,
|
||||
) -> R {
|
||||
let r = r.qualify(&self.module_path);
|
||||
if self.seen.contains(&r) {
|
||||
kf()
|
||||
} else {
|
||||
self.seen.insert(r.clone());
|
||||
let maybe_e = step(&self.context, &r);
|
||||
let saved = std::mem::replace(&mut self.module_path, r.module);
|
||||
let result = ks(self, maybe_e);
|
||||
self.module_path = saved;
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,636 +0,0 @@
|
|||
//! Implementation of the Schema-to-Rust compiler; this is the core of the
|
||||
//! [preserves-schema-rs][] program.
|
||||
//!
|
||||
//! See the [documentation for preserves-schema-rs][preserves-schema-rs] for examples of how to
|
||||
//! use the compiler programmatically from a `build.rs` script, but very briefly, use
|
||||
//! [preserves-schemac](https://preserves.dev/doc/preserves-schemac.html) to generate a
|
||||
//! metaschema instance `*.prb` file, and then put something like this in `build.rs`:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! use preserves_schema::compiler::*;
|
||||
//!
|
||||
//! const PATH_TO_PRB_FILE: &'static str = "your-metaschema-instance-file.prb";
|
||||
//!
|
||||
//! fn main() -> Result<(), std::io::Error> {
|
||||
//! let buildroot = std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
//!
|
||||
//! let mut gen_dir = buildroot.clone();
|
||||
//! gen_dir.push("src/schemas");
|
||||
//! let mut c = CompilerConfig::new("crate::schemas".to_owned());
|
||||
//!
|
||||
//! let inputs = expand_inputs(&vec![PATH_TO_PRB_FILE.to_owned()])?;
|
||||
//! c.load_schemas_and_bundles(&inputs, &vec![])?;
|
||||
//! compile(&c, &mut CodeCollector::files(gen_dir))
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! plus something like this in your `lib.rs` or main program:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! pub mod schemas {
|
||||
//! include!(concat!(env!("OUT_DIR"), "/src/schemas/mod.rs"));
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [preserves-schema-rs]: https://preserves.dev/doc/preserves-schema-rs.html
|
||||
|
||||
pub mod context;
|
||||
pub mod cycles;
|
||||
pub mod names;
|
||||
pub mod parsers;
|
||||
pub mod readers;
|
||||
pub mod types;
|
||||
pub mod unparsers;
|
||||
|
||||
use crate::compiler::context::*;
|
||||
use crate::compiler::types::Purpose;
|
||||
use crate::gen::schema;
|
||||
use crate::gen::schema::*;
|
||||
use crate::gen::Language;
|
||||
use crate::syntax::block::constructors::*;
|
||||
use crate::syntax::block::{Formatter, Item};
|
||||
use crate::*;
|
||||
|
||||
use glob::glob;
|
||||
use preserves::value::BinarySource;
|
||||
use preserves::value::BytesBinarySource;
|
||||
use preserves::value::Map;
|
||||
use preserves::value::Reader;
|
||||
use preserves::value::Set;
|
||||
|
||||
use std::fs::DirBuilder;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Names a Schema module within a (collection of) Schema bundle(s).
|
||||
pub type ModulePath = Vec<String>;
|
||||
|
||||
/// Implement this trait to extend the compiler with custom code generation support. The main
|
||||
/// code generators are also implemented as plugins.
|
||||
///
|
||||
/// For an example of its use outside the core compiler, see [`build.rs` for the `syndicate-rs` project](https://git.syndicate-lang.org/syndicate-lang/syndicate-rs/src/commit/60e6c6badfcbcbccc902994f4f32db6048f60d1f/syndicate/build.rs).
|
||||
pub trait Plugin: std::fmt::Debug {
|
||||
/// Use `_module_ctxt` to emit code at a per-module level.
|
||||
fn generate_module(&self, _module_ctxt: &mut ModuleContext) {}
|
||||
|
||||
/// Use `module_ctxt` to emit code at a per-Schema-[Definition] level.
|
||||
fn generate_definition(
|
||||
&self,
|
||||
module_ctxt: &mut ModuleContext,
|
||||
definition_name: &str,
|
||||
definition: &Definition,
|
||||
);
|
||||
}
|
||||
|
||||
pub struct LanguageTypes {
|
||||
pub fallback: Option<Box<dyn Fn(&str) -> Set<String>>>,
|
||||
pub definitions: Map<String, Box<dyn Fn(&str) -> Set<String>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for LanguageTypes {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
f.debug_struct("LanguageTypes")
|
||||
.field("fallback", &self.fallback.as_ref().map(|f| f("_")))
|
||||
.field(
|
||||
"definitions",
|
||||
&self
|
||||
.definitions
|
||||
.iter()
|
||||
.map(|(k, f)| (k.clone(), f("_")))
|
||||
.collect::<Map<String, Set<String>>>(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExternalModule {
|
||||
pub path: ModulePath,
|
||||
pub rust_namespace: String,
|
||||
pub rust_language_types: LanguageTypes,
|
||||
}
|
||||
|
||||
impl ExternalModule {
|
||||
pub fn new(path: ModulePath, rust_namespace: &str) -> Self {
|
||||
ExternalModule {
|
||||
path,
|
||||
rust_namespace: rust_namespace.to_owned(),
|
||||
rust_language_types: LanguageTypes {
|
||||
fallback: None,
|
||||
definitions: Map::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fallback_language_types<F: 'static + Fn(&str) -> Set<String>>(
|
||||
mut self,
|
||||
f: F,
|
||||
) -> Self {
|
||||
self.rust_language_types.fallback = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_definition_language_types<F: 'static + Fn(&str) -> Set<String>>(
|
||||
mut self,
|
||||
d: &str,
|
||||
f: F,
|
||||
) -> Self {
|
||||
if self
|
||||
.rust_language_types
|
||||
.definitions
|
||||
.insert(d.to_owned(), Box::new(f))
|
||||
.is_some()
|
||||
{
|
||||
panic!(
|
||||
"Duplicate language types definition installed: {:?} {:?}",
|
||||
&self.path, d
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to collect output from the compiler.
|
||||
pub enum CodeModuleCollector<'a> {
|
||||
/// Default file-based code emitter.
|
||||
Files {
|
||||
/// Where output Rust code files will be placed.
|
||||
output_dir: PathBuf,
|
||||
},
|
||||
Custom {
|
||||
/// Used to collect the various produced source files.
|
||||
/// Useful for when compiling in e.g. proc_macro context.
|
||||
collect_output: &'a mut dyn FnMut(Option<&ModulePath>, &str) -> io::Result<()>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Used to configure and collect output from the compiler.
|
||||
pub struct CodeCollector<'a> {
|
||||
pub emit_mod_declarations: bool,
|
||||
pub collect_module: CodeModuleCollector<'a>,
|
||||
}
|
||||
|
||||
/// Main entry point to the compiler.
|
||||
#[derive(Debug)]
|
||||
pub struct CompilerConfig {
|
||||
/// All known Schema modules, indexed by [ModulePath] and annotated with a [Purpose].
|
||||
pub bundle: Map<ModulePath, (Schema, Purpose)>,
|
||||
/// Fully-qualified Rust module prefix to use for each generated module.
|
||||
pub fully_qualified_module_prefix: String,
|
||||
/// Rust module path to the [preserves_schema::support][crate::support] module.
|
||||
pub support_crate: String,
|
||||
/// External modules for cross-referencing.
|
||||
pub external_modules: Map<ModulePath, ExternalModule>,
|
||||
/// Plugins active in this compiler instance.
|
||||
pub plugins: Vec<Box<dyn Plugin>>,
|
||||
/// If true, a directive is emitted in each module instructing
|
||||
/// [rustfmt](https://github.com/rust-lang/rustfmt) to ignore it.
|
||||
pub rustfmt_skip: bool,
|
||||
}
|
||||
|
||||
/// Loads a [Schema] or [Bundle] from path `i` into `bundle` for the given `purpose`.
|
||||
///
|
||||
/// If `i` holds a [Schema], then the file stem of `i` is used as the module name when placing
|
||||
/// the schema in `bundle`.
|
||||
pub fn load_schema_or_bundle_with_purpose(
|
||||
bundle: &mut Map<ModulePath, (Schema, Purpose)>,
|
||||
i: &PathBuf,
|
||||
purpose: Purpose,
|
||||
) -> io::Result<()> {
|
||||
let mut inserted = Map::<ModulePath, Schema>::new();
|
||||
load_schema_or_bundle(&mut inserted, i)?;
|
||||
for (k, v) in inserted.into_iter() {
|
||||
bundle.insert(k, (v, purpose));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loads a [Schema] or [Bundle] from raw binary encoded value `input` into `bundle` for the
|
||||
/// given `purpose`.
|
||||
///
|
||||
/// If `input` corresponds to a [Schema], then `prefix` is used as its module name; otherwise,
|
||||
/// it's a [Bundle], and `prefix` is ignored.
|
||||
pub fn load_schema_or_bundle_bin_with_purpose(
|
||||
bundle: &mut Map<ModulePath, (Schema, Purpose)>,
|
||||
prefix: &str,
|
||||
input: &[u8],
|
||||
purpose: Purpose,
|
||||
) -> io::Result<()> {
|
||||
let mut inserted = Map::<ModulePath, Schema>::new();
|
||||
load_schema_or_bundle_bin(&mut inserted, prefix, input)?;
|
||||
for (k, v) in inserted.into_iter() {
|
||||
bundle.insert(k, (v, purpose));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bundle_prefix(i: &PathBuf) -> io::Result<&str> {
|
||||
i.file_stem()
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Bad schema file stem: {:?}", i),
|
||||
)
|
||||
})?
|
||||
.to_str()
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Invalid UTF-8 in schema file name: {:?}", i),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads a [Schema] or [Bundle] from path `i` into `bundle`.
|
||||
///
|
||||
/// If `i` holds a [Schema], then the file stem of `i` is used as the module name when placing
|
||||
/// the schema in `bundle`.
|
||||
///
|
||||
/// Returns true if it was a schema, false if it was a bundle.
|
||||
pub fn load_schema_or_bundle(bundle: &mut Map<ModulePath, Schema>, i: &PathBuf) -> io::Result<bool> {
|
||||
let mut f = File::open(&i)?;
|
||||
let mut bs = vec![];
|
||||
f.read_to_end(&mut bs)?;
|
||||
load_schema_or_bundle_bin(bundle, bundle_prefix(i)?, &bs[..])
|
||||
}
|
||||
|
||||
/// Loads a [Schema] or [Bundle] from raw binary encoded value `input` into `bundle`.
|
||||
///
|
||||
/// If `input` corresponds to a [Schema], then `prefix` is used as its module name; otherwise,
|
||||
/// it's a [Bundle], and `prefix` is ignored.
|
||||
///
|
||||
/// Returns true if it was a schema, false if it was a bundle.
|
||||
pub fn load_schema_or_bundle_bin(
|
||||
bundle: &mut Map<ModulePath, Schema>,
|
||||
prefix: &str,
|
||||
input: &[u8],
|
||||
) -> io::Result<bool> {
|
||||
let mut src = BytesBinarySource::new(input);
|
||||
let mut reader = src.packed_iovalues();
|
||||
let blob = reader.demand_next(false)?;
|
||||
let language = Language::default();
|
||||
|
||||
if let Ok(s) = language.parse(&blob) {
|
||||
bundle.insert(vec![prefix.to_owned()], s);
|
||||
Ok(true)
|
||||
} else if let Ok(Bundle { modules }) = language.parse(&blob) {
|
||||
for (ModulePath(k), v) in modules.0 {
|
||||
bundle.insert(k, v);
|
||||
}
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Invalid schema binary blob {:?}", prefix),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerConfig {
|
||||
/// Construct a [CompilerConfig] configured to use `fully_qualified_module_prefix` as the
|
||||
/// Rust module prefix for generated code.
|
||||
pub fn new(fully_qualified_module_prefix: String) -> Self {
|
||||
CompilerConfig {
|
||||
bundle: Map::new(),
|
||||
fully_qualified_module_prefix,
|
||||
support_crate: "preserves_schema".to_owned(),
|
||||
external_modules: Map::new(),
|
||||
plugins: vec![
|
||||
Box::new(types::TypePlugin),
|
||||
Box::new(readers::ReaderPlugin),
|
||||
Box::new(parsers::ParserPlugin),
|
||||
Box::new(unparsers::UnparserPlugin),
|
||||
],
|
||||
rustfmt_skip: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_external_module(&mut self, m: ExternalModule) {
|
||||
let path = m.path.clone();
|
||||
if self.external_modules.insert(path.clone(), m).is_some() {
|
||||
panic!("Duplicate external module installed: {:?}", path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_schemas_and_bundles(
|
||||
&mut self,
|
||||
inputs: &Vec<PathBuf>,
|
||||
xrefs: &Vec<PathBuf>,
|
||||
) -> io::Result<()> {
|
||||
for i in inputs {
|
||||
load_schema_or_bundle_with_purpose(&mut self.bundle, i, Purpose::Codegen)?;
|
||||
}
|
||||
for i in xrefs {
|
||||
load_schema_or_bundle_with_purpose(&mut self.bundle, i, Purpose::Xref)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_xref_bin(&mut self, prefix: &str, bundle_or_schema: &[u8]) -> io::Result<()> {
|
||||
load_schema_or_bundle_bin_with_purpose(
|
||||
&mut self.bundle,
|
||||
prefix,
|
||||
bundle_or_schema,
|
||||
Purpose::Xref,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_type_cache(&self) -> Map<Ref, types::TDefinition> {
|
||||
self.bundle
|
||||
.iter()
|
||||
.flat_map(|(modpath, s)| {
|
||||
let modpath = ModulePath(modpath.clone());
|
||||
s.0.definitions.0.iter().map(move |(name, def)| {
|
||||
let ty = types::definition_type(&modpath, s.1, name, def);
|
||||
(ty.self_ref.clone(), ty)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_definition(
|
||||
&self,
|
||||
b: &mut BundleContext,
|
||||
k: &ModulePath,
|
||||
v: &Schema,
|
||||
n: &str,
|
||||
d: &Definition,
|
||||
mode: ModuleContextMode,
|
||||
generated: &mut Map<ModuleContextMode, Vec<Item>>,
|
||||
) {
|
||||
b.generate_module(k, v, mode, generated, |m| {
|
||||
for plugin in self.plugins.iter() {
|
||||
plugin.generate_definition(m, n, d);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands a vector of [mod@glob]s to a vector of actual paths.
|
||||
pub fn expand_inputs(globs: &Vec<String>) -> io::Result<Vec<PathBuf>> {
|
||||
let mut result = Vec::new();
|
||||
for g in globs.iter() {
|
||||
for p in
|
||||
glob(g).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?
|
||||
{
|
||||
result.push(p.map_err(glob::GlobError::into_error)?)
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl<'a> CodeCollector<'a> {
|
||||
/// Construct a [CodeCollector] that collects output Rust modules directly into the file
|
||||
/// system tree rooted at `output_dir`.
|
||||
pub fn files(output_dir: PathBuf) -> Self {
|
||||
CodeCollector {
|
||||
emit_mod_declarations: true,
|
||||
collect_module: CodeModuleCollector::Files { output_dir },
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn collect_output(&mut self, module: Option<&ModulePath>, contents: &str) -> io::Result<()> {
|
||||
match &mut self.collect_module {
|
||||
CodeModuleCollector::Files { output_dir } => {
|
||||
let mut output_path = output_dir.clone();
|
||||
if let Some(k) = module {
|
||||
output_path.extend(k);
|
||||
let module_name = output_path
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let module_name = names::render_modname(&module_name);
|
||||
output_path.set_file_name(format!("{}.rs", module_name));
|
||||
} else {
|
||||
output_path.push("mod.rs");
|
||||
}
|
||||
DirBuilder::new().recursive(true).create(output_path.parent().unwrap())?;
|
||||
|
||||
if output_path.exists() {
|
||||
if let Ok(mut f) = File::open(&output_path) {
|
||||
let mut existing_contents = String::new();
|
||||
f.read_to_string(&mut existing_contents)?;
|
||||
if existing_contents == contents {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut f = File::create(output_path)?;
|
||||
f.write_all(contents.as_bytes())
|
||||
}
|
||||
CodeModuleCollector::Custom { collect_output } => {
|
||||
collect_output(module, contents)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ref {
|
||||
pub fn qualify(&self, default_module_path: &schema::ModulePath) -> Ref {
|
||||
if self.module.0.is_empty() {
|
||||
Ref {
|
||||
module: default_module_path.clone(),
|
||||
name: self.name.clone(),
|
||||
}
|
||||
} else {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Schema {
|
||||
pub fn has_embedded_type(&self) -> bool {
|
||||
self.embedded_type != EmbeddedTypeName::False
|
||||
}
|
||||
}
|
||||
|
||||
/// Main entry point: runs the compilation process.
|
||||
pub fn compile<'a>(config: &CompilerConfig, emitter: &mut CodeCollector<'a>) -> io::Result<()> {
|
||||
let mut b = BundleContext::new(config);
|
||||
|
||||
for (k, (v, module_purpose)) in config.bundle.iter() {
|
||||
if *module_purpose != Purpose::Codegen {
|
||||
continue;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
let mut generated = Map::new();
|
||||
|
||||
b.generate_module(k, v, ModuleContextMode::TargetModule, &mut generated, |m| {
|
||||
for plugin in config.plugins.iter() {
|
||||
plugin.generate_module(m);
|
||||
}
|
||||
});
|
||||
|
||||
for (n, d) in &v.definitions.0 {
|
||||
use ModuleContextMode::*;
|
||||
config.generate_definition(&mut b, k, v, n, d, TargetToplevel, &mut generated);
|
||||
config.generate_definition(&mut b, k, v, n, d, TargetGeneric, &mut generated);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
let mut lines: Vec<String> = Vec::new();
|
||||
|
||||
lines.push(Formatter::to_string(vertical(
|
||||
false,
|
||||
seq!["#![allow(unused_parens)]", "#![allow(unused_imports)]"],
|
||||
)));
|
||||
if config.rustfmt_skip {
|
||||
lines.push("#![cfg_attr(rustfmt, rustfmt_skip)]".to_owned());
|
||||
}
|
||||
lines.push(Formatter::to_string(vertical(
|
||||
false,
|
||||
seq![
|
||||
"",
|
||||
"use std::convert::TryFrom;",
|
||||
format!("use {}::support as _support;", &config.support_crate),
|
||||
"use _support::Deserialize;",
|
||||
"use _support::Parse;",
|
||||
"use _support::Unparse;",
|
||||
"use _support::preserves;",
|
||||
"use preserves::value::Domain;",
|
||||
"use preserves::value::NestedValue;",
|
||||
""
|
||||
],
|
||||
)));
|
||||
|
||||
let mut emit_items = |items: Vec<Item>| {
|
||||
if !items.is_empty() {
|
||||
lines.push(Formatter::to_string(vertical(true, seq(items))));
|
||||
lines.push("".to_owned());
|
||||
}
|
||||
};
|
||||
emit_items(generated.remove(&ModuleContextMode::TargetModule).unwrap());
|
||||
emit_items(
|
||||
generated
|
||||
.remove(&ModuleContextMode::TargetToplevel)
|
||||
.unwrap(),
|
||||
);
|
||||
emit_items(generated.remove(&ModuleContextMode::TargetGeneric).unwrap());
|
||||
|
||||
{
|
||||
let contents = lines.join("\n");
|
||||
emitter.collect_output(Some(k), &contents)?;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut lines = Vec::new();
|
||||
|
||||
if config.rustfmt_skip {
|
||||
lines.push("#![cfg_attr(rustfmt, rustfmt_skip)]".to_owned());
|
||||
lines.push("".to_owned());
|
||||
}
|
||||
|
||||
if emitter.emit_mod_declarations {
|
||||
for (modpath, (_, module_purpose)) in config.bundle.iter() {
|
||||
if *module_purpose != Purpose::Codegen {
|
||||
continue;
|
||||
}
|
||||
lines.push(format!(
|
||||
"pub mod {};",
|
||||
names::render_modname(modpath.last().unwrap())
|
||||
));
|
||||
}
|
||||
lines.push("".to_owned());
|
||||
}
|
||||
|
||||
lines.push(format!(
|
||||
"use {}::support as _support;",
|
||||
&config.support_crate
|
||||
));
|
||||
lines.push("use _support::preserves;".to_owned());
|
||||
lines.push("".to_owned());
|
||||
|
||||
lines.push("#[allow(non_snake_case)]".to_owned());
|
||||
lines.push(Formatter::to_string(item(seq![
|
||||
"pub struct ",
|
||||
b.language_struct_name(),
|
||||
anglebrackets!["N: preserves::value::NestedValue"],
|
||||
" ",
|
||||
vertical(
|
||||
false,
|
||||
braces(
|
||||
b.literals
|
||||
.iter()
|
||||
.map(|(value, name)| item(format!("pub {}: N /* {:?} */", name, value)))
|
||||
.collect()
|
||||
)
|
||||
)
|
||||
])));
|
||||
lines.push("".to_owned());
|
||||
lines.push(Formatter::to_string(item(seq![
|
||||
"impl",
|
||||
anglebrackets!["N: preserves::value::NestedValue"],
|
||||
" Default for ",
|
||||
b.language_struct_name(),
|
||||
"<N> ",
|
||||
codeblock![seq![
|
||||
"fn default() -> Self ",
|
||||
codeblock![seq![
|
||||
b.language_struct_name(),
|
||||
" ",
|
||||
vertical(
|
||||
false,
|
||||
braces(
|
||||
b.literals
|
||||
.iter()
|
||||
.map(|(value, name)| {
|
||||
let bs = preserves::value::PackedWriter::encode_iovalue(&value)
|
||||
.unwrap();
|
||||
item(format!(
|
||||
"{}: /* {:?} */ _support::decode_lit(&{:?}).unwrap()",
|
||||
name, value, bs
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
)
|
||||
]]
|
||||
]]
|
||||
])));
|
||||
lines.push("".to_owned());
|
||||
{
|
||||
let mut b = Bundle {
|
||||
modules: Modules(Map::new()),
|
||||
};
|
||||
for (modpath, (schema, purpose)) in config.bundle.iter() {
|
||||
if *purpose == Purpose::Codegen {
|
||||
b.modules
|
||||
.0
|
||||
.insert(ModulePath(modpath.clone()), schema.clone());
|
||||
}
|
||||
}
|
||||
let b_value = Language::default().unparse(&b);
|
||||
let b_bin = preserves::value::PackedWriter::encode_iovalue(&b_value).unwrap();
|
||||
let mut hex_encoded_bundle = String::new();
|
||||
let mut count = 0;
|
||||
for b in b_bin {
|
||||
if count % 16 == 0 {
|
||||
hex_encoded_bundle.push_str("\\\n ");
|
||||
}
|
||||
count += 1;
|
||||
hex_encoded_bundle.push_str(&format!("\\x{:02x}", b));
|
||||
}
|
||||
lines.push(Formatter::to_string(item(seq![
|
||||
"pub fn _bundle() -> &'static [u8] ",
|
||||
codeblock![seq!["b\"", hex_encoded_bundle, "\""]]
|
||||
])));
|
||||
}
|
||||
lines.push("".to_owned());
|
||||
|
||||
let contents = lines.join("\n");
|
||||
emitter.collect_output(None, &contents)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
use convert_case::{Case, Casing};
|
||||
|
||||
pub fn render_constructor(n: &str) -> String {
|
||||
n.to_case(Case::UpperCamel)
|
||||
}
|
||||
|
||||
pub fn render_fieldname(n: &str) -> String {
|
||||
n.to_case(Case::Snake)
|
||||
}
|
||||
|
||||
pub fn render_modname(n: &str) -> String {
|
||||
n.to_case(Case::Snake)
|
||||
}
|
|
@ -1,467 +0,0 @@
|
|||
use crate::gen::schema::*;
|
||||
use crate::syntax::block::constructors::*;
|
||||
use crate::syntax::block::Item;
|
||||
use crate::*;
|
||||
|
||||
use super::context::FunctionContext;
|
||||
use super::context::ModuleContext;
|
||||
use super::context::ModuleContextMode;
|
||||
use super::context::RefRenderStyle;
|
||||
use super::names;
|
||||
use super::types::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParserPlugin;
|
||||
|
||||
impl compiler::Plugin for ParserPlugin {
|
||||
fn generate_definition(
|
||||
&self,
|
||||
module_ctxt: &mut ModuleContext,
|
||||
definition_name: &str,
|
||||
definition: &Definition,
|
||||
) {
|
||||
if let ModuleContextMode::TargetGeneric = module_ctxt.mode {
|
||||
gen_definition_parser(module_ctxt, definition_name, definition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_definition_parser(m: &mut ModuleContext, n: &str, d: &Definition) {
|
||||
let ty = definition_type(&m.module_path, Purpose::Codegen, n, d);
|
||||
|
||||
m.define_function(n, |mut ctxt| {
|
||||
let mut body = vec![];
|
||||
|
||||
match d {
|
||||
Definition::Or {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => {
|
||||
let mut ps = vec![&**pattern_0, &**pattern_1];
|
||||
ps.extend(pattern_n);
|
||||
for NamedAlternative {
|
||||
variant_label: name,
|
||||
pattern: pat,
|
||||
} in ps
|
||||
{
|
||||
let fname = seq![
|
||||
"parse_",
|
||||
names::render_fieldname(n),
|
||||
"_",
|
||||
names::render_fieldname(name)
|
||||
];
|
||||
let ctorname = item(name![
|
||||
names::render_constructor(n),
|
||||
names::render_constructor(name)
|
||||
]);
|
||||
ctxt.m
|
||||
.define_function(&(n.to_owned() + "::" + name), |mut ctxt| {
|
||||
let mut body = Vec::new();
|
||||
let dest = pattern_parser(&mut ctxt, pat, "value", None, &mut body);
|
||||
let dest = dest.as_ref().map(String::as_str);
|
||||
construct(&ctxt, ctorname, false, &pattern_type(pat), dest, &mut body);
|
||||
|
||||
item(seq![
|
||||
"fn ",
|
||||
fname.clone(),
|
||||
ctxt.m.parse_unparse_generic_decls(&ty),
|
||||
"(_ctxt: _L, value: &",
|
||||
ctxt.m.any_type(),
|
||||
") -> ",
|
||||
"std::result::Result<",
|
||||
names::render_constructor(n),
|
||||
ty.generic_arg(ctxt.m),
|
||||
", _support::ParseError> ",
|
||||
codeblock(body)
|
||||
])
|
||||
});
|
||||
body.push(item(seq![
|
||||
"if let Ok(r) = ",
|
||||
fname,
|
||||
"(_ctxt, value) { return Ok(r); }"
|
||||
]));
|
||||
}
|
||||
body.push(item(seq![ctxt.err_code()]));
|
||||
}
|
||||
Definition::And {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => {
|
||||
let mut ps = vec![&**pattern_0, &**pattern_1];
|
||||
ps.extend(pattern_n);
|
||||
for e in &ps {
|
||||
named_pattern_parser(&mut ctxt, e, "value", None, &mut body);
|
||||
}
|
||||
construct(
|
||||
&ctxt,
|
||||
item(names::render_constructor(n)),
|
||||
true,
|
||||
&record_type(&ps),
|
||||
None,
|
||||
&mut body,
|
||||
);
|
||||
}
|
||||
Definition::Pattern(p) => {
|
||||
let dest = pattern_parser(&mut ctxt, p, "value", None, &mut body);
|
||||
let dest = dest.as_ref().map(String::as_str);
|
||||
construct(
|
||||
&ctxt,
|
||||
item(names::render_constructor(n)),
|
||||
true,
|
||||
&pattern_type(p),
|
||||
dest,
|
||||
&mut body,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
item(seq![
|
||||
"impl",
|
||||
ctxt.m.parse_unparse_generic_decls(&ty),
|
||||
" _support::Parse",
|
||||
anglebrackets!["_L", ctxt.m.any_type()],
|
||||
" for ",
|
||||
names::render_constructor(n),
|
||||
ty.generic_arg(ctxt.m),
|
||||
" ",
|
||||
codeblock![seq![
|
||||
"fn parse(_ctxt: _L, value: &",
|
||||
ctxt.m.any_type(),
|
||||
")",
|
||||
" -> std::result::Result<Self, _support::ParseError> ",
|
||||
codeblock(body)
|
||||
]]
|
||||
])
|
||||
});
|
||||
}
|
||||
|
||||
fn construct(
|
||||
ctxt: &FunctionContext,
|
||||
ctorname: Item,
|
||||
is_struct: bool,
|
||||
ty: &TSimple,
|
||||
dest: Option<&str>,
|
||||
body: &mut Vec<Item>,
|
||||
) {
|
||||
match ty {
|
||||
TSimple::Field(TField::Unit) => body.push(item(seq!["Ok(", ctorname, ")"])),
|
||||
TSimple::Field(fieldty) => body.push(item(seq![
|
||||
"Ok(",
|
||||
ctorname,
|
||||
parens![store_wrap(is_struct, fieldty, dest.unwrap())],
|
||||
")"
|
||||
])),
|
||||
TSimple::Record(_) => body.push(item(seq![
|
||||
"Ok(",
|
||||
ctorname,
|
||||
" ",
|
||||
braces(
|
||||
ctxt.captures
|
||||
.iter()
|
||||
.map(|c| item(seq![
|
||||
c.field_name.clone(),
|
||||
": ",
|
||||
store_wrap(is_struct, &c.ty, &c.source_expr)
|
||||
]))
|
||||
.collect()
|
||||
),
|
||||
")"
|
||||
])),
|
||||
}
|
||||
}
|
||||
|
||||
fn store_wrap(is_struct: bool, ty: &TField, expr: &str) -> String {
|
||||
match ty {
|
||||
TField::Unit | TField::Array(_) | TField::Set(_) | TField::Map(_, _) => expr.to_owned(),
|
||||
TField::Ref(_) => {
|
||||
if is_struct {
|
||||
expr.to_owned()
|
||||
} else {
|
||||
format!("std::boxed::Box::new({})", expr)
|
||||
}
|
||||
}
|
||||
TField::Base(_) | TField::Any | TField::Embedded => format!("{}.clone()", expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn simple_pattern_parser(
|
||||
ctxt: &mut FunctionContext,
|
||||
p: &SimplePattern,
|
||||
src: &str,
|
||||
sequence_base: Option<usize>,
|
||||
body: &mut Vec<Item>,
|
||||
) -> String {
|
||||
let dest = ctxt.gentempname();
|
||||
match p {
|
||||
SimplePattern::Any => {
|
||||
ctxt.define_atom(body, &dest, item(src.to_owned()));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Atom { atom_kind: k } => {
|
||||
let converter = match &**k {
|
||||
AtomKind::Boolean => "to_boolean",
|
||||
AtomKind::Double => "to_double",
|
||||
AtomKind::SignedInteger => "to_signedinteger",
|
||||
AtomKind::String => "to_string",
|
||||
AtomKind::ByteString => "to_bytestring",
|
||||
AtomKind::Symbol => "to_symbol",
|
||||
};
|
||||
ctxt.define_atom(
|
||||
body,
|
||||
&dest,
|
||||
item(seq![src.to_owned(), ".value().", converter, "()?"]),
|
||||
);
|
||||
dest
|
||||
}
|
||||
SimplePattern::Embedded { .. } => {
|
||||
ctxt.define_atom(
|
||||
body,
|
||||
&dest,
|
||||
item(seq![parens![seq![
|
||||
src.to_owned(),
|
||||
".value().to_embedded()?"
|
||||
]]]),
|
||||
);
|
||||
dest
|
||||
}
|
||||
SimplePattern::Lit { value } => {
|
||||
body.push(item(seq![
|
||||
"if ",
|
||||
src.to_owned(),
|
||||
" != ",
|
||||
ctxt.m.define_literal(value),
|
||||
" { return ",
|
||||
ctxt.err_code(),
|
||||
"; }"
|
||||
]));
|
||||
ctxt.define_atom(body, &dest, item("()"));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Seqof { pattern } => {
|
||||
let (src, n) = sequenceify(ctxt, src, sequence_base, body);
|
||||
let tmp = ctxt.gentempname();
|
||||
let mut inner = Vec::new();
|
||||
let item_dest = simple_pattern_parser(ctxt, pattern, &tmp, None, &mut inner);
|
||||
inner.push(item(seq![
|
||||
dest.to_owned(),
|
||||
".push(",
|
||||
store_wrap(true, &field_type(pattern), &item_dest),
|
||||
");"
|
||||
]));
|
||||
ctxt.declare_compound(body, &dest, item("std::vec::Vec::new()"));
|
||||
body.push(item(seq![
|
||||
"for ",
|
||||
tmp.to_owned(),
|
||||
" in &",
|
||||
src.to_owned(),
|
||||
brackets![seq![n.to_string(), ".."]],
|
||||
" ",
|
||||
codeblock(inner)
|
||||
]));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Setof { pattern } => {
|
||||
let tmp = ctxt.gentempname();
|
||||
let mut inner = Vec::new();
|
||||
let item_dest = simple_pattern_parser(ctxt, pattern, &tmp, None, &mut inner);
|
||||
inner.push(item(seq![
|
||||
dest.to_owned(),
|
||||
".insert(",
|
||||
store_wrap(true, &field_type(pattern), &item_dest),
|
||||
");"
|
||||
]));
|
||||
ctxt.declare_compound(body, &dest, item("preserves::value::Set::new()"));
|
||||
body.push(item(seq![
|
||||
"for ",
|
||||
tmp.to_owned(),
|
||||
" in ",
|
||||
src.to_owned(),
|
||||
".value().to_set()?",
|
||||
" ",
|
||||
codeblock(inner)
|
||||
]));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Dictof { key, value } => {
|
||||
let tmp_key = ctxt.gentempname();
|
||||
let tmp_value = ctxt.gentempname();
|
||||
let mut inner = Vec::new();
|
||||
let key_dest = simple_pattern_parser(ctxt, key, &tmp_key, None, &mut inner);
|
||||
let value_dest = simple_pattern_parser(ctxt, value, &tmp_value, None, &mut inner);
|
||||
inner.push(item(seq![
|
||||
dest.to_owned(),
|
||||
".insert(",
|
||||
store_wrap(true, &field_type(key), &key_dest),
|
||||
", ",
|
||||
store_wrap(true, &field_type(value), &value_dest),
|
||||
");"
|
||||
]));
|
||||
ctxt.declare_compound(body, &dest, item("preserves::value::Map::new()"));
|
||||
body.push(item(seq![
|
||||
"for (",
|
||||
tmp_key.to_owned(),
|
||||
", ",
|
||||
tmp_value.to_owned(),
|
||||
")",
|
||||
" in ",
|
||||
src.to_owned(),
|
||||
".value().to_dictionary()?",
|
||||
" ",
|
||||
codeblock(inner)
|
||||
]));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Ref(r) => {
|
||||
let tf = name![ctxt.m.render_ref(&**r, RefRenderStyle::Bare), "parse"];
|
||||
ctxt.define_atom(
|
||||
body,
|
||||
&dest,
|
||||
item(seq![tf, parens!["_ctxt", src.to_owned()], "?"]),
|
||||
);
|
||||
dest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sequenceify(
|
||||
ctxt: &mut FunctionContext,
|
||||
src: &str,
|
||||
sequence_base: Option<usize>,
|
||||
body: &mut Vec<Item>,
|
||||
) -> (String, usize) {
|
||||
match sequence_base {
|
||||
Some(n) => (src.to_owned(), n),
|
||||
None => {
|
||||
let tmp = ctxt.gentempname();
|
||||
ctxt.define_atom(
|
||||
body,
|
||||
&tmp,
|
||||
item(seq![src.to_owned(), ".value().to_sequence()?"]),
|
||||
);
|
||||
(tmp, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fixed_sequence_parser(
|
||||
ctxt: &mut FunctionContext,
|
||||
base: usize,
|
||||
ps: &[NamedPattern],
|
||||
src: &str,
|
||||
body: &mut Vec<Item>,
|
||||
) {
|
||||
let mut i = base;
|
||||
let required_count = ps.len();
|
||||
if required_count > 0 {
|
||||
body.push(item(seq![
|
||||
"if ",
|
||||
src.to_owned(),
|
||||
".len()",
|
||||
if base > 0 {
|
||||
seq![" - ", base.to_string()]
|
||||
} else {
|
||||
seq![]
|
||||
},
|
||||
" < ",
|
||||
required_count.to_string(),
|
||||
" { return ",
|
||||
ctxt.err_code(),
|
||||
"; }"
|
||||
]));
|
||||
}
|
||||
for p in ps {
|
||||
named_pattern_parser(ctxt, p, &format!("(&{}[{}])", src, i), None, body);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn named_pattern_parser(
|
||||
ctxt: &mut FunctionContext,
|
||||
p: &NamedPattern,
|
||||
src: &str,
|
||||
sequence_base: Option<usize>,
|
||||
body: &mut Vec<Item>,
|
||||
) {
|
||||
match p {
|
||||
NamedPattern::Anonymous(p) => {
|
||||
pattern_parser(ctxt, p, src, sequence_base, body);
|
||||
}
|
||||
NamedPattern::Named(b) => {
|
||||
let Binding { name, pattern } = &**b;
|
||||
let dest = simple_pattern_parser(ctxt, pattern, src, sequence_base, body);
|
||||
let capture_ty = field_type(pattern);
|
||||
ctxt.capture(names::render_fieldname(name), capture_ty, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pattern_parser(
|
||||
ctxt: &mut FunctionContext,
|
||||
p: &Pattern,
|
||||
src: &str,
|
||||
sequence_base: Option<usize>,
|
||||
body: &mut Vec<Item>,
|
||||
) -> Option<String> {
|
||||
match p {
|
||||
Pattern::SimplePattern(s) => Some(simple_pattern_parser(ctxt, s, src, sequence_base, body)),
|
||||
Pattern::CompoundPattern(c) => {
|
||||
match &**c {
|
||||
CompoundPattern::Rec { label, fields } => {
|
||||
let rtmp = ctxt.gentempname();
|
||||
ctxt.define_atom(
|
||||
body,
|
||||
&rtmp,
|
||||
item(seq![src.to_owned(), ".value().to_record(None)?"]),
|
||||
);
|
||||
named_pattern_parser(ctxt, &**label, &format!("{}.label()", rtmp), None, body);
|
||||
named_pattern_parser(
|
||||
ctxt,
|
||||
&**fields,
|
||||
&format!("{}.fields()", rtmp),
|
||||
Some(0),
|
||||
body,
|
||||
);
|
||||
}
|
||||
CompoundPattern::Tuple { patterns } => {
|
||||
let (src, n) = sequenceify(ctxt, src, sequence_base, body);
|
||||
fixed_sequence_parser(ctxt, n, patterns, &src, body);
|
||||
}
|
||||
CompoundPattern::TuplePrefix { fixed, variable } => {
|
||||
let (src, n) = sequenceify(ctxt, src, sequence_base, body);
|
||||
fixed_sequence_parser(ctxt, n, fixed, &src, body);
|
||||
named_pattern_parser(
|
||||
ctxt,
|
||||
&promote(variable),
|
||||
&src,
|
||||
Some(n + fixed.len()),
|
||||
body,
|
||||
);
|
||||
}
|
||||
CompoundPattern::Dict { entries } => {
|
||||
let dtmp = ctxt.gentempname();
|
||||
ctxt.define_atom(
|
||||
body,
|
||||
&dtmp,
|
||||
item(seq![src.to_owned(), ".value().to_dictionary()?"]),
|
||||
);
|
||||
for (key_lit, value_pat) in entries.0.iter() {
|
||||
let vtmp = ctxt.gentempname();
|
||||
let init_expr = item(seq![
|
||||
dtmp.to_owned(),
|
||||
".get",
|
||||
parens![ctxt.m.define_literal(key_lit)],
|
||||
".ok_or_else(|| ",
|
||||
ctxt.conformance_err_code(),
|
||||
")?"
|
||||
]);
|
||||
ctxt.define_atom(body, &vtmp, init_expr);
|
||||
named_pattern_parser(ctxt, &promote(value_pat), &vtmp, None, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,652 +0,0 @@
|
|||
use crate::gen::schema::*;
|
||||
use crate::syntax::block::constructors::*;
|
||||
use crate::syntax::block::escape_bytes;
|
||||
use crate::syntax::block::escape_string;
|
||||
use crate::syntax::block::Item;
|
||||
use crate::*;
|
||||
|
||||
use preserves::value::AtomClass;
|
||||
use preserves::value::CompoundClass;
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::ValueClass;
|
||||
|
||||
use super::context::FunctionContext;
|
||||
use super::context::ModuleContext;
|
||||
use super::context::ModuleContextMode;
|
||||
use super::context::RefRenderStyle;
|
||||
use super::names;
|
||||
use super::types::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReaderPlugin;
|
||||
|
||||
impl compiler::Plugin for ReaderPlugin {
|
||||
fn generate_definition(
|
||||
&self,
|
||||
module_ctxt: &mut ModuleContext,
|
||||
definition_name: &str,
|
||||
definition: &Definition,
|
||||
) {
|
||||
if let ModuleContextMode::TargetGeneric = module_ctxt.mode {
|
||||
gen_definition_reader(module_ctxt, definition_name, definition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BoundaryTracker {
|
||||
tracker_name: String,
|
||||
item_expr: &'static str,
|
||||
}
|
||||
|
||||
impl BoundaryTracker {
|
||||
fn unwrap(
|
||||
ctxt: &mut FunctionContext,
|
||||
body: &mut Vec<Item>,
|
||||
open_expr: &'static str,
|
||||
e: Option<&BoundaryTracker>,
|
||||
) -> Self {
|
||||
match e {
|
||||
None => Self::new(ctxt, body, open_expr, "_support::B::Item::SequenceValue"),
|
||||
Some(b) => b.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new(
|
||||
ctxt: &mut FunctionContext,
|
||||
body: &mut Vec<Item>,
|
||||
open_expr: &'static str,
|
||||
item_expr: &'static str,
|
||||
) -> Self {
|
||||
let tracker_name = ctxt.gentempname();
|
||||
body.push(item(open_expr));
|
||||
body.push(item(seq![
|
||||
"let mut ",
|
||||
tracker_name.clone(),
|
||||
" = _support::B::Type::default();"
|
||||
]));
|
||||
BoundaryTracker {
|
||||
tracker_name,
|
||||
item_expr,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_boundary(&self, body: &mut Vec<Item>) {
|
||||
body.push(item(seq![
|
||||
self.tracker_name.clone(),
|
||||
".shift(Some(",
|
||||
self.item_expr,
|
||||
"));"
|
||||
]));
|
||||
body.push(item(seq!["r.boundary(&", self.tracker_name.clone(), ")?;"]));
|
||||
}
|
||||
|
||||
fn emit_loop(&self, body: &mut Vec<Item>, inner: Vec<Item>) {
|
||||
body.push(item(seq![
|
||||
"while !r.close_compound",
|
||||
parens![
|
||||
seq!["&mut ", self.tracker_name.clone()],
|
||||
seq!["&", self.item_expr]
|
||||
],
|
||||
"? ",
|
||||
codeblock(inner)
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_definition_reader(m: &mut ModuleContext, n: &str, d: &Definition) {
|
||||
let ty = definition_type(&m.module_path, Purpose::Codegen, n, d);
|
||||
|
||||
m.define_function(n, |mut ctxt| {
|
||||
let mut body = vec![];
|
||||
|
||||
match d {
|
||||
Definition::Or {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => {
|
||||
let mut ps = vec![&**pattern_0, &**pattern_1];
|
||||
ps.extend(pattern_n);
|
||||
ctxt.define_atom(&mut body, "_mark", item("r.mark()?"));
|
||||
for NamedAlternative {
|
||||
variant_label: name,
|
||||
pattern: pat,
|
||||
} in ps
|
||||
{
|
||||
let fname = seq![
|
||||
"read_",
|
||||
names::render_fieldname(n),
|
||||
"_",
|
||||
names::render_fieldname(name)
|
||||
];
|
||||
let ctorname = item(name![
|
||||
names::render_constructor(n),
|
||||
names::render_constructor(name)
|
||||
]);
|
||||
ctxt.m
|
||||
.define_function(&(n.to_owned() + "::" + name), |mut ctxt| {
|
||||
let mut body = Vec::new();
|
||||
let dest = pattern_reader(&mut ctxt, pat, None, &mut body);
|
||||
let dest = dest.as_ref().map(String::as_str);
|
||||
construct(&ctxt, ctorname, false, &pattern_type(pat), dest, &mut body);
|
||||
item(seq![
|
||||
"fn ",
|
||||
fname.clone(),
|
||||
anglebrackets![
|
||||
"'de",
|
||||
seq![ctxt.m.any_type(), ": preserves::value::NestedValue"],
|
||||
seq!["R: _support::Reader<'de, ", ctxt.m.any_type(), ">"]
|
||||
],
|
||||
"(r: &mut R) -> ",
|
||||
"std::result::Result<",
|
||||
names::render_constructor(n),
|
||||
ty.generic_arg(ctxt.m),
|
||||
", _support::ParseError> ",
|
||||
codeblock(body)
|
||||
])
|
||||
});
|
||||
body.push(item(seq![
|
||||
"match ",
|
||||
fname,
|
||||
"(r) { ",
|
||||
"Err(e) if e.is_conformance_error() => r.restore(&_mark)?, ",
|
||||
"result => return result }"
|
||||
]));
|
||||
}
|
||||
body.push(item(seq![ctxt.err_code()]));
|
||||
}
|
||||
Definition::And {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => {
|
||||
let mut ps = vec![&**pattern_0, &**pattern_1];
|
||||
let mut need_restore = false;
|
||||
ps.extend(pattern_n);
|
||||
ctxt.define_atom(&mut body, "_mark", item("r.mark()?"));
|
||||
for e in &ps {
|
||||
if need_restore {
|
||||
body.push(item("r.restore(&_mark)?;"));
|
||||
} else {
|
||||
need_restore = true;
|
||||
}
|
||||
named_pattern_reader(&mut ctxt, e, None, &mut body);
|
||||
}
|
||||
construct(
|
||||
&ctxt,
|
||||
item(names::render_constructor(n)),
|
||||
true,
|
||||
&record_type(&ps),
|
||||
None,
|
||||
&mut body,
|
||||
);
|
||||
}
|
||||
Definition::Pattern(p) => {
|
||||
let dest = pattern_reader(&mut ctxt, p, None, &mut body);
|
||||
let dest = dest.as_ref().map(String::as_str);
|
||||
construct(
|
||||
&ctxt,
|
||||
item(names::render_constructor(n)),
|
||||
true,
|
||||
&pattern_type(p),
|
||||
dest,
|
||||
&mut body,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
item(seq![
|
||||
"impl",
|
||||
anglebrackets![seq![ctxt.m.any_type(), ": preserves::value::NestedValue"]],
|
||||
" _support::Deserialize",
|
||||
anglebrackets![ctxt.m.any_type()],
|
||||
" for ",
|
||||
names::render_constructor(n),
|
||||
ty.generic_arg(ctxt.m),
|
||||
" ",
|
||||
codeblock![seq![
|
||||
"fn deserialize",
|
||||
anglebrackets![
|
||||
"'de",
|
||||
seq!["R: _support::Reader<'de, ", ctxt.m.any_type(), ">"]
|
||||
],
|
||||
"(r: &mut R) -> ",
|
||||
"std::result::Result<Self, _support::ParseError> ",
|
||||
codeblock(body)
|
||||
]]
|
||||
])
|
||||
});
|
||||
}
|
||||
|
||||
fn construct(
|
||||
ctxt: &FunctionContext,
|
||||
ctorname: Item,
|
||||
is_struct: bool,
|
||||
ty: &TSimple,
|
||||
dest: Option<&str>,
|
||||
body: &mut Vec<Item>,
|
||||
) {
|
||||
match ty {
|
||||
TSimple::Field(TField::Unit) => body.push(item(seq!["Ok(", ctorname, ")"])),
|
||||
TSimple::Field(fieldty) => body.push(item(seq![
|
||||
"Ok(",
|
||||
ctorname,
|
||||
parens![store_wrap(is_struct, fieldty, dest.unwrap())],
|
||||
")"
|
||||
])),
|
||||
TSimple::Record(_) => body.push(item(seq![
|
||||
"Ok(",
|
||||
ctorname,
|
||||
" ",
|
||||
braces(
|
||||
ctxt.captures
|
||||
.iter()
|
||||
.map(|c| item(seq![
|
||||
c.field_name.clone(),
|
||||
": ",
|
||||
store_wrap(is_struct, &c.ty, &c.source_expr)
|
||||
]))
|
||||
.collect()
|
||||
),
|
||||
")"
|
||||
])),
|
||||
}
|
||||
}
|
||||
|
||||
fn store_wrap(is_struct: bool, ty: &TField, expr: &str) -> String {
|
||||
match ty {
|
||||
TField::Unit | TField::Array(_) | TField::Set(_) | TField::Map(_, _) => expr.to_owned(),
|
||||
TField::Ref(_) => {
|
||||
if is_struct {
|
||||
expr.to_owned()
|
||||
} else {
|
||||
format!("std::boxed::Box::new({})", expr)
|
||||
}
|
||||
}
|
||||
TField::Base(_) | TField::Any | TField::Embedded => expr.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn group_by<T, K, Key>(mut items: Vec<T>, mut key: Key) -> Vec<(K, Vec<T>)>
|
||||
where
|
||||
K: Ord + Clone,
|
||||
Key: FnMut(&T) -> K,
|
||||
{
|
||||
let mut result = Vec::new();
|
||||
let mut current_key: Option<K> = None;
|
||||
let mut buf = Vec::new();
|
||||
items.sort_by(|a, b| key(a).cmp(&key(b)));
|
||||
for (k, v) in items.into_iter().map(|t| (key(&t), t)) {
|
||||
match current_key.cmp(&Some(k.clone())) {
|
||||
std::cmp::Ordering::Equal => (),
|
||||
std::cmp::Ordering::Less | std::cmp::Ordering::Greater => {
|
||||
if let Some(k) = current_key {
|
||||
result.push((k, std::mem::take(&mut buf)));
|
||||
}
|
||||
current_key = Some(k);
|
||||
}
|
||||
}
|
||||
buf.push(v)
|
||||
}
|
||||
if let Some(k) = current_key {
|
||||
result.push((k.clone(), std::mem::take(&mut buf)));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
type LiteralContinuation = Box<dyn FnOnce(&mut FunctionContext, &mut Vec<Item>) -> ()>;
|
||||
type LiteralCases = Vec<(IOValue, LiteralContinuation)>;
|
||||
type LiteralSeqCases = Vec<(Vec<IOValue>, LiteralContinuation)>;
|
||||
|
||||
fn read_expected_literal_seqs(
|
||||
ctxt: &mut FunctionContext,
|
||||
body: &mut Vec<Item>,
|
||||
possibilities: LiteralSeqCases,
|
||||
) {
|
||||
let grouped = group_by(possibilities, |(vs, _f)| {
|
||||
if vs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(vs[0].clone())
|
||||
}
|
||||
});
|
||||
let mut cases = Vec::new();
|
||||
let mut nested: LiteralCases = Vec::new();
|
||||
for (head, group) in grouped.into_iter() {
|
||||
match head {
|
||||
None => {
|
||||
let mut inner = Vec::new();
|
||||
group.into_iter().next().unwrap().1(ctxt, &mut inner);
|
||||
cases.push(item(seq![
|
||||
"preserves::value::Token::End => ",
|
||||
codeblock(inner)
|
||||
]));
|
||||
}
|
||||
Some(h) => {
|
||||
let tails = group
|
||||
.into_iter()
|
||||
.map(|(mut vs, f)| {
|
||||
vs.remove(0);
|
||||
(vs, f)
|
||||
})
|
||||
.collect();
|
||||
nested.push((
|
||||
h,
|
||||
Box::new(|ctxt: &mut FunctionContext, b: &'_ mut Vec<Item>| {
|
||||
read_expected_literal_seqs(ctxt, b, tails)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
cases.extend(read_expected_literals_cases(ctxt, nested));
|
||||
body.push(item(seq!["match r.next_token(true)? ", codeblock(cases)]));
|
||||
}
|
||||
|
||||
fn read_expected_literals_cases(
|
||||
ctxt: &mut FunctionContext,
|
||||
possibilities: LiteralCases,
|
||||
) -> Vec<Item> {
|
||||
let grouped = group_by(possibilities, |(v, _f)| v.value_class());
|
||||
let mut cases = grouped.into_iter().map(|(n, group)| {
|
||||
match n {
|
||||
ValueClass::Atomic(cls) => {
|
||||
let mut subcases = Vec::new();
|
||||
for p in group {
|
||||
let mut inner = Vec::new();
|
||||
p.1(ctxt, &mut inner);
|
||||
subcases.push(item(seq![
|
||||
format!("preserves::value::Value::{:?}(w)", cls),
|
||||
match cls {
|
||||
AtomClass::Boolean => match p.0.value().to_boolean().unwrap() {
|
||||
true => " if *w".to_owned(),
|
||||
false => " if !*w".to_owned(),
|
||||
},
|
||||
AtomClass::Double =>
|
||||
format!(" if w.0 == {:?}", p.0),
|
||||
AtomClass::SignedInteger =>
|
||||
format!(" if *w == ({:?}).into()", p.0),
|
||||
AtomClass::String =>
|
||||
format!(" if w == {}", escape_string(p.0.value().to_string().unwrap())),
|
||||
AtomClass::ByteString =>
|
||||
format!(" if w == {}", escape_bytes(p.0.value().to_bytestring().unwrap())),
|
||||
AtomClass::Symbol =>
|
||||
format!(" if w == {}", escape_string(p.0.value().to_symbol().unwrap())),
|
||||
},
|
||||
" => ",
|
||||
codeblock(inner)]));
|
||||
}
|
||||
subcases.push(item(seq!["_ => return ", ctxt.err_code(), "?,"]));
|
||||
item(seq!["preserves::value::Token::Atom(v) => match v.value() ", codeblock(subcases)])
|
||||
}
|
||||
ValueClass::Compound(CompoundClass::Record) => {
|
||||
let mut subcases = Vec::new();
|
||||
read_expected_literal_seqs(ctxt, &mut subcases, group.into_iter().map(|(v, f)| {
|
||||
let r = v.value().to_record(None).unwrap();
|
||||
(r.0.clone(), f)
|
||||
}).collect());
|
||||
item(seq![
|
||||
"preserves::value::Token::Compound(preserves::value::CompoundClass::Record) => ",
|
||||
codeblock(subcases)])
|
||||
}
|
||||
ValueClass::Compound(CompoundClass::Sequence) => {
|
||||
let mut subcases = Vec::new();
|
||||
read_expected_literal_seqs(ctxt, &mut subcases, group.into_iter().map(|(v, f)| {
|
||||
let s = v.value().to_sequence().unwrap().clone();
|
||||
(s, f)
|
||||
}).collect());
|
||||
item(seq![
|
||||
"preserves::value::Token::Compound(preserves::value::CompoundClass::Sequence) => ",
|
||||
codeblock(subcases)])
|
||||
}
|
||||
ValueClass::Compound(CompoundClass::Set) => {
|
||||
panic!("Sets in literal constants in Schema not yet supported");
|
||||
}
|
||||
ValueClass::Compound(CompoundClass::Dictionary) => {
|
||||
panic!("Dictionaries in literal constants in Schema not yet supported");
|
||||
}
|
||||
ValueClass::Embedded => {
|
||||
panic!("Embedded values in literal constants in Schema not yet supported");
|
||||
}
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
cases.push(item(seq!["_ => return ", ctxt.err_code(), "?,"]));
|
||||
cases
|
||||
}
|
||||
|
||||
fn read_expected_literals(
|
||||
ctxt: &mut FunctionContext,
|
||||
body: &mut Vec<Item>,
|
||||
possibilities: LiteralCases,
|
||||
) {
|
||||
let cases = read_expected_literals_cases(ctxt, possibilities);
|
||||
body.push(item(seq!["match r.next_token(true)? ", codeblock(cases)]));
|
||||
}
|
||||
|
||||
fn simple_pattern_reader(
|
||||
ctxt: &mut FunctionContext,
|
||||
p: &SimplePattern,
|
||||
boundary_tracker: Option<&BoundaryTracker>,
|
||||
body: &mut Vec<Item>,
|
||||
) -> String {
|
||||
let dest = ctxt.gentempname();
|
||||
match p {
|
||||
SimplePattern::Any => {
|
||||
ctxt.define_atom(body, &dest, item("r.demand_next(true)?"));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Atom { atom_kind: k } => {
|
||||
let reader = match &**k {
|
||||
AtomKind::Boolean => "r.next_boolean()?",
|
||||
AtomKind::Double => "r.next_double()?",
|
||||
AtomKind::SignedInteger => "r.next_signedinteger()?",
|
||||
AtomKind::String => "r.next_str()?.into_owned()",
|
||||
AtomKind::ByteString => "r.next_bytestring()?.into_owned()",
|
||||
AtomKind::Symbol => "r.next_symbol()?.into_owned()",
|
||||
};
|
||||
ctxt.define_atom(body, &dest, item(reader.to_owned()));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Embedded { .. } => {
|
||||
ctxt.define_atom(
|
||||
body,
|
||||
&dest,
|
||||
item("r.demand_next(true)?.value().to_embedded()?.clone()"),
|
||||
);
|
||||
dest
|
||||
}
|
||||
SimplePattern::Lit { value } => {
|
||||
let f = Box::new(|_ctxt: &mut FunctionContext, _: &'_ mut Vec<Item>| ());
|
||||
read_expected_literals(ctxt, body, vec![(value.clone(), f)]);
|
||||
ctxt.define_atom(body, &dest, item("()"));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Seqof { pattern } => {
|
||||
let compound_dest = ctxt.gentempname();
|
||||
ctxt.with_definite_mode(|ctxt| {
|
||||
let boundary_tracker =
|
||||
BoundaryTracker::unwrap(ctxt, body, "r.open_sequence()?;", boundary_tracker);
|
||||
let mut inner = Vec::new();
|
||||
boundary_tracker.emit_boundary(&mut inner);
|
||||
let item_dest = simple_pattern_reader(ctxt, pattern, None, &mut inner);
|
||||
inner.push(item(seq![
|
||||
compound_dest.to_owned(),
|
||||
".push(",
|
||||
store_wrap(true, &field_type(pattern), &item_dest),
|
||||
");"
|
||||
]));
|
||||
ctxt.declare_compound(body, &compound_dest, item("std::vec::Vec::new()"));
|
||||
boundary_tracker.emit_loop(body, inner);
|
||||
});
|
||||
ctxt.define_atom(body, &dest, item(compound_dest));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Setof { pattern } => {
|
||||
let compound_dest = ctxt.gentempname();
|
||||
ctxt.with_definite_mode(|ctxt| {
|
||||
let boundary_tracker = BoundaryTracker::new(
|
||||
ctxt,
|
||||
body,
|
||||
"r.open_set()?;",
|
||||
"_support::B::Item::SetValue",
|
||||
);
|
||||
let mut inner = Vec::new();
|
||||
boundary_tracker.emit_boundary(&mut inner);
|
||||
let item_dest = simple_pattern_reader(ctxt, pattern, None, &mut inner);
|
||||
inner.push(item(seq![
|
||||
compound_dest.to_owned(),
|
||||
".insert(",
|
||||
store_wrap(true, &field_type(pattern), &item_dest),
|
||||
");"
|
||||
]));
|
||||
ctxt.declare_compound(body, &compound_dest, item("preserves::value::Set::new()"));
|
||||
boundary_tracker.emit_loop(body, inner);
|
||||
});
|
||||
ctxt.define_atom(body, &dest, item(compound_dest));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Dictof { key, value } => {
|
||||
let compound_dest = ctxt.gentempname();
|
||||
ctxt.with_definite_mode(|ctxt| {
|
||||
let mut boundary_tracker = BoundaryTracker::new(
|
||||
ctxt,
|
||||
body,
|
||||
"r.open_dictionary()?;",
|
||||
"_support::B::Item::DictionaryKey",
|
||||
);
|
||||
let mut inner = Vec::new();
|
||||
boundary_tracker.emit_boundary(&mut inner);
|
||||
let key_dest = simple_pattern_reader(ctxt, key, None, &mut inner);
|
||||
boundary_tracker.item_expr = "_support::B::Item::DictionaryValue";
|
||||
boundary_tracker.emit_boundary(&mut inner);
|
||||
let value_dest = simple_pattern_reader(ctxt, value, None, &mut inner);
|
||||
inner.push(item(seq![
|
||||
compound_dest.to_owned(),
|
||||
".insert(",
|
||||
store_wrap(true, &field_type(key), &key_dest),
|
||||
", ",
|
||||
store_wrap(true, &field_type(value), &value_dest),
|
||||
");"
|
||||
]));
|
||||
ctxt.declare_compound(body, &compound_dest, item("preserves::value::Map::new()"));
|
||||
boundary_tracker.item_expr = "_support::B::Item::DictionaryKey";
|
||||
boundary_tracker.emit_loop(body, inner);
|
||||
});
|
||||
ctxt.define_atom(body, &dest, item(compound_dest));
|
||||
dest
|
||||
}
|
||||
SimplePattern::Ref(r) => {
|
||||
let tf = name![ctxt.m.render_ref(&**r, RefRenderStyle::Bare), "deserialize"];
|
||||
ctxt.define_atom(body, &dest, item(seq![tf, "(r)?"]));
|
||||
dest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn named_pattern_reader(
|
||||
ctxt: &mut FunctionContext,
|
||||
p: &NamedPattern,
|
||||
boundary_tracker: Option<&BoundaryTracker>,
|
||||
body: &mut Vec<Item>,
|
||||
) {
|
||||
match p {
|
||||
NamedPattern::Anonymous(p) => {
|
||||
pattern_reader(ctxt, p, boundary_tracker, body);
|
||||
}
|
||||
NamedPattern::Named(b) => {
|
||||
let Binding { name, pattern } = &**b;
|
||||
let dest = simple_pattern_reader(ctxt, pattern, boundary_tracker, body);
|
||||
let capture_ty = field_type(pattern);
|
||||
ctxt.capture(names::render_fieldname(name), capture_ty, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pattern_reader(
|
||||
ctxt: &mut FunctionContext,
|
||||
p: &Pattern,
|
||||
boundary_tracker: Option<&BoundaryTracker>,
|
||||
body: &mut Vec<Item>,
|
||||
) -> Option<String> {
|
||||
match p {
|
||||
Pattern::SimplePattern(s) => Some(simple_pattern_reader(ctxt, s, boundary_tracker, body)),
|
||||
Pattern::CompoundPattern(c) => {
|
||||
match &**c {
|
||||
CompoundPattern::Rec { label, fields } => {
|
||||
let mut boundary_tracker = BoundaryTracker::new(
|
||||
ctxt,
|
||||
body,
|
||||
"r.open_record(None)?;",
|
||||
"_support::B::Item::RecordLabel",
|
||||
);
|
||||
boundary_tracker.emit_boundary(body);
|
||||
boundary_tracker.item_expr = "_support::B::Item::RecordField";
|
||||
named_pattern_reader(ctxt, &**label, None, body);
|
||||
named_pattern_reader(ctxt, &**fields, Some(&boundary_tracker), body);
|
||||
}
|
||||
CompoundPattern::Tuple { patterns } => {
|
||||
let boundary_tracker = BoundaryTracker::unwrap(
|
||||
ctxt,
|
||||
body,
|
||||
"r.open_sequence()?;",
|
||||
boundary_tracker,
|
||||
);
|
||||
for p in patterns {
|
||||
boundary_tracker.emit_boundary(body);
|
||||
named_pattern_reader(ctxt, p, None, body);
|
||||
}
|
||||
body.push(item(seq![
|
||||
"r.ensure_complete",
|
||||
parens![
|
||||
boundary_tracker.tracker_name.clone(),
|
||||
seq!["&", boundary_tracker.item_expr]
|
||||
],
|
||||
"?;"
|
||||
]));
|
||||
}
|
||||
CompoundPattern::TuplePrefix { fixed, variable } => {
|
||||
let boundary_tracker = BoundaryTracker::unwrap(
|
||||
ctxt,
|
||||
body,
|
||||
"r.open_sequence()?;",
|
||||
boundary_tracker,
|
||||
);
|
||||
for p in fixed {
|
||||
boundary_tracker.emit_boundary(body);
|
||||
named_pattern_reader(ctxt, p, None, body);
|
||||
}
|
||||
named_pattern_reader(ctxt, &promote(variable), Some(&boundary_tracker), body);
|
||||
}
|
||||
CompoundPattern::Dict { entries } => {
|
||||
let boundary_tracker = BoundaryTracker::new(
|
||||
ctxt,
|
||||
body,
|
||||
"r.open_dictionary()?;",
|
||||
"_support::B::Item::DictionaryKey",
|
||||
);
|
||||
let mut inner = Vec::new();
|
||||
boundary_tracker.emit_boundary(&mut inner);
|
||||
let mut val_boundary_tracker = boundary_tracker.clone();
|
||||
val_boundary_tracker.item_expr = "_support::B::Item::DictionaryValue";
|
||||
body.extend(ctxt.with_indefinite_mode(|ctxt| {
|
||||
read_expected_literals(ctxt, &mut inner, entries.0.iter().map(move |(key_lit, value_pat)| {
|
||||
let value_pat = value_pat.clone();
|
||||
let val_boundary_tracker = val_boundary_tracker.clone();
|
||||
let f: LiteralContinuation = Box::new(
|
||||
move |ctxt: &mut FunctionContext, innerinner: &mut Vec<Item>| {
|
||||
val_boundary_tracker.emit_boundary(innerinner);
|
||||
named_pattern_reader(ctxt, &promote(&value_pat), None, innerinner);
|
||||
innerinner.push(item("continue;"));
|
||||
});
|
||||
(key_lit.clone(), f)
|
||||
}).collect());
|
||||
}));
|
||||
boundary_tracker.emit_loop(body, inner);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,461 +0,0 @@
|
|||
use crate::gen::schema::*;
|
||||
use crate::syntax::block::constructors::*;
|
||||
use crate::syntax::block::{Emittable, Item};
|
||||
use crate::*;
|
||||
|
||||
use preserves::value::Set;
|
||||
|
||||
use super::context::BundleContext;
|
||||
use super::context::ModuleContext;
|
||||
use super::context::ModuleContextMode;
|
||||
use super::context::RefRenderStyle;
|
||||
use super::names;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
||||
pub enum Purpose {
|
||||
Codegen,
|
||||
Xref,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct TDefinition {
|
||||
pub purpose: Purpose,
|
||||
pub self_ref: Ref,
|
||||
pub body: TDefinitionBody,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub enum TDefinitionBody {
|
||||
Union(Vec<(String, TSimple)>),
|
||||
Simple(TSimple),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub enum TSimple {
|
||||
Field(TField),
|
||||
Record(TRecord),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub enum TField {
|
||||
Unit,
|
||||
Any,
|
||||
Embedded,
|
||||
Array(Box<TField>),
|
||||
Set(Box<TField>),
|
||||
Map(Box<TField>, Box<TField>),
|
||||
Ref(Ref),
|
||||
Base(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct TRecord(pub Vec<(String, TField)>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TypePlugin;
|
||||
|
||||
impl compiler::Plugin for TypePlugin {
|
||||
fn generate_module(&self, m: &mut ModuleContext) {
|
||||
if let EmbeddedTypeName::Ref(r) = &m.schema.embedded_type {
|
||||
m.define_type(item(vertical(
|
||||
false,
|
||||
seq![
|
||||
seq![
|
||||
"pub type _Dom = ",
|
||||
m.render_ref(&*r, RefRenderStyle::Bare),
|
||||
";"
|
||||
],
|
||||
seq!["pub type _Ptr = std::sync::Arc<_Dom>;"],
|
||||
seq!["pub type _Any = preserves::value::ArcValue<_Ptr>;"]
|
||||
],
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_definition(&self, m: &mut ModuleContext, n: &str, d: &Definition) {
|
||||
if let ModuleContextMode::TargetGeneric = m.mode {
|
||||
let ty = definition_type(&m.module_path, Purpose::Codegen, n, d);
|
||||
m.define_type(item(ty.render(m, n)));
|
||||
m.define_type(item(seq![
|
||||
"impl",
|
||||
ty.generic_decl(m),
|
||||
" preserves::value::Domain for ",
|
||||
names::render_constructor(n),
|
||||
ty.generic_arg(m),
|
||||
" {}"
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn definition_type(
|
||||
module: &ModulePath,
|
||||
purpose: Purpose,
|
||||
n: &str,
|
||||
d: &Definition,
|
||||
) -> TDefinition {
|
||||
TDefinition {
|
||||
purpose,
|
||||
self_ref: Ref {
|
||||
module: module.clone(),
|
||||
name: n.to_owned(),
|
||||
},
|
||||
body: match d {
|
||||
Definition::Or {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => TDefinitionBody::Union(or_definition_type(pattern_0, pattern_1, pattern_n)),
|
||||
Definition::And {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => TDefinitionBody::Simple(and_definition_type(pattern_0, pattern_1, pattern_n)),
|
||||
Definition::Pattern(p) => TDefinitionBody::Simple(pattern_type(p)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_definition_type(
|
||||
p0: &NamedAlternative,
|
||||
p1: &NamedAlternative,
|
||||
pn: &Vec<NamedAlternative>,
|
||||
) -> Vec<(String, TSimple)> {
|
||||
let mut entries = Vec::new();
|
||||
entries.push((p0.variant_label.to_owned(), pattern_type(&p0.pattern)));
|
||||
entries.push((p1.variant_label.to_owned(), pattern_type(&p1.pattern)));
|
||||
for e in pn {
|
||||
entries.push((e.variant_label.to_owned(), pattern_type(&e.pattern)));
|
||||
}
|
||||
entries
|
||||
}
|
||||
|
||||
pub fn and_definition_type(
|
||||
p0: &NamedPattern,
|
||||
p1: &NamedPattern,
|
||||
pn: &Vec<NamedPattern>,
|
||||
) -> TSimple {
|
||||
let mut arms = vec![p0, p1];
|
||||
arms.extend(pn);
|
||||
record_type(&arms)
|
||||
}
|
||||
|
||||
pub fn pattern_type(p: &Pattern) -> TSimple {
|
||||
match p {
|
||||
Pattern::SimplePattern(p) => TSimple::Field(field_type(p)),
|
||||
Pattern::CompoundPattern(_) => {
|
||||
record_type(&vec![&NamedPattern::Anonymous(Box::new(p.clone()))])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn record_type(ps: &Vec<&NamedPattern>) -> TSimple {
|
||||
let fs = gather_fields(ps, Vec::new());
|
||||
if fs.is_empty() {
|
||||
TSimple::Field(TField::Unit)
|
||||
} else {
|
||||
TSimple::Record(TRecord(fs))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gather_fields(
|
||||
ps: &Vec<&NamedPattern>,
|
||||
mut fs: Vec<(String, TField)>,
|
||||
) -> Vec<(String, TField)> {
|
||||
for p in ps.iter() {
|
||||
fs = gather_field(p, fs);
|
||||
}
|
||||
fs
|
||||
}
|
||||
|
||||
pub fn gather_field(p: &NamedPattern, mut fs: Vec<(String, TField)>) -> Vec<(String, TField)> {
|
||||
match p {
|
||||
NamedPattern::Named(b) => {
|
||||
let Binding { name, pattern } = &**b;
|
||||
fs.push((name.to_owned(), field_type(pattern)));
|
||||
fs
|
||||
}
|
||||
NamedPattern::Anonymous(p) => match &**p {
|
||||
Pattern::SimplePattern(_) => fs,
|
||||
Pattern::CompoundPattern(c) => match &**c {
|
||||
CompoundPattern::Rec { label, fields } => {
|
||||
gather_field(&*fields, gather_field(&*label, fs))
|
||||
}
|
||||
CompoundPattern::Tuple { patterns } => {
|
||||
gather_fields(&patterns.iter().collect(), fs)
|
||||
}
|
||||
CompoundPattern::TuplePrefix { fixed, variable } => gather_field(
|
||||
&promote(&**variable),
|
||||
gather_fields(&fixed.iter().collect(), fs),
|
||||
),
|
||||
CompoundPattern::Dict { entries } => {
|
||||
for (_k, p) in &entries.0 {
|
||||
fs = gather_field(&promote(&p), fs);
|
||||
}
|
||||
fs
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn promote(p: &NamedSimplePattern) -> NamedPattern {
|
||||
match p {
|
||||
NamedSimplePattern::Anonymous(p) => {
|
||||
NamedPattern::Anonymous(Box::new(Pattern::SimplePattern(p.clone())))
|
||||
}
|
||||
NamedSimplePattern::Named(n) => NamedPattern::Named(n.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_type(p: &SimplePattern) -> TField {
|
||||
match p {
|
||||
SimplePattern::Any => TField::Any,
|
||||
SimplePattern::Atom { atom_kind: k } => match **k {
|
||||
AtomKind::Boolean => TField::Base("bool".to_owned()),
|
||||
AtomKind::Double => TField::Base("preserves::value::Double".to_owned()),
|
||||
AtomKind::SignedInteger => {
|
||||
TField::Base("preserves::value::signed_integer::SignedInteger".to_owned())
|
||||
}
|
||||
AtomKind::String => TField::Base("std::string::String".to_owned()),
|
||||
AtomKind::ByteString => TField::Base("std::vec::Vec<u8>".to_owned()),
|
||||
AtomKind::Symbol => TField::Base("std::string::String".to_owned()),
|
||||
},
|
||||
SimplePattern::Embedded { .. } => TField::Embedded,
|
||||
SimplePattern::Lit { .. } => TField::Unit,
|
||||
SimplePattern::Seqof { pattern: t } => TField::Array(Box::new(field_type(t))),
|
||||
SimplePattern::Setof { pattern: t } => TField::Set(Box::new(field_type(t))),
|
||||
SimplePattern::Dictof { key: k, value: v } => {
|
||||
TField::Map(Box::new(field_type(k)), Box::new(field_type(v)))
|
||||
}
|
||||
SimplePattern::Ref(r) => TField::Ref((**r).clone()),
|
||||
}
|
||||
}
|
||||
|
||||
type WalkState<'a, 'b> = super::cycles::WalkState<&'a BundleContext<'b>>;
|
||||
|
||||
impl TField {
|
||||
fn render(&self, ctxt: &ModuleContext, box_needed: bool) -> impl Emittable {
|
||||
match self {
|
||||
TField::Unit => seq!["()"],
|
||||
TField::Any => seq![ctxt.any_type()],
|
||||
TField::Embedded => seq![ctxt.any_type(), "::Embedded"],
|
||||
TField::Array(t) => seq!["std::vec::Vec<", t.render(ctxt, false), ">"],
|
||||
TField::Set(t) => seq!["preserves::value::Set<", t.render(ctxt, false), ">"],
|
||||
TField::Map(k, v) => seq![
|
||||
"preserves::value::Map",
|
||||
anglebrackets![k.render(ctxt, false), v.render(ctxt, false)]
|
||||
],
|
||||
TField::Ref(r) => {
|
||||
if box_needed {
|
||||
seq![
|
||||
"std::boxed::Box",
|
||||
anglebrackets![ctxt.render_ref(r, RefRenderStyle::Qualified)]
|
||||
]
|
||||
} else {
|
||||
seq![ctxt.render_ref(r, RefRenderStyle::Qualified)]
|
||||
}
|
||||
}
|
||||
TField::Base(n) => seq![n.to_owned()],
|
||||
}
|
||||
}
|
||||
|
||||
fn language_types(&self, s: &mut WalkState, ts: &mut Set<String>) {
|
||||
match self {
|
||||
TField::Unit | TField::Any | TField::Embedded | TField::Base(_) => (),
|
||||
TField::Array(f) => f.language_types(s, ts),
|
||||
TField::Set(f) => f.language_types(s, ts),
|
||||
TField::Map(k, v) => {
|
||||
k.language_types(s, ts);
|
||||
v.language_types(s, ts);
|
||||
}
|
||||
TField::Ref(r) => s.cycle_check(
|
||||
r,
|
||||
|ctxt, r| ctxt.type_for_name(r),
|
||||
|s, t| match t {
|
||||
Some(ty) if ty.purpose == Purpose::Codegen => ty._language_types(s, ts),
|
||||
Some(_) | None => {
|
||||
let xmts = &s
|
||||
.context
|
||||
.config
|
||||
.external_modules
|
||||
.get(&r.module.0)
|
||||
.unwrap()
|
||||
.rust_language_types;
|
||||
if let Some(f) = xmts.definitions.get(&r.name).or(xmts.fallback.as_ref()) {
|
||||
ts.extend(f(s.context.any_type()));
|
||||
}
|
||||
}
|
||||
},
|
||||
|| (),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_embedded(&self, s: &mut WalkState) -> bool {
|
||||
match self {
|
||||
TField::Unit | TField::Base(_) => false,
|
||||
TField::Any | TField::Embedded => true, // at least potentially true
|
||||
TField::Array(f) => f.has_embedded(s),
|
||||
TField::Set(f) => f.has_embedded(s),
|
||||
TField::Map(k, v) => k.has_embedded(s) || v.has_embedded(s),
|
||||
TField::Ref(r) =>
|
||||
// v TODO: should the "false" be configurable? cf. ModuleContext::ref_has_embedded.
|
||||
{
|
||||
s.cycle_check(
|
||||
r,
|
||||
|ctxt, r| ctxt.type_for_name(r),
|
||||
|s, t| t.map(|t| t._has_embedded(s)).unwrap_or(false),
|
||||
|| false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TSimple {
|
||||
pub fn render(
|
||||
&self,
|
||||
ctxt: &ModuleContext,
|
||||
ptr: Item,
|
||||
is_struct: bool,
|
||||
n: &str,
|
||||
) -> impl Emittable {
|
||||
let semi = if is_struct { seq![";"] } else { seq![] };
|
||||
let ppub = if is_struct { "pub " } else { "" };
|
||||
seq![
|
||||
names::render_constructor(n),
|
||||
ptr.to_owned(),
|
||||
match self {
|
||||
TSimple::Record(TRecord(fs)) => seq![
|
||||
" ",
|
||||
vertical(
|
||||
false,
|
||||
braces(
|
||||
fs.iter()
|
||||
.map(|(n, d)| item(seq![
|
||||
ppub,
|
||||
names::render_fieldname(n),
|
||||
": ",
|
||||
d.render(ctxt, !is_struct)
|
||||
]))
|
||||
.collect()
|
||||
)
|
||||
)
|
||||
],
|
||||
TSimple::Field(TField::Unit) => semi,
|
||||
TSimple::Field(t) => seq![parens![seq![ppub, t.render(ctxt, !is_struct)]], semi],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
fn language_types(&self, s: &mut WalkState, ts: &mut Set<String>) {
|
||||
match self {
|
||||
TSimple::Field(f) => f.language_types(s, ts),
|
||||
TSimple::Record(TRecord(fs)) => fs.iter().for_each(|(_k, v)| v.language_types(s, ts)),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_embedded(&self, s: &mut WalkState) -> bool {
|
||||
match self {
|
||||
TSimple::Field(f) => f.has_embedded(s),
|
||||
TSimple::Record(TRecord(fs)) => fs.iter().any(|(_k, v)| v.has_embedded(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TDefinition {
|
||||
pub fn generic_decl(&self, ctxt: &ModuleContext) -> Item {
|
||||
if self.has_embedded(ctxt.bundle) {
|
||||
item(anglebrackets![seq![
|
||||
ctxt.any_type(),
|
||||
": preserves::value::NestedValue"
|
||||
]])
|
||||
} else {
|
||||
item("")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generic_decl_with_defaults(&self, ctxt: &ModuleContext) -> Item {
|
||||
if self.has_embedded(ctxt.bundle) {
|
||||
item(anglebrackets![seq![
|
||||
ctxt.any_type(),
|
||||
": preserves::value::NestedValue = ",
|
||||
match ctxt.schema.embedded_type {
|
||||
EmbeddedTypeName::False => "preserves::value::IOValue",
|
||||
EmbeddedTypeName::Ref(_) => "_Any",
|
||||
}
|
||||
]])
|
||||
} else {
|
||||
item("")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generic_arg(&self, ctxt: &ModuleContext) -> Item {
|
||||
if self.has_embedded(ctxt.bundle) {
|
||||
item(anglebrackets![ctxt.any_type()])
|
||||
} else {
|
||||
item("")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, ctxt: &ModuleContext, n: &str) -> impl Emittable {
|
||||
vertical(
|
||||
false,
|
||||
seq![
|
||||
"#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash)]",
|
||||
match &self.body {
|
||||
TDefinitionBody::Union(items) => seq![
|
||||
"pub enum ",
|
||||
names::render_constructor(n),
|
||||
self.generic_decl_with_defaults(ctxt),
|
||||
" ",
|
||||
vertical(
|
||||
false,
|
||||
braces(
|
||||
items
|
||||
.iter()
|
||||
.map(|(n, d)| item(d.render(ctxt, item(""), false, n)))
|
||||
.collect()
|
||||
)
|
||||
)
|
||||
],
|
||||
TDefinitionBody::Simple(s) => seq![
|
||||
"pub struct ",
|
||||
s.render(ctxt, self.generic_decl_with_defaults(ctxt), true, n)
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
fn walk_state<'a, 'b>(&self, ctxt: &'a BundleContext<'b>) -> WalkState<'a, 'b> {
|
||||
WalkState::new(ctxt, self.self_ref.module.clone())
|
||||
}
|
||||
|
||||
pub fn language_types(&self, ctxt: &BundleContext) -> Set<String> {
|
||||
let mut ts = Set::new();
|
||||
self._language_types(&mut self.walk_state(ctxt), &mut ts);
|
||||
ts
|
||||
}
|
||||
|
||||
fn _language_types(&self, s: &mut WalkState, ts: &mut Set<String>) {
|
||||
match &self.body {
|
||||
TDefinitionBody::Union(entries) => {
|
||||
entries.iter().for_each(|(_k, v)| v.language_types(s, ts))
|
||||
}
|
||||
TDefinitionBody::Simple(t) => t.language_types(s, ts),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_embedded(&self, ctxt: &BundleContext) -> bool {
|
||||
self._has_embedded(&mut self.walk_state(ctxt))
|
||||
}
|
||||
|
||||
fn _has_embedded(&self, s: &mut WalkState) -> bool {
|
||||
match &self.body {
|
||||
TDefinitionBody::Union(entries) => entries.iter().any(|(_k, v)| v.has_embedded(s)),
|
||||
TDefinitionBody::Simple(t) => t.has_embedded(s),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,440 +0,0 @@
|
|||
use crate::gen::schema::*;
|
||||
use crate::syntax::block::constructors::*;
|
||||
use crate::syntax::block::{escape_string, Emittable, Item};
|
||||
use crate::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::context::{FunctionContext, ModuleContext, ModuleContextMode};
|
||||
use super::names;
|
||||
use super::types::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnparserPlugin;
|
||||
|
||||
impl compiler::Plugin for UnparserPlugin {
|
||||
fn generate_definition(
|
||||
&self,
|
||||
module_ctxt: &mut ModuleContext,
|
||||
definition_name: &str,
|
||||
definition: &Definition,
|
||||
) {
|
||||
if let ModuleContextMode::TargetGeneric = module_ctxt.mode {
|
||||
gen_definition_unparser(module_ctxt, definition_name, definition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ValueSource = Option<String>;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct FieldsSink {
|
||||
finish: Item,
|
||||
vec_expr: Item,
|
||||
body: Vec<Item>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ValueSink {
|
||||
Normal,
|
||||
Fields(Rc<Cell<Option<FieldsSink>>>),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ValueSink {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
ValueSink::Normal => write!(f, "ValueSink::Normal"),
|
||||
ValueSink::Fields(_) => write!(f, "ValueSink::Normal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ValueContext {
|
||||
src: ValueSource,
|
||||
sink: ValueSink,
|
||||
is_struct: bool,
|
||||
}
|
||||
|
||||
fn normal_none(is_struct: bool) -> ValueContext {
|
||||
ValueContext {
|
||||
src: None,
|
||||
sink: ValueSink::Normal,
|
||||
is_struct,
|
||||
}
|
||||
}
|
||||
|
||||
fn normal_src(src: String, is_struct: bool) -> ValueContext {
|
||||
ValueContext {
|
||||
src: Some(src),
|
||||
sink: ValueSink::Normal,
|
||||
is_struct,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_definition_unparser(m: &mut ModuleContext, n: &str, d: &Definition) {
|
||||
let ty = definition_type(&m.module_path, Purpose::Codegen, n, d);
|
||||
|
||||
m.define_function(n, |mut ctxt| {
|
||||
let mut body = vec![];
|
||||
|
||||
match d {
|
||||
Definition::Or {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => {
|
||||
let mut ps = vec![&**pattern_0, &**pattern_1];
|
||||
ps.extend(pattern_n);
|
||||
body.push(item(seq![
|
||||
"match self ",
|
||||
codeblock(
|
||||
ps.iter()
|
||||
.map(
|
||||
|NamedAlternative {
|
||||
variant_label: name,
|
||||
pattern: pat,
|
||||
}| ctxt.branch(|ctxt| {
|
||||
let ctorname = item(name![
|
||||
names::render_constructor(n),
|
||||
names::render_constructor(name)
|
||||
]);
|
||||
let (patpat, vc) =
|
||||
destruct(ctxt, ctorname, false, &pattern_type(pat));
|
||||
item(seq![
|
||||
patpat,
|
||||
" => ",
|
||||
pattern_unparser(ctxt, pat, &vc),
|
||||
","
|
||||
])
|
||||
})
|
||||
)
|
||||
.collect()
|
||||
)
|
||||
]));
|
||||
}
|
||||
Definition::And {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => {
|
||||
let mut ps = vec![&**pattern_0, &**pattern_1];
|
||||
ps.extend(pattern_n);
|
||||
let (patpat, vc) = destruct(
|
||||
&mut ctxt,
|
||||
item(names::render_constructor(n)),
|
||||
true,
|
||||
&record_type(&ps),
|
||||
);
|
||||
body.push(item(seq!["let ", patpat, " = self;"]));
|
||||
body.push(item(seq![
|
||||
"preserves::value::merge(vec!",
|
||||
brackets(
|
||||
ps.iter()
|
||||
.map(|p| named_pattern_unparser(&mut ctxt, p, &vc))
|
||||
.collect()
|
||||
),
|
||||
").expect",
|
||||
parens![escape_string(
|
||||
&("merge of ".to_owned() + &ctxt.fully_qualified_error_context())
|
||||
)]
|
||||
]));
|
||||
}
|
||||
Definition::Pattern(p) => {
|
||||
let (patpat, vc) = destruct(
|
||||
&mut ctxt,
|
||||
item(names::render_constructor(n)),
|
||||
true,
|
||||
&pattern_type(p),
|
||||
);
|
||||
body.push(item(seq!["let ", patpat, " = self;"]));
|
||||
body.push(pattern_unparser(&mut ctxt, p, &vc));
|
||||
}
|
||||
}
|
||||
|
||||
item(seq![
|
||||
"impl",
|
||||
ctxt.m.parse_unparse_generic_decls(&ty),
|
||||
" _support::Unparse",
|
||||
anglebrackets!["_L", ctxt.m.any_type()],
|
||||
" for ",
|
||||
names::render_constructor(n),
|
||||
ty.generic_arg(ctxt.m),
|
||||
" ",
|
||||
codeblock![seq![
|
||||
"fn unparse(&self, _ctxt: _L) -> ",
|
||||
ctxt.m.any_type(),
|
||||
" ",
|
||||
codeblock(body)
|
||||
]]
|
||||
])
|
||||
});
|
||||
}
|
||||
|
||||
fn destruct(
|
||||
ctxt: &mut FunctionContext,
|
||||
ctorname: Item,
|
||||
is_struct: bool,
|
||||
ty: &TSimple,
|
||||
) -> (impl Emittable, ValueContext) {
|
||||
match ty {
|
||||
TSimple::Field(TField::Unit) => (seq![ctorname], normal_none(is_struct)),
|
||||
TSimple::Field(_) => {
|
||||
let src = ctxt.gentempname();
|
||||
(
|
||||
seq![ctorname, parens![src.to_owned()]],
|
||||
normal_src(src, is_struct),
|
||||
)
|
||||
}
|
||||
TSimple::Record(TRecord(fs)) => {
|
||||
for (fname, fty) in fs {
|
||||
let fsrc = ctxt.gentempname();
|
||||
ctxt.capture(names::render_fieldname(fname), fty.clone(), fsrc);
|
||||
}
|
||||
(
|
||||
seq![
|
||||
ctorname,
|
||||
" ",
|
||||
braces(
|
||||
ctxt.captures
|
||||
.iter()
|
||||
.map(|c| item(seq![c.field_name.clone(), ": ", c.source_expr.clone()]))
|
||||
.collect()
|
||||
)
|
||||
],
|
||||
normal_none(is_struct),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn simple_pattern_unparser(
|
||||
ctxt: &mut FunctionContext,
|
||||
p: &SimplePattern,
|
||||
vc: &ValueContext,
|
||||
) -> Item {
|
||||
let src = &vc.src;
|
||||
match p {
|
||||
SimplePattern::Any => item(seq![src.as_ref().unwrap().to_owned(), ".clone()"]),
|
||||
SimplePattern::Atom { atom_kind: k } => match &**k {
|
||||
AtomKind::Symbol => item(seq![
|
||||
"preserves::value::Value::symbol(",
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
").wrap()"
|
||||
]),
|
||||
AtomKind::ByteString => item(seq![
|
||||
"preserves::value::Value::ByteString(",
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
".clone()).wrap()"
|
||||
]),
|
||||
_ => item(seq![
|
||||
"preserves::value::Value::from(",
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
").wrap()"
|
||||
]),
|
||||
},
|
||||
SimplePattern::Embedded { .. } => item(seq![
|
||||
"preserves::value::Value::Embedded(",
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
".clone()).wrap()"
|
||||
]),
|
||||
SimplePattern::Lit { value } => {
|
||||
item(seq![parens![ctxt.m.define_literal(value)], ".clone()"])
|
||||
}
|
||||
SimplePattern::Seqof { pattern } => {
|
||||
let mut fields_sink = sequenceify(ctxt, vc);
|
||||
let tmp = ctxt.gentempname();
|
||||
fields_sink.body.push(item(seq![
|
||||
"for ",
|
||||
tmp.to_owned(),
|
||||
" in ",
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
" ",
|
||||
codeblock![seq![
|
||||
fields_sink.vec_expr.clone(),
|
||||
".push(",
|
||||
simple_pattern_unparser(ctxt, pattern, &normal_src(tmp, true)),
|
||||
");"
|
||||
]]
|
||||
]));
|
||||
finish(fields_sink)
|
||||
}
|
||||
SimplePattern::Setof { pattern } => {
|
||||
let tmp = ctxt.gentempname();
|
||||
item(seq![
|
||||
"preserves::value::Value::Set(",
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
".iter().map(|",
|
||||
tmp.to_owned(),
|
||||
"| ",
|
||||
simple_pattern_unparser(ctxt, pattern, &normal_src(tmp, true)),
|
||||
").collect()).wrap()"
|
||||
])
|
||||
}
|
||||
SimplePattern::Dictof { key, value } => {
|
||||
let tmp_key = ctxt.gentempname();
|
||||
let tmp_value = ctxt.gentempname();
|
||||
item(seq![
|
||||
"preserves::value::Value::Dictionary(",
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
".iter().map(|(",
|
||||
tmp_key.to_owned(),
|
||||
", ",
|
||||
tmp_value.to_owned(),
|
||||
")| ",
|
||||
parens![
|
||||
simple_pattern_unparser(ctxt, key, &normal_src(tmp_key, true)),
|
||||
simple_pattern_unparser(ctxt, value, &normal_src(tmp_value, true))
|
||||
],
|
||||
").collect()).wrap()"
|
||||
])
|
||||
}
|
||||
SimplePattern::Ref(_r) => item(seq![
|
||||
src.as_ref().unwrap().to_owned(),
|
||||
if vc.is_struct { "" } else { ".as_ref()" },
|
||||
".unparse(_ctxt)"
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
fn named_pattern_unparser(ctxt: &mut FunctionContext, p: &NamedPattern, vc: &ValueContext) -> Item {
|
||||
match p {
|
||||
NamedPattern::Anonymous(p) => pattern_unparser(ctxt, p, vc),
|
||||
NamedPattern::Named(b) => {
|
||||
let Binding { name, pattern } = &**b;
|
||||
let src = ctxt
|
||||
.lookup_capture(&names::render_fieldname(name))
|
||||
.source_expr
|
||||
.to_owned();
|
||||
simple_pattern_unparser(
|
||||
ctxt,
|
||||
pattern,
|
||||
&ValueContext {
|
||||
src: Some(src),
|
||||
sink: vc.sink.clone(),
|
||||
is_struct: vc.is_struct,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pattern_unparser(ctxt: &mut FunctionContext, p: &Pattern, vc: &ValueContext) -> Item {
|
||||
match p {
|
||||
Pattern::SimplePattern(s) => simple_pattern_unparser(ctxt, s, vc),
|
||||
Pattern::CompoundPattern(c) => match &**c {
|
||||
CompoundPattern::Rec { label, fields } => {
|
||||
let rtmp = ctxt.gentempname();
|
||||
let mut body = Vec::new();
|
||||
let init_expr = item(seq![
|
||||
"preserves::value::Record(vec![",
|
||||
named_pattern_unparser(ctxt, label, &normal_none(vc.is_struct)),
|
||||
"])"
|
||||
]);
|
||||
ctxt.declare_compound(&mut body, &rtmp, init_expr);
|
||||
named_pattern_unparser(
|
||||
ctxt,
|
||||
fields,
|
||||
&ValueContext {
|
||||
src: None,
|
||||
sink: ValueSink::Fields(Rc::new(Cell::new(Some(FieldsSink {
|
||||
finish: item(seq![rtmp.clone(), ".finish().wrap()"]),
|
||||
vec_expr: item(seq![rtmp.clone(), ".fields_vec_mut()"]),
|
||||
body,
|
||||
})))),
|
||||
is_struct: vc.is_struct,
|
||||
},
|
||||
)
|
||||
}
|
||||
CompoundPattern::Tuple { patterns } => {
|
||||
let mut fields_sink = sequenceify(ctxt, vc);
|
||||
fixed_sequence_parser(ctxt, patterns, &mut fields_sink, vc.is_struct);
|
||||
finish(fields_sink)
|
||||
}
|
||||
CompoundPattern::TuplePrefix { fixed, variable } => {
|
||||
let mut fields_sink = sequenceify(ctxt, vc);
|
||||
fixed_sequence_parser(ctxt, fixed, &mut fields_sink, vc.is_struct);
|
||||
named_pattern_unparser(
|
||||
ctxt,
|
||||
&promote(variable),
|
||||
&ValueContext {
|
||||
src: vc.src.clone(),
|
||||
sink: ValueSink::Fields(Rc::new(Cell::new(Some(fields_sink)))),
|
||||
is_struct: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
CompoundPattern::Dict { entries } => {
|
||||
let dtmp = ctxt.gentempname();
|
||||
let mut body = Vec::new();
|
||||
ctxt.declare_compound(&mut body, &dtmp, item("preserves::value::Map::new()"));
|
||||
for (key_lit, value_pat) in entries.0.iter() {
|
||||
body.push(item(seq![
|
||||
dtmp.clone(),
|
||||
".insert",
|
||||
parens![
|
||||
seq![parens![ctxt.m.define_literal(key_lit)], ".clone()"],
|
||||
named_pattern_unparser(
|
||||
ctxt,
|
||||
&promote(value_pat),
|
||||
&normal_none(vc.is_struct)
|
||||
)
|
||||
],
|
||||
";"
|
||||
]));
|
||||
}
|
||||
body.push(item(seq![
|
||||
"preserves::value::Value::Dictionary(",
|
||||
dtmp,
|
||||
").wrap()"
|
||||
]));
|
||||
item(codeblock(body))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn sequenceify<'a>(ctxt: &mut FunctionContext, vc: &'a ValueContext) -> FieldsSink {
|
||||
match vc {
|
||||
ValueContext {
|
||||
sink: ValueSink::Fields(fields_sink),
|
||||
..
|
||||
} => (**fields_sink).take().unwrap(),
|
||||
_ => {
|
||||
let rtmp = ctxt.gentempname();
|
||||
let mut body = Vec::new();
|
||||
ctxt.declare_compound(&mut body, &rtmp, item("std::vec::Vec::new()"));
|
||||
FieldsSink {
|
||||
finish: item(seq![
|
||||
"preserves::value::Value::Sequence",
|
||||
parens![rtmp.clone()],
|
||||
".wrap()"
|
||||
]),
|
||||
vec_expr: item(rtmp),
|
||||
body,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(mut fields_sink: FieldsSink) -> Item {
|
||||
fields_sink.body.push(fields_sink.finish);
|
||||
item(codeblock(fields_sink.body))
|
||||
}
|
||||
|
||||
fn fixed_sequence_parser(
|
||||
ctxt: &mut FunctionContext,
|
||||
patterns: &Vec<NamedPattern>,
|
||||
fields_sink: &mut FieldsSink,
|
||||
is_struct: bool,
|
||||
) {
|
||||
for p in patterns {
|
||||
fields_sink.body.push(item(seq![
|
||||
fields_sink.vec_expr.clone(),
|
||||
".push",
|
||||
parens![named_pattern_unparser(ctxt, p, &normal_none(is_struct))],
|
||||
";"
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
|
||||
pub mod schema;
|
||||
|
||||
use crate::support as _support;
|
||||
use _support::preserves;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct Language<N: preserves::value::NestedValue> {
|
||||
pub LIT_14_FALSE: N /* #f */,
|
||||
pub LIT_27_1: N /* 1 */,
|
||||
pub LIT_0_BOOLEAN: N /* Boolean */,
|
||||
pub LIT_4_BYTE_STRING: N /* ByteString */,
|
||||
pub LIT_1_DOUBLE: N /* Double */,
|
||||
pub LIT_2_SIGNED_INTEGER: N /* SignedInteger */,
|
||||
pub LIT_3_STRING: N /* String */,
|
||||
pub LIT_5_SYMBOL: N /* Symbol */,
|
||||
pub LIT_13_AND: N /* and */,
|
||||
pub LIT_20_ANY: N /* any */,
|
||||
pub LIT_21_ATOM: N /* atom */,
|
||||
pub LIT_7_BUNDLE: N /* bundle */,
|
||||
pub LIT_17_DEFINITIONS: N /* definitions */,
|
||||
pub LIT_11_DICT: N /* dict */,
|
||||
pub LIT_26_DICTOF: N /* dictof */,
|
||||
pub LIT_22_EMBEDDED: N /* embedded */,
|
||||
pub LIT_18_EMBEDDED_TYPE: N /* embeddedType */,
|
||||
pub LIT_23_LIT: N /* lit */,
|
||||
pub LIT_6_NAMED: N /* named */,
|
||||
pub LIT_12_OR: N /* or */,
|
||||
pub LIT_8_REC: N /* rec */,
|
||||
pub LIT_15_REF: N /* ref */,
|
||||
pub LIT_16_SCHEMA: N /* schema */,
|
||||
pub LIT_24_SEQOF: N /* seqof */,
|
||||
pub LIT_25_SETOF: N /* setof */,
|
||||
pub LIT_9_TUPLE: N /* tuple */,
|
||||
pub LIT_10_TUPLE_PREFIX: N /* tuplePrefix */,
|
||||
pub LIT_19_VERSION: N /* version */
|
||||
}
|
||||
|
||||
impl<N: preserves::value::NestedValue> Default for Language<N> {
|
||||
fn default() -> Self {
|
||||
Language {
|
||||
LIT_14_FALSE: /* #f */ _support::decode_lit(&[128]).unwrap(),
|
||||
LIT_27_1: /* 1 */ _support::decode_lit(&[176, 1, 1]).unwrap(),
|
||||
LIT_0_BOOLEAN: /* Boolean */ _support::decode_lit(&[179, 7, 66, 111, 111, 108, 101, 97, 110]).unwrap(),
|
||||
LIT_4_BYTE_STRING: /* ByteString */ _support::decode_lit(&[179, 10, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103]).unwrap(),
|
||||
LIT_1_DOUBLE: /* Double */ _support::decode_lit(&[179, 6, 68, 111, 117, 98, 108, 101]).unwrap(),
|
||||
LIT_2_SIGNED_INTEGER: /* SignedInteger */ _support::decode_lit(&[179, 13, 83, 105, 103, 110, 101, 100, 73, 110, 116, 101, 103, 101, 114]).unwrap(),
|
||||
LIT_3_STRING: /* String */ _support::decode_lit(&[179, 6, 83, 116, 114, 105, 110, 103]).unwrap(),
|
||||
LIT_5_SYMBOL: /* Symbol */ _support::decode_lit(&[179, 6, 83, 121, 109, 98, 111, 108]).unwrap(),
|
||||
LIT_13_AND: /* and */ _support::decode_lit(&[179, 3, 97, 110, 100]).unwrap(),
|
||||
LIT_20_ANY: /* any */ _support::decode_lit(&[179, 3, 97, 110, 121]).unwrap(),
|
||||
LIT_21_ATOM: /* atom */ _support::decode_lit(&[179, 4, 97, 116, 111, 109]).unwrap(),
|
||||
LIT_7_BUNDLE: /* bundle */ _support::decode_lit(&[179, 6, 98, 117, 110, 100, 108, 101]).unwrap(),
|
||||
LIT_17_DEFINITIONS: /* definitions */ _support::decode_lit(&[179, 11, 100, 101, 102, 105, 110, 105, 116, 105, 111, 110, 115]).unwrap(),
|
||||
LIT_11_DICT: /* dict */ _support::decode_lit(&[179, 4, 100, 105, 99, 116]).unwrap(),
|
||||
LIT_26_DICTOF: /* dictof */ _support::decode_lit(&[179, 6, 100, 105, 99, 116, 111, 102]).unwrap(),
|
||||
LIT_22_EMBEDDED: /* embedded */ _support::decode_lit(&[179, 8, 101, 109, 98, 101, 100, 100, 101, 100]).unwrap(),
|
||||
LIT_18_EMBEDDED_TYPE: /* embeddedType */ _support::decode_lit(&[179, 12, 101, 109, 98, 101, 100, 100, 101, 100, 84, 121, 112, 101]).unwrap(),
|
||||
LIT_23_LIT: /* lit */ _support::decode_lit(&[179, 3, 108, 105, 116]).unwrap(),
|
||||
LIT_6_NAMED: /* named */ _support::decode_lit(&[179, 5, 110, 97, 109, 101, 100]).unwrap(),
|
||||
LIT_12_OR: /* or */ _support::decode_lit(&[179, 2, 111, 114]).unwrap(),
|
||||
LIT_8_REC: /* rec */ _support::decode_lit(&[179, 3, 114, 101, 99]).unwrap(),
|
||||
LIT_15_REF: /* ref */ _support::decode_lit(&[179, 3, 114, 101, 102]).unwrap(),
|
||||
LIT_16_SCHEMA: /* schema */ _support::decode_lit(&[179, 6, 115, 99, 104, 101, 109, 97]).unwrap(),
|
||||
LIT_24_SEQOF: /* seqof */ _support::decode_lit(&[179, 5, 115, 101, 113, 111, 102]).unwrap(),
|
||||
LIT_25_SETOF: /* setof */ _support::decode_lit(&[179, 5, 115, 101, 116, 111, 102]).unwrap(),
|
||||
LIT_9_TUPLE: /* tuple */ _support::decode_lit(&[179, 5, 116, 117, 112, 108, 101]).unwrap(),
|
||||
LIT_10_TUPLE_PREFIX: /* tuplePrefix */ _support::decode_lit(&[179, 11, 116, 117, 112, 108, 101, 80, 114, 101, 102, 105, 120]).unwrap(),
|
||||
LIT_19_VERSION: /* version */ _support::decode_lit(&[179, 7, 118, 101, 114, 115, 105, 111, 110]).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _bundle() -> &'static [u8] {
|
||||
b"\
|
||||
\xb4\xb3\x06\x62\x75\x6e\x64\x6c\x65\xb7\xb5\xb3\x06\x73\x63\x68\
|
||||
\x65\x6d\x61\x84\xb4\xb3\x06\x73\x63\x68\x65\x6d\x61\xb7\xb3\x07\
|
||||
\x76\x65\x72\x73\x69\x6f\x6e\xb0\x01\x01\xb3\x0b\x64\x65\x66\x69\
|
||||
\x6e\x69\x74\x69\x6f\x6e\x73\xb7\xb3\x03\x52\x65\x66\xb4\xb3\x03\
|
||||
\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\xb3\x03\x72\x65\x66\x84\xb4\
|
||||
\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\
|
||||
\xb3\x06\x6d\x6f\x64\x75\x6c\x65\xb4\xb3\x03\x72\x65\x66\xb5\x84\
|
||||
\xb3\x0a\x4d\x6f\x64\x75\x6c\x65\x50\x61\x74\x68\x84\x84\xb4\xb3\
|
||||
\x05\x6e\x61\x6d\x65\x64\xb3\x04\x6e\x61\x6d\x65\xb4\xb3\x04\x61\
|
||||
\x74\x6f\x6d\xb3\x06\x53\x79\x6d\x62\x6f\x6c\x84\x84\x84\x84\x84\
|
||||
\xb3\x06\x42\x75\x6e\x64\x6c\x65\xb4\xb3\x03\x72\x65\x63\xb4\xb3\
|
||||
\x03\x6c\x69\x74\xb3\x06\x62\x75\x6e\x64\x6c\x65\x84\xb4\xb3\x05\
|
||||
\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x07\
|
||||
\x6d\x6f\x64\x75\x6c\x65\x73\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\
|
||||
\x07\x4d\x6f\x64\x75\x6c\x65\x73\x84\x84\x84\x84\x84\xb3\x06\x53\
|
||||
\x63\x68\x65\x6d\x61\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\
|
||||
\x74\xb3\x06\x73\x63\x68\x65\x6d\x61\x84\xb4\xb3\x05\x74\x75\x70\
|
||||
\x6c\x65\xb5\xb4\xb3\x04\x64\x69\x63\x74\xb7\xb3\x07\x76\x65\x72\
|
||||
\x73\x69\x6f\x6e\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x07\x76\x65\
|
||||
\x72\x73\x69\x6f\x6e\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x07\x56\
|
||||
\x65\x72\x73\x69\x6f\x6e\x84\x84\xb3\x0b\x64\x65\x66\x69\x6e\x69\
|
||||
\x74\x69\x6f\x6e\x73\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x0b\x64\
|
||||
\x65\x66\x69\x6e\x69\x74\x69\x6f\x6e\x73\xb4\xb3\x03\x72\x65\x66\
|
||||
\xb5\x84\xb3\x0b\x44\x65\x66\x69\x6e\x69\x74\x69\x6f\x6e\x73\x84\
|
||||
\x84\xb3\x0c\x65\x6d\x62\x65\x64\x64\x65\x64\x54\x79\x70\x65\xb4\
|
||||
\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x0c\x65\x6d\x62\x65\x64\x64\x65\
|
||||
\x64\x54\x79\x70\x65\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x10\x45\
|
||||
\x6d\x62\x65\x64\x64\x65\x64\x54\x79\x70\x65\x4e\x61\x6d\x65\x84\
|
||||
\x84\x84\x84\x84\x84\x84\xb3\x07\x42\x69\x6e\x64\x69\x6e\x67\xb4\
|
||||
\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\xb3\x05\x6e\x61\x6d\
|
||||
\x65\x64\x84\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x05\x6e\
|
||||
\x61\x6d\x65\x64\xb3\x04\x6e\x61\x6d\x65\xb4\xb3\x04\x61\x74\x6f\
|
||||
\x6d\xb3\x06\x53\x79\x6d\x62\x6f\x6c\x84\x84\xb4\xb3\x05\x6e\x61\
|
||||
\x6d\x65\x64\xb3\x07\x70\x61\x74\x74\x65\x72\x6e\xb4\xb3\x03\x72\
|
||||
\x65\x66\xb5\x84\xb3\x0d\x53\x69\x6d\x70\x6c\x65\x50\x61\x74\x74\
|
||||
\x65\x72\x6e\x84\x84\x84\x84\x84\xb3\x07\x4d\x6f\x64\x75\x6c\x65\
|
||||
\x73\xb4\xb3\x06\x64\x69\x63\x74\x6f\x66\xb4\xb3\x03\x72\x65\x66\
|
||||
\xb5\x84\xb3\x0a\x4d\x6f\x64\x75\x6c\x65\x50\x61\x74\x68\x84\xb4\
|
||||
\xb3\x03\x72\x65\x66\xb5\x84\xb3\x06\x53\x63\x68\x65\x6d\x61\x84\
|
||||
\x84\xb3\x07\x50\x61\x74\x74\x65\x72\x6e\xb4\xb3\x02\x6f\x72\xb5\
|
||||
\xb5\xb1\x0d\x53\x69\x6d\x70\x6c\x65\x50\x61\x74\x74\x65\x72\x6e\
|
||||
\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0d\x53\x69\x6d\x70\x6c\x65\
|
||||
\x50\x61\x74\x74\x65\x72\x6e\x84\x84\xb5\xb1\x0f\x43\x6f\x6d\x70\
|
||||
\x6f\x75\x6e\x64\x50\x61\x74\x74\x65\x72\x6e\xb4\xb3\x03\x72\x65\
|
||||
\x66\xb5\x84\xb3\x0f\x43\x6f\x6d\x70\x6f\x75\x6e\x64\x50\x61\x74\
|
||||
\x74\x65\x72\x6e\x84\x84\x84\x84\xb3\x07\x56\x65\x72\x73\x69\x6f\
|
||||
\x6e\xb4\xb3\x03\x6c\x69\x74\xb0\x01\x01\x84\xb3\x08\x41\x74\x6f\
|
||||
\x6d\x4b\x69\x6e\x64\xb4\xb3\x02\x6f\x72\xb5\xb5\xb1\x07\x42\x6f\
|
||||
\x6f\x6c\x65\x61\x6e\xb4\xb3\x03\x6c\x69\x74\xb3\x07\x42\x6f\x6f\
|
||||
\x6c\x65\x61\x6e\x84\x84\xb5\xb1\x06\x44\x6f\x75\x62\x6c\x65\xb4\
|
||||
\xb3\x03\x6c\x69\x74\xb3\x06\x44\x6f\x75\x62\x6c\x65\x84\x84\xb5\
|
||||
\xb1\x0d\x53\x69\x67\x6e\x65\x64\x49\x6e\x74\x65\x67\x65\x72\xb4\
|
||||
\xb3\x03\x6c\x69\x74\xb3\x0d\x53\x69\x67\x6e\x65\x64\x49\x6e\x74\
|
||||
\x65\x67\x65\x72\x84\x84\xb5\xb1\x06\x53\x74\x72\x69\x6e\x67\xb4\
|
||||
\xb3\x03\x6c\x69\x74\xb3\x06\x53\x74\x72\x69\x6e\x67\x84\x84\xb5\
|
||||
\xb1\x0a\x42\x79\x74\x65\x53\x74\x72\x69\x6e\x67\xb4\xb3\x03\x6c\
|
||||
\x69\x74\xb3\x0a\x42\x79\x74\x65\x53\x74\x72\x69\x6e\x67\x84\x84\
|
||||
\xb5\xb1\x06\x53\x79\x6d\x62\x6f\x6c\xb4\xb3\x03\x6c\x69\x74\xb3\
|
||||
\x06\x53\x79\x6d\x62\x6f\x6c\x84\x84\x84\x84\xb3\x0a\x44\x65\x66\
|
||||
\x69\x6e\x69\x74\x69\x6f\x6e\xb4\xb3\x02\x6f\x72\xb5\xb5\xb1\x02\
|
||||
\x6f\x72\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\xb3\x02\
|
||||
\x6f\x72\x84\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x0b\x74\
|
||||
\x75\x70\x6c\x65\x50\x72\x65\x66\x69\x78\xb5\xb4\xb3\x05\x6e\x61\
|
||||
\x6d\x65\x64\xb3\x08\x70\x61\x74\x74\x65\x72\x6e\x30\xb4\xb3\x03\
|
||||
\x72\x65\x66\xb5\x84\xb3\x10\x4e\x61\x6d\x65\x64\x41\x6c\x74\x65\
|
||||
\x72\x6e\x61\x74\x69\x76\x65\x84\x84\xb4\xb3\x05\x6e\x61\x6d\x65\
|
||||
\x64\xb3\x08\x70\x61\x74\x74\x65\x72\x6e\x31\xb4\xb3\x03\x72\x65\
|
||||
\x66\xb5\x84\xb3\x10\x4e\x61\x6d\x65\x64\x41\x6c\x74\x65\x72\x6e\
|
||||
\x61\x74\x69\x76\x65\x84\x84\x84\xb4\xb3\x05\x6e\x61\x6d\x65\x64\
|
||||
\xb3\x08\x70\x61\x74\x74\x65\x72\x6e\x4e\xb4\xb3\x05\x73\x65\x71\
|
||||
\x6f\x66\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x10\x4e\x61\x6d\x65\
|
||||
\x64\x41\x6c\x74\x65\x72\x6e\x61\x74\x69\x76\x65\x84\x84\x84\x84\
|
||||
\x84\x84\x84\x84\xb5\xb1\x03\x61\x6e\x64\xb4\xb3\x03\x72\x65\x63\
|
||||
\xb4\xb3\x03\x6c\x69\x74\xb3\x03\x61\x6e\x64\x84\xb4\xb3\x05\x74\
|
||||
\x75\x70\x6c\x65\xb5\xb4\xb3\x0b\x74\x75\x70\x6c\x65\x50\x72\x65\
|
||||
\x66\x69\x78\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x08\x70\x61\
|
||||
\x74\x74\x65\x72\x6e\x30\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0c\
|
||||
\x4e\x61\x6d\x65\x64\x50\x61\x74\x74\x65\x72\x6e\x84\x84\xb4\xb3\
|
||||
\x05\x6e\x61\x6d\x65\x64\xb3\x08\x70\x61\x74\x74\x65\x72\x6e\x31\
|
||||
\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0c\x4e\x61\x6d\x65\x64\x50\
|
||||
\x61\x74\x74\x65\x72\x6e\x84\x84\x84\xb4\xb3\x05\x6e\x61\x6d\x65\
|
||||
\x64\xb3\x08\x70\x61\x74\x74\x65\x72\x6e\x4e\xb4\xb3\x05\x73\x65\
|
||||
\x71\x6f\x66\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0c\x4e\x61\x6d\
|
||||
\x65\x64\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\x84\x84\x84\
|
||||
\x84\xb5\xb1\x07\x50\x61\x74\x74\x65\x72\x6e\xb4\xb3\x03\x72\x65\
|
||||
\x66\xb5\x84\xb3\x07\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\
|
||||
\xb3\x0a\x4d\x6f\x64\x75\x6c\x65\x50\x61\x74\x68\xb4\xb3\x05\x73\
|
||||
\x65\x71\x6f\x66\xb4\xb3\x04\x61\x74\x6f\x6d\xb3\x06\x53\x79\x6d\
|
||||
\x62\x6f\x6c\x84\x84\xb3\x0b\x44\x65\x66\x69\x6e\x69\x74\x69\x6f\
|
||||
\x6e\x73\xb4\xb3\x06\x64\x69\x63\x74\x6f\x66\xb4\xb3\x04\x61\x74\
|
||||
\x6f\x6d\xb3\x06\x53\x79\x6d\x62\x6f\x6c\x84\xb4\xb3\x03\x72\x65\
|
||||
\x66\xb5\x84\xb3\x0a\x44\x65\x66\x69\x6e\x69\x74\x69\x6f\x6e\x84\
|
||||
\x84\xb3\x0c\x4e\x61\x6d\x65\x64\x50\x61\x74\x74\x65\x72\x6e\xb4\
|
||||
\xb3\x02\x6f\x72\xb5\xb5\xb1\x05\x6e\x61\x6d\x65\x64\xb4\xb3\x03\
|
||||
\x72\x65\x66\xb5\x84\xb3\x07\x42\x69\x6e\x64\x69\x6e\x67\x84\x84\
|
||||
\xb5\xb1\x09\x61\x6e\x6f\x6e\x79\x6d\x6f\x75\x73\xb4\xb3\x03\x72\
|
||||
\x65\x66\xb5\x84\xb3\x07\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\
|
||||
\x84\xb3\x0d\x53\x69\x6d\x70\x6c\x65\x50\x61\x74\x74\x65\x72\x6e\
|
||||
\xb4\xb3\x02\x6f\x72\xb5\xb5\xb1\x03\x61\x6e\x79\xb4\xb3\x03\x6c\
|
||||
\x69\x74\xb3\x03\x61\x6e\x79\x84\x84\xb5\xb1\x04\x61\x74\x6f\x6d\
|
||||
\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\xb3\x04\x61\x74\
|
||||
\x6f\x6d\x84\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x05\x6e\
|
||||
\x61\x6d\x65\x64\xb3\x08\x61\x74\x6f\x6d\x4b\x69\x6e\x64\xb4\xb3\
|
||||
\x03\x72\x65\x66\xb5\x84\xb3\x08\x41\x74\x6f\x6d\x4b\x69\x6e\x64\
|
||||
\x84\x84\x84\x84\x84\x84\xb5\xb1\x08\x65\x6d\x62\x65\x64\x64\x65\
|
||||
\x64\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\xb3\x08\x65\
|
||||
\x6d\x62\x65\x64\x64\x65\x64\x84\xb4\xb3\x05\x74\x75\x70\x6c\x65\
|
||||
\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x09\x69\x6e\x74\x65\x72\
|
||||
\x66\x61\x63\x65\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0d\x53\x69\
|
||||
\x6d\x70\x6c\x65\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\x84\
|
||||
\x84\xb5\xb1\x03\x6c\x69\x74\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\
|
||||
\x6c\x69\x74\xb3\x03\x6c\x69\x74\x84\xb4\xb3\x05\x74\x75\x70\x6c\
|
||||
\x65\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x05\x76\x61\x6c\x75\
|
||||
\x65\xb3\x03\x61\x6e\x79\x84\x84\x84\x84\x84\xb5\xb1\x05\x73\x65\
|
||||
\x71\x6f\x66\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\xb3\
|
||||
\x05\x73\x65\x71\x6f\x66\x84\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\
|
||||
\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x07\x70\x61\x74\x74\x65\x72\
|
||||
\x6e\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0d\x53\x69\x6d\x70\x6c\
|
||||
\x65\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\x84\x84\xb5\xb1\
|
||||
\x05\x73\x65\x74\x6f\x66\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\
|
||||
\x69\x74\xb3\x05\x73\x65\x74\x6f\x66\x84\xb4\xb3\x05\x74\x75\x70\
|
||||
\x6c\x65\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x07\x70\x61\x74\
|
||||
\x74\x65\x72\x6e\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0d\x53\x69\
|
||||
\x6d\x70\x6c\x65\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\x84\
|
||||
\x84\xb5\xb1\x06\x64\x69\x63\x74\x6f\x66\xb4\xb3\x03\x72\x65\x63\
|
||||
\xb4\xb3\x03\x6c\x69\x74\xb3\x06\x64\x69\x63\x74\x6f\x66\x84\xb4\
|
||||
\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\
|
||||
\xb3\x03\x6b\x65\x79\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0d\x53\
|
||||
\x69\x6d\x70\x6c\x65\x50\x61\x74\x74\x65\x72\x6e\x84\x84\xb4\xb3\
|
||||
\x05\x6e\x61\x6d\x65\x64\xb3\x05\x76\x61\x6c\x75\x65\xb4\xb3\x03\
|
||||
\x72\x65\x66\xb5\x84\xb3\x0d\x53\x69\x6d\x70\x6c\x65\x50\x61\x74\
|
||||
\x74\x65\x72\x6e\x84\x84\x84\x84\x84\x84\xb5\xb1\x03\x52\x65\x66\
|
||||
\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x03\x52\x65\x66\x84\x84\x84\
|
||||
\x84\xb3\x0f\x43\x6f\x6d\x70\x6f\x75\x6e\x64\x50\x61\x74\x74\x65\
|
||||
\x72\x6e\xb4\xb3\x02\x6f\x72\xb5\xb5\xb1\x03\x72\x65\x63\xb4\xb3\
|
||||
\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\xb3\x03\x72\x65\x63\x84\
|
||||
\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\
|
||||
\x64\xb3\x05\x6c\x61\x62\x65\x6c\xb4\xb3\x03\x72\x65\x66\xb5\x84\
|
||||
\xb3\x0c\x4e\x61\x6d\x65\x64\x50\x61\x74\x74\x65\x72\x6e\x84\x84\
|
||||
\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x06\x66\x69\x65\x6c\x64\x73\
|
||||
\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0c\x4e\x61\x6d\x65\x64\x50\
|
||||
\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\x84\x84\xb5\xb1\x05\x74\
|
||||
\x75\x70\x6c\x65\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\
|
||||
\xb3\x05\x74\x75\x70\x6c\x65\x84\xb4\xb3\x05\x74\x75\x70\x6c\x65\
|
||||
\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x08\x70\x61\x74\x74\x65\
|
||||
\x72\x6e\x73\xb4\xb3\x05\x73\x65\x71\x6f\x66\xb4\xb3\x03\x72\x65\
|
||||
\x66\xb5\x84\xb3\x0c\x4e\x61\x6d\x65\x64\x50\x61\x74\x74\x65\x72\
|
||||
\x6e\x84\x84\x84\x84\x84\x84\x84\xb5\xb1\x0b\x74\x75\x70\x6c\x65\
|
||||
\x50\x72\x65\x66\x69\x78\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\
|
||||
\x69\x74\xb3\x0b\x74\x75\x70\x6c\x65\x50\x72\x65\x66\x69\x78\x84\
|
||||
\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\xb3\x05\x6e\x61\x6d\x65\
|
||||
\x64\xb3\x05\x66\x69\x78\x65\x64\xb4\xb3\x05\x73\x65\x71\x6f\x66\
|
||||
\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x0c\x4e\x61\x6d\x65\x64\x50\
|
||||
\x61\x74\x74\x65\x72\x6e\x84\x84\x84\xb4\xb3\x05\x6e\x61\x6d\x65\
|
||||
\x64\xb3\x08\x76\x61\x72\x69\x61\x62\x6c\x65\xb4\xb3\x03\x72\x65\
|
||||
\x66\xb5\x84\xb3\x12\x4e\x61\x6d\x65\x64\x53\x69\x6d\x70\x6c\x65\
|
||||
\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\x84\x84\xb5\xb1\x04\
|
||||
\x64\x69\x63\x74\xb4\xb3\x03\x72\x65\x63\xb4\xb3\x03\x6c\x69\x74\
|
||||
\xb3\x04\x64\x69\x63\x74\x84\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\
|
||||
\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x07\x65\x6e\x74\x72\x69\x65\
|
||||
\x73\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x11\x44\x69\x63\x74\x69\
|
||||
\x6f\x6e\x61\x72\x79\x45\x6e\x74\x72\x69\x65\x73\x84\x84\x84\x84\
|
||||
\x84\x84\x84\x84\xb3\x10\x45\x6d\x62\x65\x64\x64\x65\x64\x54\x79\
|
||||
\x70\x65\x4e\x61\x6d\x65\xb4\xb3\x02\x6f\x72\xb5\xb5\xb1\x05\x66\
|
||||
\x61\x6c\x73\x65\xb4\xb3\x03\x6c\x69\x74\x80\x84\x84\xb5\xb1\x03\
|
||||
\x52\x65\x66\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\x03\x52\x65\x66\
|
||||
\x84\x84\x84\x84\xb3\x10\x4e\x61\x6d\x65\x64\x41\x6c\x74\x65\x72\
|
||||
\x6e\x61\x74\x69\x76\x65\xb4\xb3\x05\x74\x75\x70\x6c\x65\xb5\xb4\
|
||||
\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x0c\x76\x61\x72\x69\x61\x6e\x74\
|
||||
\x4c\x61\x62\x65\x6c\xb4\xb3\x04\x61\x74\x6f\x6d\xb3\x06\x53\x74\
|
||||
\x72\x69\x6e\x67\x84\x84\xb4\xb3\x05\x6e\x61\x6d\x65\x64\xb3\x07\
|
||||
\x70\x61\x74\x74\x65\x72\x6e\xb4\xb3\x03\x72\x65\x66\xb5\x84\xb3\
|
||||
\x07\x50\x61\x74\x74\x65\x72\x6e\x84\x84\x84\x84\xb3\x11\x44\x69\
|
||||
\x63\x74\x69\x6f\x6e\x61\x72\x79\x45\x6e\x74\x72\x69\x65\x73\xb4\
|
||||
\xb3\x06\x64\x69\x63\x74\x6f\x66\xb3\x03\x61\x6e\x79\xb4\xb3\x03\
|
||||
\x72\x65\x66\xb5\x84\xb3\x12\x4e\x61\x6d\x65\x64\x53\x69\x6d\x70\
|
||||
\x6c\x65\x50\x61\x74\x74\x65\x72\x6e\x84\x84\xb3\x12\x4e\x61\x6d\
|
||||
\x65\x64\x53\x69\x6d\x70\x6c\x65\x50\x61\x74\x74\x65\x72\x6e\xb4\
|
||||
\xb3\x02\x6f\x72\xb5\xb5\xb1\x05\x6e\x61\x6d\x65\x64\xb4\xb3\x03\
|
||||
\x72\x65\x66\xb5\x84\xb3\x07\x42\x69\x6e\x64\x69\x6e\x67\x84\x84\
|
||||
\xb5\xb1\x09\x61\x6e\x6f\x6e\x79\x6d\x6f\x75\x73\xb4\xb3\x03\x72\
|
||||
\x65\x66\xb5\x84\xb3\x0d\x53\x69\x6d\x70\x6c\x65\x50\x61\x74\x74\
|
||||
\x65\x72\x6e\x84\x84\x84\x84\x84\xb3\x0c\x65\x6d\x62\x65\x64\x64\
|
||||
\x65\x64\x54\x79\x70\x65\x80\x84\x84\x84\x84"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,98 +0,0 @@
|
|||
#![doc = concat!(
|
||||
include_str!("../README.md"),
|
||||
"# What is Preserves Schema?\n\n",
|
||||
include_str!("../doc/what-is-preserves-schema.md"),
|
||||
include_str!("../doc/example.md"),
|
||||
)]
|
||||
|
||||
pub mod compiler;
|
||||
/// Auto-generated Preserves Schema Metaschema types, parsers, and unparsers.
|
||||
pub mod gen;
|
||||
pub mod support;
|
||||
pub mod syntax;
|
||||
|
||||
pub use support::Codec;
|
||||
pub use support::Deserialize;
|
||||
pub use support::ParseError;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn can_access_preserves_core() {
|
||||
use preserves::value::*;
|
||||
assert_eq!(format!("{:?}", UnwrappedIOValue::from(3 + 4)), "7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_rendering() {
|
||||
use crate::syntax::block::*;
|
||||
use crate::*;
|
||||
|
||||
let code = semiblock![
|
||||
seq!["f", parens!["a", "b", "c"]],
|
||||
seq!["f", parens!["a", "b", "c"]],
|
||||
seq!["f", parens!["a", "b", "c"]],
|
||||
seq!["f", parens!["a", "b", "c"]],
|
||||
seq!["f", parens!["a", "b", "c"]],
|
||||
seq!["f", parens!["a", "b", "c"]],
|
||||
seq!["g", parens![]],
|
||||
parens![]
|
||||
];
|
||||
println!("{}", Formatter::to_string(&code));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metaschema_parsing() -> Result<(), std::io::Error> {
|
||||
use crate::gen::schema::*;
|
||||
use crate::support::Parse;
|
||||
use crate::support::Unparse;
|
||||
use preserves::value::{BinarySource, IOBinarySource, Reader};
|
||||
|
||||
let mut f = std::fs::File::open("../../../schema/schema.bin")?;
|
||||
let mut src = IOBinarySource::new(&mut f);
|
||||
let mut reader = src.packed_iovalues();
|
||||
let schema = reader.demand_next(false)?;
|
||||
let language = crate::gen::Language::default();
|
||||
let parsed = Schema::parse(&language, &schema).expect("successful parse");
|
||||
assert_eq!(schema, parsed.unparse(&language));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! define_language {
|
||||
($fname:ident () : $lang:ident < $default_value:ty > { $($field:ident : $($type:ident)::+ ,)* }) => {
|
||||
pub struct $lang<N: $crate::support::preserves::value::NestedValue> {
|
||||
$(pub $field: std::sync::Arc<$($type)::*<N>>),*
|
||||
}
|
||||
|
||||
$(impl<'a, N: $crate::support::preserves::value::NestedValue> From<&'a $lang<N>> for &'a $($type)::*<N> {
|
||||
fn from(v: &'a $lang<N>) -> Self {
|
||||
&v.$field
|
||||
}
|
||||
})*
|
||||
|
||||
impl<N: $crate::support::preserves::value::NestedValue> $crate::support::NestedValueCodec
|
||||
for $lang<N> {}
|
||||
|
||||
mod $fname {
|
||||
use super::*;
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref GLOBAL_LANG: std::sync::Arc<$lang<$default_value>> =
|
||||
std::sync::Arc::new($lang {
|
||||
$($field: std::sync::Arc::new($($type)::*::default())),*
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl $lang<$default_value> {
|
||||
pub fn arc() -> &'static std::sync::Arc<$lang<$default_value>> {
|
||||
&*$fname::GLOBAL_LANG
|
||||
}
|
||||
}
|
||||
|
||||
pub fn $fname() -> &'static $lang<$default_value> {
|
||||
&*$fname::GLOBAL_LANG
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,297 +0,0 @@
|
|||
//! Interpreter for instances of Preserves Schema Metaschema, for schema-directed dynamic
|
||||
//! parsing and unparsing of terms.
|
||||
|
||||
use crate::gen::schema::*;
|
||||
|
||||
use preserves::value::merge::merge2;
|
||||
use preserves::value::Map;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::Value;
|
||||
|
||||
/// Represents an environment mapping schema module names to [Schema] instances.
|
||||
pub type Env<V> = Map<Vec<String>, Schema<V>>;
|
||||
|
||||
/// Context for a given interpretation of a [Schema].
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'a, V: NestedValue> {
|
||||
pub env: &'a Env<V>,
|
||||
module: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DynField<V: NestedValue> {
|
||||
Simple(V),
|
||||
Compound(Map<V, V>),
|
||||
}
|
||||
|
||||
impl<'a, V: NestedValue> Context<'a, V> {
|
||||
/// Construct a new [Context] with the given [Env].
|
||||
pub fn new(env: &'a Env<V>) -> Self {
|
||||
Context {
|
||||
env,
|
||||
module: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse `v` using the rule named `name` from the module at path `module` in `self.env`.
|
||||
/// Yields `Some(...)` if the parse succeeds, and `None` otherwise.
|
||||
pub fn dynamic_parse(&mut self, module: &Vec<String>, name: &str, v: &V) -> Option<V> {
|
||||
let old_module =
|
||||
(module.len() > 0).then(|| std::mem::replace(&mut self.module, module.clone()));
|
||||
let schema = self.env.get(&self.module);
|
||||
let definition = schema.and_then(|s| s.definitions.0.get(name));
|
||||
let result = definition.and_then(|d| d.dynamic_parse(self, v));
|
||||
if let Some(m) = old_module {
|
||||
self.module = m;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn dynamic_unparse(&mut self, _module: &Vec<String>, _name: &str, _w: &V) -> Option<V> {
|
||||
panic!("Not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> Definition<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<V> {
|
||||
match self {
|
||||
Definition::Or {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => pattern_0
|
||||
.dynamic_parse(ctxt, v)
|
||||
.or_else(|| pattern_1.dynamic_parse(ctxt, v))
|
||||
.or_else(|| pattern_n.iter().find_map(|p| p.dynamic_parse(ctxt, v))),
|
||||
Definition::And {
|
||||
pattern_0,
|
||||
pattern_1,
|
||||
pattern_n,
|
||||
} => pattern_0
|
||||
.dynamic_parse(ctxt, v)
|
||||
.and_then(|w0| {
|
||||
pattern_1.dynamic_parse(ctxt, v).and_then(|w1| {
|
||||
pattern_n.iter().fold(merge(w0, w1), |w, p| {
|
||||
w.and_then(|w| p.dynamic_parse(ctxt, v).and_then(|wn| merge(w, wn)))
|
||||
})
|
||||
})
|
||||
})
|
||||
.map(|w| DynField::Compound(w).finish()),
|
||||
Definition::Pattern(p) => p.dynamic_parse(ctxt, v).map(|w| w.finish()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> NamedAlternative<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<V> {
|
||||
self.pattern.dynamic_parse(ctxt, v).map(|w| {
|
||||
let mut r = Value::simple_record(&self.variant_label, 1);
|
||||
match w {
|
||||
DynField::Simple(field) => r.fields_vec_mut().push(field),
|
||||
DynField::Compound(fields) => {
|
||||
if fields.len() > 0 {
|
||||
r.fields_vec_mut().push(V::new(fields))
|
||||
}
|
||||
}
|
||||
}
|
||||
r.finish().wrap()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> NamedPattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<Map<V, V>> {
|
||||
match self {
|
||||
NamedPattern::Named(b) => {
|
||||
let binding = &**b;
|
||||
binding
|
||||
.pattern
|
||||
.dynamic_parse(ctxt, v)
|
||||
.map(|w| w.to_map(Some(&binding.name)))
|
||||
}
|
||||
NamedPattern::Anonymous(b) => b.dynamic_parse(ctxt, v).map(|w| w.to_map(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> NamedSimplePattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
|
||||
match self {
|
||||
NamedSimplePattern::Named(b) => {
|
||||
let binding = &**b;
|
||||
binding
|
||||
.pattern
|
||||
.dynamic_parse(ctxt, v)
|
||||
.map(|w| DynField::Compound(w.to_map(Some(&binding.name))))
|
||||
}
|
||||
NamedSimplePattern::Anonymous(b) => b.dynamic_parse(ctxt, v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> SimplePattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
|
||||
match self {
|
||||
SimplePattern::Any => Some(DynField::Simple(v.clone())),
|
||||
SimplePattern::Atom { atom_kind } => match &**atom_kind {
|
||||
AtomKind::Boolean => v.value().is_boolean().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::Double => v.value().is_double().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::SignedInteger => v
|
||||
.value()
|
||||
.is_signedinteger()
|
||||
.then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::String => v.value().is_string().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::ByteString => v
|
||||
.value()
|
||||
.is_bytestring()
|
||||
.then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::Symbol => v.value().is_symbol().then(|| DynField::Simple(v.clone())),
|
||||
},
|
||||
SimplePattern::Embedded { .. } => {
|
||||
v.value().is_embedded().then(|| DynField::Simple(v.clone()))
|
||||
}
|
||||
SimplePattern::Lit { value } => (v == value).then(|| DynField::Compound(Map::new())),
|
||||
SimplePattern::Seqof { pattern } => v
|
||||
.value()
|
||||
.as_sequence()
|
||||
.and_then(|vs| {
|
||||
vs.iter()
|
||||
.map(|v| (**pattern).dynamic_parse(ctxt, v).map(|w| w.finish()))
|
||||
.collect::<Option<Vec<V>>>()
|
||||
})
|
||||
.map(|ws| DynField::Simple(V::new(ws))),
|
||||
SimplePattern::Setof { pattern } => v
|
||||
.value()
|
||||
.as_set()
|
||||
.and_then(|vs| {
|
||||
vs.iter()
|
||||
.map(|v| (**pattern).dynamic_parse(ctxt, v).map(|w| w.finish()))
|
||||
.collect::<Option<Vec<V>>>()
|
||||
})
|
||||
.map(|ws| DynField::Simple(V::new(ws))),
|
||||
SimplePattern::Dictof { key, value } => v
|
||||
.value()
|
||||
.as_dictionary()
|
||||
.and_then(|d| {
|
||||
d.iter()
|
||||
.map(|(k, v)| {
|
||||
(**key).dynamic_parse(ctxt, k).and_then(|kw| {
|
||||
(**value)
|
||||
.dynamic_parse(ctxt, v)
|
||||
.map(|vw| (kw.finish(), vw.finish()))
|
||||
})
|
||||
})
|
||||
.collect::<Option<Map<V, V>>>()
|
||||
})
|
||||
.map(|d| DynField::Simple(V::new(d))),
|
||||
SimplePattern::Ref(r) => ctxt
|
||||
.dynamic_parse(&r.module.0, &r.name, v)
|
||||
.map(DynField::Simple),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> CompoundPattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<Map<V, V>> {
|
||||
match self {
|
||||
CompoundPattern::Rec { label, fields } => v.value().as_record(None).and_then(|r| {
|
||||
(**label).dynamic_parse(ctxt, r.label()).and_then(|lw| {
|
||||
(**fields)
|
||||
.dynamic_parse(ctxt, &V::new(r.fields().to_vec()))
|
||||
.and_then(|fsw| merge(lw, fsw))
|
||||
})
|
||||
}),
|
||||
CompoundPattern::Tuple { patterns } => v.value().as_sequence().and_then(|vs| {
|
||||
if vs.len() == patterns.len() {
|
||||
patterns
|
||||
.iter()
|
||||
.zip(vs)
|
||||
.fold(Some(Map::new()), |acc, (p, v)| {
|
||||
acc.and_then(|acc| p.dynamic_parse(ctxt, v).and_then(|w| merge(acc, w)))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
CompoundPattern::TuplePrefix { fixed, variable } => {
|
||||
v.value().as_sequence().and_then(|vs| {
|
||||
if vs.len() >= fixed.len() {
|
||||
fixed
|
||||
.iter()
|
||||
.zip(vs)
|
||||
.fold(Some(Map::new()), |acc, (p, v)| {
|
||||
acc.and_then(|acc| {
|
||||
p.dynamic_parse(ctxt, v).and_then(|w| merge(acc, w))
|
||||
})
|
||||
})
|
||||
.and_then(|fixed_ws| {
|
||||
let remainder = V::new(vs[fixed.len()..].to_vec());
|
||||
(**variable).dynamic_parse(ctxt, &remainder).and_then(
|
||||
|variable_ws| merge(fixed_ws, variable_ws.unwrap_compound()),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
CompoundPattern::Dict { entries } => v.value().as_dictionary().and_then(|d| {
|
||||
(**entries).0.iter().fold(Some(Map::new()), |acc, (k, p)| {
|
||||
acc.and_then(|acc| {
|
||||
d.get(k).and_then(|v| {
|
||||
p.dynamic_parse(ctxt, v)
|
||||
.and_then(|w| merge(acc, w.unwrap_compound()))
|
||||
})
|
||||
})
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> Pattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
|
||||
match self {
|
||||
Pattern::SimplePattern(b) => (**b).dynamic_parse(ctxt, v),
|
||||
Pattern::CompoundPattern(b) => (**b).dynamic_parse(ctxt, v).map(DynField::Compound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> DynField<V> {
|
||||
fn finish(self) -> V {
|
||||
match self {
|
||||
DynField::Simple(v) => v,
|
||||
DynField::Compound(v) => V::new(v),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_map(self, key: Option<&str>) -> Map<V, V> {
|
||||
match self {
|
||||
DynField::Simple(v) => {
|
||||
let mut d = Map::new();
|
||||
if let Some(k) = key {
|
||||
d.insert(V::symbol(k), v);
|
||||
}
|
||||
d
|
||||
}
|
||||
DynField::Compound(d) => d,
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_compound(self) -> Map<V, V> {
|
||||
match self {
|
||||
DynField::Simple(_) => panic!("Cannot unwrap DynField::Simple to compound fields"),
|
||||
DynField::Compound(d) => d,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn merge<V: NestedValue>(a: Map<V, V>, b: Map<V, V>) -> Option<Map<V, V>> {
|
||||
merge2(V::new(a), V::new(b)).map(|d| {
|
||||
d.value_owned()
|
||||
.into_dictionary()
|
||||
.expect("merge to yield Dictionary")
|
||||
})
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
//! The runtime support library for compiled Schemas.
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Reexport lazy_static for generated code to use.
|
||||
pub use lazy_static::lazy_static;
|
||||
|
||||
pub use preserves;
|
||||
pub use preserves::value::boundary as B;
|
||||
pub use preserves::value::Reader;
|
||||
|
||||
pub mod interpret;
|
||||
|
||||
use preserves::value::ArcValue;
|
||||
use preserves::value::Domain;
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::NoEmbeddedDomainCodec;
|
||||
use preserves::value::Value;
|
||||
|
||||
use std::convert::From;
|
||||
use std::convert::Into;
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Every [language][crate::define_language] implements [NestedValueCodec] as a marker trait.
|
||||
pub trait NestedValueCodec {} // marker trait
|
||||
impl NestedValueCodec for () {}
|
||||
|
||||
/// Implementors of [Parse] can produce instances of themselves from a [Value], given a
|
||||
/// supporting [language][crate::define_language]. All Schema-compiler-produced types implement
|
||||
/// [Parse].
|
||||
pub trait Parse<L, Value: NestedValue>: Sized {
|
||||
/// Decode the given `value` (using auxiliary structure from the `language` instance) to
|
||||
/// produce an instance of [Self].
|
||||
fn parse(language: L, value: &Value) -> Result<Self, ParseError>;
|
||||
}
|
||||
|
||||
impl<'a, T: NestedValueCodec, Value: NestedValue> Parse<&'a T, Value> for Value {
|
||||
fn parse(_language: &'a T, value: &Value) -> Result<Self, ParseError> {
|
||||
Ok(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementors of [Unparse] can convert themselves into a [Value], given a supporting
|
||||
/// [language][crate::define_language]. All Schema-compiler-produced types implement [Unparse].
|
||||
pub trait Unparse<L, Value: NestedValue> {
|
||||
/// Encode `self` into a [Value] (using auxiliary structure from the `language` instance).
|
||||
fn unparse(&self, language: L) -> Value;
|
||||
}
|
||||
|
||||
impl<'a, T: NestedValueCodec, Value: NestedValue> Unparse<&'a T, Value> for Value {
|
||||
fn unparse(&self, _language: &'a T) -> Value {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Every [language][crate::define_language] implements [Codec], which supplies convenient
|
||||
/// shorthand for invoking [Parse::parse] and [Unparse::unparse].
|
||||
pub trait Codec<N: NestedValue> {
|
||||
/// Delegates to [`T::parse`][Parse::parse], using `self` as language and the given `value`
|
||||
/// as input.
|
||||
fn parse<'a, T: Parse<&'a Self, N>>(&'a self, value: &N) -> Result<T, ParseError>;
|
||||
/// Delegates to [`value.unparse`][Unparse::unparse], using `self` as language.
|
||||
fn unparse<'a, T: Unparse<&'a Self, N>>(&'a self, value: &T) -> N;
|
||||
}
|
||||
|
||||
impl<L, N: NestedValue> Codec<N> for L {
|
||||
fn parse<'a, T: Parse<&'a L, N>>(&'a self, value: &N) -> Result<T, ParseError> {
|
||||
T::parse(self, value)
|
||||
}
|
||||
|
||||
fn unparse<'a, T: Unparse<&'a L, N>>(&'a self, value: &T) -> N {
|
||||
value.unparse(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementors of [Deserialize] can produce instances of themselves from a [Value]. All
|
||||
/// Schema-compiler-produced types implement [Deserialize].
|
||||
///
|
||||
/// The difference between [Deserialize] and [Parse] is that implementors of [Deserialize] know
|
||||
/// which [language][crate::define_language] to use.
|
||||
pub trait Deserialize<N: NestedValue>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn deserialize<'de, R: Reader<'de, N>>(r: &mut R) -> Result<Self, ParseError>;
|
||||
}
|
||||
|
||||
/// Extracts a simple literal term from a byte array using
|
||||
/// [PackedReader][preserves::value::packed::PackedReader]. No embedded values are permitted.
|
||||
pub fn decode_lit<N: NestedValue>(bs: &[u8]) -> io::Result<N> {
|
||||
preserves::value::packed::from_bytes(bs, NoEmbeddedDomainCodec)
|
||||
}
|
||||
|
||||
/// When `D` can parse itself from an [IOValue], this function parses all embedded [IOValue]s
|
||||
/// into `D`s.
|
||||
pub fn decode_embedded<D: Domain>(v: &IOValue) -> Result<ArcValue<Arc<D>>, ParseError>
|
||||
where
|
||||
for<'a> D: TryFrom<&'a IOValue, Error = ParseError>,
|
||||
{
|
||||
v.copy_via(&mut |d| Ok(Value::Embedded(Arc::new(D::try_from(d)?))))
|
||||
}
|
||||
|
||||
/// When `D` can unparse itself into an [IOValue], this function converts all embedded `D`s
|
||||
/// into [IOValue]s.
|
||||
pub fn encode_embedded<D: Domain>(v: &ArcValue<Arc<D>>) -> IOValue
|
||||
where
|
||||
for<'a> IOValue: From<&'a D>,
|
||||
{
|
||||
v.copy_via::<_, _, std::convert::Infallible>(&mut |d| Ok(Value::Embedded(IOValue::from(d))))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Error value yielded when parsing of an [IOValue] into a Schema-compiler-produced type.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ParseError {
|
||||
/// Signalled when the input does not match the Preserves Schema associated with the type.
|
||||
#[error("Input not conformant with Schema: {0}")]
|
||||
ConformanceError(&'static str),
|
||||
/// Signalled when the underlying Preserves library signals an error.
|
||||
#[error(transparent)]
|
||||
Preserves(preserves::error::Error),
|
||||
}
|
||||
|
||||
impl From<preserves::error::Error> for ParseError {
|
||||
fn from(v: preserves::error::Error) -> Self {
|
||||
match v {
|
||||
preserves::error::Error::Expected(_, _) => {
|
||||
ParseError::ConformanceError("preserves::error::Error::Expected")
|
||||
}
|
||||
_ => ParseError::Preserves(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for ParseError {
|
||||
fn from(v: io::Error) -> Self {
|
||||
preserves::error::Error::from(v).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for io::Error {
|
||||
fn from(v: ParseError) -> Self {
|
||||
match v {
|
||||
ParseError::ConformanceError(_) => io::Error::new(io::ErrorKind::InvalidData, v),
|
||||
ParseError::Preserves(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
/// Constructs a [ParseError::ConformanceError].
|
||||
pub fn conformance_error(context: &'static str) -> Self {
|
||||
ParseError::ConformanceError(context)
|
||||
}
|
||||
|
||||
/// True iff `self` is a [ParseError::ConformanceError].
|
||||
pub fn is_conformance_error(&self) -> bool {
|
||||
return if let ParseError::ConformanceError(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,538 +0,0 @@
|
|||
//! A library for emitting pretty-formatted structured source code.
|
||||
//!
|
||||
//! The main entry points are [Formatter::to_string] and [Formatter::write], plus the utilities
|
||||
//! in the [macros] submodule.
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::str;
|
||||
|
||||
/// Default width for pretty-formatting, in columns.
|
||||
pub const DEFAULT_WIDTH: usize = 80;
|
||||
|
||||
/// All pretty-formattable items must implement this trait.
|
||||
pub trait Emittable: std::fmt::Debug {
|
||||
/// Serializes `self`, as pretty-printed code, on `f`.
|
||||
fn write_on(&self, f: &mut Formatter);
|
||||
}
|
||||
|
||||
/// Tailoring of behaviour for [Vertical] groupings.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum VerticalMode {
|
||||
Variable,
|
||||
Normal,
|
||||
ExtraNewline,
|
||||
}
|
||||
|
||||
/// Vertical formatting for [Emittable]s.
|
||||
pub trait Vertical {
|
||||
fn set_vertical_mode(&mut self, mode: VerticalMode);
|
||||
fn write_vertically_on(&self, f: &mut Formatter);
|
||||
}
|
||||
|
||||
/// Polymorphic [Emittable], used consistently in the API.
|
||||
pub type Item = std::rc::Rc<dyn Emittable>;
|
||||
|
||||
/// A possibly-vertical sequence of items with item-separating and -terminating text.
|
||||
#[derive(Clone)]
|
||||
pub struct Sequence {
|
||||
pub items: Vec<Item>,
|
||||
pub vertical_mode: VerticalMode,
|
||||
pub separator: &'static str,
|
||||
pub terminator: &'static str,
|
||||
}
|
||||
|
||||
/// A sequence of items, indented when formatted vertically, surrounded by opening and closing
|
||||
/// text.
|
||||
#[derive(Clone)]
|
||||
pub struct Grouping {
|
||||
pub sequence: Sequence,
|
||||
pub open: &'static str,
|
||||
pub close: &'static str,
|
||||
}
|
||||
|
||||
/// State needed for pretty-formatting of [Emittable]s.
|
||||
pub struct Formatter {
|
||||
/// Number of available columns. Used to decide between horizontal and vertical layouts.
|
||||
pub width: usize,
|
||||
indent_delta: String,
|
||||
current_indent: String,
|
||||
/// Mutable output buffer. Accumulates emitted text during writing.
|
||||
pub buffer: String,
|
||||
}
|
||||
|
||||
impl Formatter {
|
||||
/// Construct a Formatter using [DEFAULT_WIDTH] and a four-space indent.
|
||||
pub fn new() -> Self {
|
||||
Formatter {
|
||||
width: DEFAULT_WIDTH,
|
||||
indent_delta: " ".to_owned(),
|
||||
current_indent: "\n".to_owned(),
|
||||
buffer: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a Formatter just like `self` but with an empty `buffer`.
|
||||
pub fn copy_empty(&self) -> Formatter {
|
||||
Formatter {
|
||||
width: self.width,
|
||||
indent_delta: self.indent_delta.clone(),
|
||||
current_indent: self.current_indent.clone(),
|
||||
buffer: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Yields the indent size.
|
||||
pub fn indent_size(self) -> usize {
|
||||
self.indent_delta.len()
|
||||
}
|
||||
|
||||
/// Updates the indent size.
|
||||
pub fn set_indent_size(&mut self, n: usize) {
|
||||
self.indent_delta = str::repeat(" ", n)
|
||||
}
|
||||
|
||||
/// Accumulates a text serialization of `e` in `buffer`.
|
||||
pub fn write<E: Emittable>(&mut self, e: E) {
|
||||
e.write_on(self)
|
||||
}
|
||||
|
||||
/// Emits a newline followed by indentation into `buffer`.
|
||||
pub fn newline(&mut self) {
|
||||
self.buffer.push_str(&self.current_indent)
|
||||
}
|
||||
|
||||
/// Creates a default Formatter, uses it to [write][Formatter::write] `e`, and yields the
|
||||
/// contents of its `buffer`.
|
||||
pub fn to_string<E: Emittable>(e: E) -> String {
|
||||
let mut f = Formatter::new();
|
||||
f.write(e);
|
||||
f.buffer
|
||||
}
|
||||
|
||||
/// Calls `f` in a context where the indentation has been increased by
|
||||
/// [Formatter::indent_size] spaces. Restores the indentation level after `f` returns.
|
||||
/// Yields the result of the call to `f`.
|
||||
pub fn with_indent<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
|
||||
let old_indent = self.current_indent.clone();
|
||||
self.current_indent += &self.indent_delta;
|
||||
let r = f(self);
|
||||
self.current_indent = old_indent;
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Formatter {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VerticalMode {
|
||||
fn default() -> Self {
|
||||
Self::Variable
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
impl Emittable for &str {
|
||||
fn write_on(&self, f: &mut Formatter) {
|
||||
f.buffer.push_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Emittable for String {
|
||||
fn write_on(&self, f: &mut Formatter) {
|
||||
f.write(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Emittable> Emittable for &'a Vec<E>
|
||||
where
|
||||
&'a E: Emittable,
|
||||
{
|
||||
fn write_on(&self, f: &mut Formatter) {
|
||||
for e in self.iter() {
|
||||
f.write(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Emittable for Sequence {
|
||||
fn write_on(&self, f: &mut Formatter) {
|
||||
if self.vertical_mode != VerticalMode::Variable {
|
||||
self.write_vertically_on(f)
|
||||
} else {
|
||||
let mut need_sep = false;
|
||||
for e in self.items.iter() {
|
||||
if need_sep {
|
||||
self.separator.write_on(f)
|
||||
} else {
|
||||
need_sep = true
|
||||
}
|
||||
e.write_on(f)
|
||||
}
|
||||
if !self.items.is_empty() {
|
||||
self.terminator.write_on(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Vertical for Sequence {
|
||||
fn set_vertical_mode(&mut self, vertical_mode: VerticalMode) {
|
||||
self.vertical_mode = vertical_mode;
|
||||
}
|
||||
|
||||
fn write_vertically_on(&self, f: &mut Formatter) {
|
||||
let mut i = self.items.len();
|
||||
let mut first = true;
|
||||
for e in self.items.iter() {
|
||||
if !first {
|
||||
if self.vertical_mode == VerticalMode::ExtraNewline {
|
||||
f.write("\n");
|
||||
}
|
||||
f.newline();
|
||||
}
|
||||
first = false;
|
||||
e.write_on(f);
|
||||
let delim = if i == 1 {
|
||||
self.terminator
|
||||
} else {
|
||||
self.separator
|
||||
};
|
||||
delim
|
||||
.trim_end_matches(|c: char| c.is_whitespace() && c != '\n')
|
||||
.write_on(f);
|
||||
i = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Emittable for Grouping {
|
||||
fn write_on(&self, f: &mut Formatter) {
|
||||
if self.sequence.vertical_mode != VerticalMode::Variable {
|
||||
self.write_vertically_on(f)
|
||||
} else {
|
||||
let mut g = f.copy_empty();
|
||||
self.open.write_on(&mut g);
|
||||
g.write(&self.sequence);
|
||||
self.close.write_on(&mut g);
|
||||
let s = g.buffer;
|
||||
if s.len() <= f.width {
|
||||
f.write(&s)
|
||||
} else {
|
||||
self.write_vertically_on(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Vertical for Grouping {
|
||||
fn set_vertical_mode(&mut self, vertical_mode: VerticalMode) {
|
||||
self.sequence.set_vertical_mode(vertical_mode);
|
||||
}
|
||||
|
||||
fn write_vertically_on(&self, f: &mut Formatter) {
|
||||
self.open.write_on(f);
|
||||
if !self.sequence.items.is_empty() {
|
||||
f.with_indent(|f| {
|
||||
f.newline();
|
||||
self.sequence.write_vertically_on(f)
|
||||
});
|
||||
f.newline()
|
||||
}
|
||||
self.close.write_on(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Emittable> Emittable for &'a E {
|
||||
fn write_on(&self, f: &mut Formatter) {
|
||||
(*self).write_on(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Emittable for Item {
|
||||
fn write_on(&self, f: &mut Formatter) {
|
||||
(**self).write_on(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Sequence {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
f.write_str(&Formatter::to_string(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Grouping {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
f.write_str(&Formatter::to_string(self))
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/// Escapes `s` by substituting `\\` for `\`, `\"` for `"`, and `\u{...}` for characters
|
||||
/// outside the range 32..126, inclusive.
|
||||
///
|
||||
/// This process is intended to generate literals compatible with `rustc`; see [the language
|
||||
/// reference on "Character and string
|
||||
/// literals"](https://doc.rust-lang.org/reference/tokens.html#character-and-string-literals).
|
||||
pub fn escape_string(s: &str) -> String {
|
||||
let mut buf = String::new();
|
||||
buf.push('"');
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'\\' => buf.push_str("\\\\"),
|
||||
'"' => buf.push_str("\\\""),
|
||||
_ if c >= ' ' && c <= '~' => buf.push(c),
|
||||
_ => write!(&mut buf, "\\u{{{:x}}}", c as i32).expect("no IO errors building a string"),
|
||||
}
|
||||
}
|
||||
buf.push('"');
|
||||
buf
|
||||
}
|
||||
|
||||
/// Escapes `bs` into a Rust byte string literal, treating each byte as its ASCII equivalent
|
||||
/// except producing `\\` for 0x5c, `\"` for 0x22, and `\x..` for bytes outside the range
|
||||
/// 0x20..0x7e, inclusive.
|
||||
///
|
||||
/// This process is intended to generate literals compatible with `rustc`; see [the language
|
||||
/// reference on "Byte string
|
||||
/// literals"](https://doc.rust-lang.org/reference/tokens.html#byte-string-literals).
|
||||
pub fn escape_bytes(bs: &[u8]) -> String {
|
||||
let mut buf = String::new();
|
||||
buf.push_str("b\"");
|
||||
for b in bs {
|
||||
let c = *b as char;
|
||||
match c {
|
||||
'\\' => buf.push_str("\\\\"),
|
||||
'"' => buf.push_str("\\\""),
|
||||
_ if c >= ' ' && c <= '~' => buf.push(c),
|
||||
_ => write!(&mut buf, "\\x{:02x}", b).expect("no IO errors building a string"),
|
||||
}
|
||||
}
|
||||
buf.push('"');
|
||||
buf
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/// Utilities for constructing many useful kinds of [Sequence] and [Grouping].
|
||||
pub mod constructors {
|
||||
use super::Emittable;
|
||||
use super::Grouping;
|
||||
use super::Item;
|
||||
use super::Sequence;
|
||||
use super::Vertical;
|
||||
use super::VerticalMode;
|
||||
|
||||
/// Produces a polymorphic, reference-counted [Item] from some generic [Emittable].
|
||||
pub fn item<E: 'static + Emittable>(i: E) -> Item {
|
||||
std::rc::Rc::new(i)
|
||||
}
|
||||
|
||||
/// *a*`::`*b*`::`*...*`::`*z*
|
||||
pub fn name(pieces: Vec<Item>) -> Sequence {
|
||||
Sequence {
|
||||
items: pieces,
|
||||
vertical_mode: VerticalMode::default(),
|
||||
separator: "::",
|
||||
terminator: "",
|
||||
}
|
||||
}
|
||||
|
||||
/// *ab...z* (directly adjacent, no separators or terminators)
|
||||
pub fn seq(items: Vec<Item>) -> Sequence {
|
||||
Sequence {
|
||||
items: items,
|
||||
vertical_mode: VerticalMode::default(),
|
||||
separator: "",
|
||||
terminator: "",
|
||||
}
|
||||
}
|
||||
|
||||
/// *a*`, `*b*`, `*...*`, `*z*
|
||||
pub fn commas(items: Vec<Item>) -> Sequence {
|
||||
Sequence {
|
||||
items: items,
|
||||
vertical_mode: VerticalMode::default(),
|
||||
separator: ", ",
|
||||
terminator: "",
|
||||
}
|
||||
}
|
||||
|
||||
/// `(`*a*`, `*b*`, `*...*`, `*z*`)`
|
||||
pub fn parens(items: Vec<Item>) -> Grouping {
|
||||
Grouping {
|
||||
sequence: commas(items),
|
||||
open: "(",
|
||||
close: ")",
|
||||
}
|
||||
}
|
||||
|
||||
/// `[`*a*`, `*b*`, `*...*`, `*z*`]`
|
||||
pub fn brackets(items: Vec<Item>) -> Grouping {
|
||||
Grouping {
|
||||
sequence: commas(items),
|
||||
open: "[",
|
||||
close: "]",
|
||||
}
|
||||
}
|
||||
|
||||
/// `<`*a*`, `*b*`, `*...*`, `*z*`>`
|
||||
pub fn anglebrackets(items: Vec<Item>) -> Grouping {
|
||||
Grouping {
|
||||
sequence: commas(items),
|
||||
open: "<",
|
||||
close: ">",
|
||||
}
|
||||
}
|
||||
|
||||
/// `{`*a*`, `*b*`, `*...*`, `*z*`}`
|
||||
pub fn braces(items: Vec<Item>) -> Grouping {
|
||||
Grouping {
|
||||
sequence: commas(items),
|
||||
open: "{",
|
||||
close: "}",
|
||||
}
|
||||
}
|
||||
|
||||
/// `{`*a*` `*b*` `*...*` `*z*`}`
|
||||
pub fn block(items: Vec<Item>) -> Grouping {
|
||||
Grouping {
|
||||
sequence: Sequence {
|
||||
items: items,
|
||||
vertical_mode: VerticalMode::default(),
|
||||
separator: " ",
|
||||
terminator: "",
|
||||
},
|
||||
open: "{",
|
||||
close: "}",
|
||||
}
|
||||
}
|
||||
|
||||
/// As [block], but always vertical
|
||||
pub fn codeblock(items: Vec<Item>) -> Grouping {
|
||||
vertical(false, block(items))
|
||||
}
|
||||
|
||||
/// `{`*a*`; `*b*`; `*...*`; `*z*`}`
|
||||
pub fn semiblock(items: Vec<Item>) -> Grouping {
|
||||
Grouping {
|
||||
sequence: Sequence {
|
||||
items: items,
|
||||
vertical_mode: VerticalMode::default(),
|
||||
separator: "; ",
|
||||
terminator: "",
|
||||
},
|
||||
open: "{",
|
||||
close: "}",
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides `v` to be always vertical.
|
||||
///
|
||||
/// If `spaced` is true, inserts an extra newline between items.
|
||||
pub fn vertical<V: Vertical>(spaced: bool, mut v: V) -> V {
|
||||
v.set_vertical_mode(if spaced {
|
||||
VerticalMode::ExtraNewline
|
||||
} else {
|
||||
VerticalMode::Normal
|
||||
});
|
||||
v
|
||||
}
|
||||
|
||||
/// Adds a layer of indentation to the given [Sequence].
|
||||
pub fn indented(sequence: Sequence) -> Grouping {
|
||||
Grouping {
|
||||
sequence,
|
||||
open: "",
|
||||
close: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ergonomic syntax for using the constructors in submodule [constructors]; see the
|
||||
/// documentation for the macros, which appears on the [page for the crate
|
||||
/// itself][crate#macros].
|
||||
pub mod macros {
|
||||
/// `name!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ *a*`::`*b*`::`*...*`::`*z*
|
||||
///
|
||||
/// See [super::constructors::name].
|
||||
#[macro_export]
|
||||
macro_rules! name {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::name(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `seq!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ *ab...z*
|
||||
///
|
||||
/// See [super::constructors::seq].
|
||||
#[macro_export]
|
||||
macro_rules! seq {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::seq(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `commas!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ *a*`, `*b*`, `*...*`, `*z*
|
||||
///
|
||||
/// See [super::constructors::commas].
|
||||
#[macro_export]
|
||||
macro_rules! commas {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::commas(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `parens!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `(`*a*`, `*b*`, `*...*`, `*z*`)`
|
||||
///
|
||||
/// See [super::constructors::parens].
|
||||
#[macro_export]
|
||||
macro_rules! parens {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::parens(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `brackets!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `[`*a*`, `*b*`, `*...*`, `*z*`]`
|
||||
///
|
||||
/// See [super::constructors::brackets].
|
||||
#[macro_export]
|
||||
macro_rules! brackets {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::brackets(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `anglebrackets!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `<`*a*`, `*b*`, `*...*`, `*z*`>`
|
||||
///
|
||||
/// See [super::constructors::anglebrackets].
|
||||
#[macro_export]
|
||||
macro_rules! anglebrackets {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::anglebrackets(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `braces!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `{`*a*`, `*b*`, `*...*`, `*z*`}`
|
||||
///
|
||||
/// See [super::constructors::braces].
|
||||
#[macro_export]
|
||||
macro_rules! braces {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::braces(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `block!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `{`*a*` `*b*` `*...*` `*z*`}`
|
||||
///
|
||||
/// See [super::constructors::block].
|
||||
#[macro_export]
|
||||
macro_rules! block {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::block(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// As [`block`]`!`, but always vertical. See
|
||||
/// [constructors::codeblock][super::constructors::codeblock].
|
||||
#[macro_export]
|
||||
macro_rules! codeblock {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::codeblock(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
|
||||
/// `semiblock!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `{`*a*`; `*b*`; `*...*`; `*z*`}`
|
||||
///
|
||||
/// See [super::constructors::semiblock].
|
||||
#[macro_export]
|
||||
macro_rules! semiblock {
|
||||
($($item:expr),*) => {$crate::syntax::block::constructors::semiblock(vec![$(std::rc::Rc::new($item)),*])}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
//! A library for emitting pretty-formatted structured source code.
|
||||
|
||||
pub mod block;
|
|
@ -1,21 +0,0 @@
|
|||
[package]
|
||||
name = "preserves-tools"
|
||||
version = "4.993.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Command-line utilities for working with Preserves documents."
|
||||
homepage = "https://preserves.dev/"
|
||||
repository = "https://gitlab.com/preserves/preserves"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
preserves = { path = "../preserves", version = "4.993.0"}
|
||||
preserves-path = { path = "../preserves-path", version = "5.993.0"}
|
||||
preserves-schema = { path = "../preserves-schema", version = "5.993.0"}
|
||||
|
||||
bytes = "1.0"
|
||||
clap = { version = "3", features = ["derive"] }
|
||||
clap_complete = "3"
|
||||
|
||||
[package.metadata.workspaces]
|
||||
independent = true
|
|
@ -1,548 +0,0 @@
|
|||
use bytes::Buf;
|
||||
use bytes::BufMut;
|
||||
use bytes::BytesMut;
|
||||
|
||||
use clap::{value_parser, ArgEnum, Command, IntoApp, Parser};
|
||||
use clap_complete::{generate, Generator, Shell};
|
||||
|
||||
use preserves::value::IOBinarySource;
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::IOValueDomainCodec;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::PackedReader;
|
||||
use preserves::value::PackedWriter;
|
||||
use preserves::value::Reader;
|
||||
use preserves::value::Set;
|
||||
use preserves::value::TextReader;
|
||||
use preserves::value::TextWriter;
|
||||
use preserves::value::Value;
|
||||
use preserves::value::ViaCodec;
|
||||
use preserves::value::Writer;
|
||||
|
||||
use preserves::value::text::writer::CommaStyle;
|
||||
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
// #[derive(ArgEnum, Clone, Debug)]
|
||||
// enum Encoding {
|
||||
// None,
|
||||
// Base64,
|
||||
// Hex,
|
||||
// }
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
enum InputFormat {
|
||||
AutoDetect,
|
||||
Text,
|
||||
Binary,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
enum OutputFormat {
|
||||
Text,
|
||||
Binary,
|
||||
Unquoted,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
enum CommasFormat {
|
||||
None,
|
||||
Separating,
|
||||
Terminating,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Copy, Debug)]
|
||||
enum Boolish {
|
||||
#[clap(alias = "no", alias = "n", alias = "off", alias = "0", alias = "false")]
|
||||
Disabled,
|
||||
#[clap(alias = "yes", alias = "y", alias = "on", alias = "1", alias = "true")]
|
||||
Enabled,
|
||||
}
|
||||
|
||||
impl From<Boolish> for bool {
|
||||
fn from(b: Boolish) -> Self {
|
||||
matches!(b, Boolish::Enabled)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommasFormat> for CommaStyle {
|
||||
fn from(commas: CommasFormat) -> Self {
|
||||
match commas {
|
||||
CommasFormat::None => CommaStyle::None,
|
||||
CommasFormat::Separating => CommaStyle::Separating,
|
||||
CommasFormat::Terminating => CommaStyle::Terminating,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
enum SelectOutput {
|
||||
Sequence,
|
||||
Set,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
struct Convert {
|
||||
// #[clap(long, arg_enum, default_value = "none")]
|
||||
// input_encoding: Encoding,
|
||||
|
||||
// #[clap(long, arg_enum, default_value = "none")]
|
||||
// output_encoding: Encoding,
|
||||
#[clap(long, short, arg_enum, default_value = "auto-detect")]
|
||||
input_format: InputFormat,
|
||||
|
||||
#[clap(long, short, arg_enum, default_value = "text")]
|
||||
output_format: OutputFormat,
|
||||
|
||||
#[clap(long)]
|
||||
escape_spaces: bool,
|
||||
|
||||
#[clap(long, short, arg_enum, default_value = "none")]
|
||||
commas: CommasFormat,
|
||||
|
||||
#[clap(long)]
|
||||
limit: Option<usize>,
|
||||
|
||||
#[clap(long, arg_enum, value_name = "on/off", default_value = "on")]
|
||||
indent: Boolish,
|
||||
|
||||
#[clap(long = "select", default_value = "*")]
|
||||
select_expr: String,
|
||||
|
||||
#[clap(long, arg_enum, default_value = "sequence")]
|
||||
select_output: SelectOutput,
|
||||
|
||||
#[clap(long)]
|
||||
collect: bool,
|
||||
|
||||
#[clap(long, arg_enum, value_name = "on/off", default_value = "on")]
|
||||
read_annotations: Boolish,
|
||||
|
||||
#[clap(long, arg_enum, value_name = "on/off", default_value = "on")]
|
||||
write_annotations: Boolish,
|
||||
|
||||
#[clap(long, value_name = "filename")]
|
||||
bundle: Vec<std::path::PathBuf>,
|
||||
// #[clap(long)]
|
||||
// schema: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
enum StringInputTerminator {
|
||||
EOF,
|
||||
Newline,
|
||||
Nul,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
struct StringQuotation {
|
||||
#[clap(long, arg_enum, default_value = "eof")]
|
||||
input_terminator: StringInputTerminator,
|
||||
|
||||
#[clap(long)]
|
||||
include_terminator: bool,
|
||||
|
||||
#[clap(long)]
|
||||
escape_spaces: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
enum QuotationOutput {
|
||||
String(StringQuotation),
|
||||
ByteString,
|
||||
Symbol(StringQuotation),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
struct Quote {
|
||||
#[clap(long, short, arg_enum, default_value = "text")]
|
||||
output_format: OutputFormat,
|
||||
|
||||
#[clap(subcommand)]
|
||||
output: QuotationOutput,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
enum Subcommand {
|
||||
Completions {
|
||||
#[clap(value_parser=value_parser!(Shell))]
|
||||
shell: Shell,
|
||||
},
|
||||
Convert(Convert),
|
||||
Quote(Quote),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
#[clap(name = "preserves-tool")]
|
||||
#[clap(version)]
|
||||
#[clap(args_conflicts_with_subcommands = true)]
|
||||
/// Swiss-army knife tool for working with Preserves data.
|
||||
/// See https://preserves.dev/.
|
||||
/// If no subcommand is specified, the default subcommand will be `convert`.
|
||||
struct CommandLine {
|
||||
#[clap(subcommand)]
|
||||
command: Option<Subcommand>,
|
||||
|
||||
#[clap(flatten, next_help_heading="OPTIONS FOR DEFAULT SUBCOMMAND convert")]
|
||||
convert: Convert,
|
||||
}
|
||||
|
||||
fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
|
||||
generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let args = CommandLine::parse();
|
||||
Ok(match args.command {
|
||||
Some(subcommand) => match subcommand {
|
||||
Subcommand::Completions { shell } => {
|
||||
let mut cmd = CommandLine::into_app();
|
||||
print_completions(shell, &mut cmd);
|
||||
}
|
||||
Subcommand::Convert(c) => convert(c)?,
|
||||
Subcommand::Quote(q) => quote(q)?,
|
||||
}
|
||||
None => convert(args.convert)?,
|
||||
})
|
||||
}
|
||||
|
||||
struct RollingBuffer<R: io::Read> {
|
||||
r: R,
|
||||
discarded: usize,
|
||||
pos: usize,
|
||||
buf: BytesMut,
|
||||
}
|
||||
|
||||
impl<R: io::Read> RollingBuffer<R> {
|
||||
fn new(r: R) -> Self {
|
||||
RollingBuffer {
|
||||
r,
|
||||
discarded: 0,
|
||||
pos: 0,
|
||||
buf: BytesMut::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_more(&mut self) -> io::Result<usize> {
|
||||
let mut buf = [0; 8192];
|
||||
let n = self.r.read(&mut buf)?;
|
||||
self.buf.put(&buf[..n]);
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn peek_buf(&mut self) -> io::Result<&[u8]> {
|
||||
if self.rhs() == self.pos {
|
||||
let _ = self.read_more()?;
|
||||
}
|
||||
return Ok(&self.buf[self.pos - self.discarded..]);
|
||||
}
|
||||
|
||||
fn rhs(&self) -> usize {
|
||||
self.buf.remaining() + self.discarded
|
||||
}
|
||||
|
||||
fn discard(&mut self, count: usize) {
|
||||
self.buf.advance(count);
|
||||
self.discarded += count;
|
||||
}
|
||||
|
||||
fn discard_to_pos(&mut self) {
|
||||
self.discard(self.pos - self.discarded)
|
||||
}
|
||||
|
||||
fn read_upto(&mut self, delimiter: u8, inclusive: bool) -> io::Result<Option<Vec<u8>>> {
|
||||
let mut result = Vec::new();
|
||||
let mut buf = [0; 1];
|
||||
while self.read(&mut buf)? == 1 {
|
||||
if buf[0] == delimiter {
|
||||
if inclusive {
|
||||
result.push(delimiter);
|
||||
}
|
||||
return Ok(Some(result));
|
||||
}
|
||||
result.push(buf[0]);
|
||||
}
|
||||
Ok(if result.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> io::Seek for RollingBuffer<R> {
|
||||
fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
|
||||
let new_position = match offset {
|
||||
io::SeekFrom::Current(delta) => {
|
||||
if delta >= 0 {
|
||||
self.pos + delta as usize
|
||||
} else {
|
||||
self.pos - (-delta) as usize
|
||||
}
|
||||
}
|
||||
io::SeekFrom::End(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"Cannot seek wrt end on open-ended stream",
|
||||
))?,
|
||||
io::SeekFrom::Start(new_position) => new_position as usize,
|
||||
};
|
||||
if new_position > self.rhs() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Attempt to seek beyond end of buffer",
|
||||
))?;
|
||||
}
|
||||
if new_position < self.discarded {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Attempt to seek before start of buffer",
|
||||
))?;
|
||||
}
|
||||
self.pos = new_position;
|
||||
Ok(new_position as u64)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Ok(self.pos as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> io::Read for RollingBuffer<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let i = self.pos - self.discarded;
|
||||
loop {
|
||||
let n = std::cmp::min(self.buf.remaining() - i, buf.len());
|
||||
if n == 0 {
|
||||
if self.read_more()? == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let _ = &buf[..n].copy_from_slice(&self.buf[i..i + n]);
|
||||
self.pos += n;
|
||||
return Ok(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: extract this and RollingBuffer to some preserves utils module
|
||||
pub struct ValueStream<R: io::Read> {
|
||||
input_format: InputFormat,
|
||||
read_annotations: bool,
|
||||
source: IOBinarySource<RollingBuffer<R>>,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<R: io::Read> ValueStream<R> {
|
||||
fn new(input_format: InputFormat, read_annotations: bool, r: R) -> Self {
|
||||
ValueStream {
|
||||
input_format,
|
||||
read_annotations,
|
||||
source: IOBinarySource::new(RollingBuffer::new(r)),
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self) -> io::Result<Option<IOValue>> {
|
||||
let is_text = {
|
||||
let peek_buf = self.source.read.peek_buf()?;
|
||||
if peek_buf.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
peek_buf[0] < 128
|
||||
};
|
||||
|
||||
let maybe_value: Option<IOValue> = if is_text {
|
||||
match self.input_format {
|
||||
InputFormat::AutoDetect | InputFormat::Text => (),
|
||||
InputFormat::Binary => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Expected binary input, saw text input",
|
||||
))
|
||||
}
|
||||
}
|
||||
TextReader::new(&mut self.source, ViaCodec::new(IOValueDomainCodec))
|
||||
.next(self.read_annotations)?
|
||||
} else {
|
||||
match self.input_format {
|
||||
InputFormat::AutoDetect | InputFormat::Binary => (),
|
||||
InputFormat::Text => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Expected text input, saw binary input",
|
||||
))
|
||||
}
|
||||
}
|
||||
PackedReader::new(&mut self.source, IOValueDomainCodec).next(self.read_annotations)?
|
||||
};
|
||||
|
||||
match maybe_value {
|
||||
None => return Ok(None),
|
||||
Some(value) => {
|
||||
self.source.read.discard_to_pos();
|
||||
self.count += 1;
|
||||
Ok(Some(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> std::iter::Iterator for ValueStream<R> {
|
||||
type Item = io::Result<IOValue>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.read().transpose()
|
||||
}
|
||||
}
|
||||
|
||||
fn print_unquoted(v: &IOValue) {
|
||||
match v.value() {
|
||||
Value::String(s) => println!("{}", &s),
|
||||
Value::Symbol(s) => println!("{}", &s),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert(c: Convert) -> io::Result<()> {
|
||||
let mut env = preserves_path::Env::new();
|
||||
for f in c.bundle.iter() {
|
||||
env.load_bundle(f)?;
|
||||
}
|
||||
let select = preserves_path::Node::from_str(&env, &c.select_expr).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Invalid select expression: {}: {:?}", e, c.select_expr),
|
||||
)
|
||||
})?;
|
||||
let mut vs = ValueStream::new(c.input_format, c.read_annotations.into(), io::stdin());
|
||||
let write_ann: bool = c.write_annotations.into();
|
||||
let mut w: Box<dyn FnMut(&IOValue) -> io::Result<()>> = match c.output_format {
|
||||
OutputFormat::Text => {
|
||||
let mut t = TextWriter::new(io::stdout())
|
||||
.set_escape_spaces(c.escape_spaces)
|
||||
.set_comma_style(c.commas.into());
|
||||
if c.indent.into() {
|
||||
t.indentation = 2;
|
||||
}
|
||||
Box::new(move |v| {
|
||||
if write_ann {
|
||||
t.write(&mut IOValueDomainCodec, v)?;
|
||||
} else {
|
||||
t.write(&mut IOValueDomainCodec, &v.strip_annotations::<IOValue>())?;
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
OutputFormat::Binary => {
|
||||
let mut p = PackedWriter::new(io::stdout());
|
||||
Box::new(move |v| {
|
||||
if write_ann {
|
||||
p.write(&mut IOValueDomainCodec, v)?;
|
||||
} else {
|
||||
p.write(&mut IOValueDomainCodec, &v.strip_annotations::<IOValue>())?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
OutputFormat::Unquoted => Box::new(|v| {
|
||||
print_unquoted(v);
|
||||
Ok(())
|
||||
}),
|
||||
};
|
||||
while let Some(value) = vs.next() {
|
||||
let value = value?;
|
||||
let matches = select.exec(&mut env.to_context(), &value);
|
||||
if c.collect {
|
||||
match c.select_output {
|
||||
SelectOutput::Sequence => w(&IOValue::new(matches))?,
|
||||
SelectOutput::Set => w(&IOValue::new(Set::from_iter(matches)))?,
|
||||
}
|
||||
} else {
|
||||
match c.select_output {
|
||||
SelectOutput::Sequence => {
|
||||
for v in matches {
|
||||
w(&v)?;
|
||||
}
|
||||
}
|
||||
SelectOutput::Set => {
|
||||
for v in Set::from_iter(matches) {
|
||||
w(&v)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(limit) = c.limit {
|
||||
if vs.count >= limit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Quote {
|
||||
fn escape_spaces(&self) -> bool {
|
||||
match &self.output {
|
||||
QuotationOutput::ByteString => false,
|
||||
QuotationOutput::String(s) | QuotationOutput::Symbol(s) => s.escape_spaces,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn output_one(q: &Quote, v: &IOValue) -> io::Result<()> {
|
||||
match q.output_format {
|
||||
OutputFormat::Binary => PackedWriter::new(io::stdout()).write(&mut IOValueDomainCodec, v),
|
||||
OutputFormat::Text => {
|
||||
TextWriter::new(io::stdout())
|
||||
.set_escape_spaces(q.escape_spaces())
|
||||
.write(&mut IOValueDomainCodec, v)?;
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
OutputFormat::Unquoted => {
|
||||
print_unquoted(v);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn quote(q: Quote) -> io::Result<()> {
|
||||
match &q.output {
|
||||
QuotationOutput::ByteString => {
|
||||
let mut buf = Vec::new();
|
||||
io::stdin().read_to_end(&mut buf)?;
|
||||
output_one(&q, &IOValue::new(&buf[..]))
|
||||
}
|
||||
QuotationOutput::String(s) | QuotationOutput::Symbol(s) => match s.input_terminator {
|
||||
StringInputTerminator::EOF => {
|
||||
let mut buf = String::new();
|
||||
io::stdin().read_to_string(&mut buf)?;
|
||||
quote_chunk(&q, buf)
|
||||
}
|
||||
StringInputTerminator::Newline => quote_terminated_strings(b'\n', &q, s),
|
||||
StringInputTerminator::Nul => quote_terminated_strings(b'\0', &q, s),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn quote_chunk(q: &Quote, buf: String) -> io::Result<()> {
|
||||
match q.output {
|
||||
QuotationOutput::Symbol(_) => output_one(q, &IOValue::symbol(&buf)),
|
||||
_ => output_one(q, &IOValue::new(buf)),
|
||||
}
|
||||
}
|
||||
|
||||
fn quote_terminated_strings(delimiter: u8, q: &Quote, s: &StringQuotation) -> io::Result<()> {
|
||||
let mut r = RollingBuffer::new(io::stdin());
|
||||
while let Some(chunk) = r.read_upto(delimiter, s.include_terminator)? {
|
||||
quote_chunk(
|
||||
q,
|
||||
String::from_utf8(chunk)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8"))?,
|
||||
)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
[package]
|
||||
name = "preserves"
|
||||
version = "4.993.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Implementation of the Preserves serialization format via serde."
|
||||
homepage = "https://preserves.dev/"
|
||||
repository = "https://gitlab.com/preserves/preserves"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[badges]
|
||||
gitlab = { repository = "preserves/preserves" }
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
dtoa = "0.4"
|
||||
num = "0.4"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_bytes = "0.11"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "codec"
|
||||
harness = false
|
||||
|
||||
[package.metadata.workspaces]
|
||||
independent = true
|
|
@ -1,25 +0,0 @@
|
|||
TESTARGS=--lib --bins --examples --tests
|
||||
|
||||
# cargo install cargo-watch
|
||||
watch:
|
||||
cargo watch -c -x 'test $(TESTARGS) -- --nocapture'
|
||||
|
||||
test:
|
||||
cargo test $(TESTARGS) -- --nocapture
|
||||
|
||||
bench: benches/testdata.bin
|
||||
cargo bench --benches
|
||||
|
||||
.INTERMEDIATE: benches/testdata.bin
|
||||
benches/testdata.bin: benches/testdata.bin.gz
|
||||
gzip -dc $< > $@
|
||||
|
||||
clippy-watch:
|
||||
cargo watch -c -x 'clippy $(TESTARGS)'
|
||||
|
||||
inotifytest:
|
||||
inotifytest sh -c 'reset; cargo build $(TESTARGS) && RUST_BACKTRACE=1 cargo test $(TESTARGS) -- --nocapture'
|
||||
|
||||
debug-tests:
|
||||
cargo test $(TESTARGS) --no-run
|
||||
gdb --args $$(cargo test 3>&1 1>&2 2>&3 3>&- | grep Running | awk '{print $$2}') --test-threads=1
|
|
@ -1,23 +0,0 @@
|
|||
```shell
|
||||
cargo add preserves
|
||||
```
|
||||
|
||||
This crate ([`preserves` on crates.io](https://crates.io/crates/preserves)) implements
|
||||
[Preserves](https://preserves.dev/) for Rust. It provides the core
|
||||
[semantics](https://preserves.dev/preserves.html#semantics) as well as both the [human-readable
|
||||
text syntax][crate::value::text] (a superset of JSON) and [machine-oriented binary
|
||||
format][crate::value::packed] (including
|
||||
[canonicalization](https://preserves.dev/canonical-binary.html)) for Preserves.
|
||||
|
||||
This crate is the foundation for others such as
|
||||
|
||||
- [`preserves-schema`](https://docs.rs/preserves-schema/), which implements [Preserves
|
||||
Schema](https://preserves.dev/preserves-schema.html);
|
||||
- [`preserves-path`](https://docs.rs/preserves-path/), which implements [Preserves
|
||||
Path](https://preserves.dev/preserves-path.html); and
|
||||
- [`preserves-tools`](https://crates.io/crates/preserves-tools), which provides command-line
|
||||
utilities for working with Preserves, in particular
|
||||
[`preserves-tool`](https://preserves.dev/doc/preserves-tool.html), a kind of Preserves
|
||||
Swiss-army knife.
|
||||
|
||||
It also includes [Serde](https://serde.rs/) support (modules [de], [ser], [symbol], [set]).
|
|
@ -1 +0,0 @@
|
|||
testdata.bin
|
|
@ -1,170 +0,0 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use preserves::de;
|
||||
use preserves::ser;
|
||||
use preserves::value;
|
||||
use preserves::value::packed::annotated_iovalue_from_bytes;
|
||||
use preserves::value::BinarySource;
|
||||
use preserves::value::BytesBinarySource;
|
||||
use preserves::value::IOBinarySource;
|
||||
use preserves::value::IOValueDomainCodec;
|
||||
use preserves::value::PackedWriter;
|
||||
use preserves::value::Reader;
|
||||
use preserves::value::Writer;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::io::Seek;
|
||||
|
||||
#[path = "../tests/samples/mod.rs"]
|
||||
mod samples;
|
||||
use samples::TestCases;
|
||||
|
||||
pub fn bench_decoder_bytes(c: &mut Criterion) {
|
||||
let mut fh = File::open("../../../tests/samples.bin").unwrap();
|
||||
let mut bs = vec![];
|
||||
fh.read_to_end(&mut bs).ok();
|
||||
c.bench_function("decode samples.bin via bytes", |b| {
|
||||
b.iter_with_large_drop(|| annotated_iovalue_from_bytes(&bs[..]).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn bench_decoder_file(c: &mut Criterion) {
|
||||
let mut fh = File::open("../../../tests/samples.bin").unwrap();
|
||||
c.bench_function("decode samples.bin via file", |b| {
|
||||
b.iter_with_large_drop(|| {
|
||||
fh.seek(io::SeekFrom::Start(0)).ok();
|
||||
IOBinarySource::new(&mut fh)
|
||||
.packed_iovalues()
|
||||
.demand_next(true)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn bench_decoder_buffered_file(c: &mut Criterion) {
|
||||
let mut fh = io::BufReader::new(File::open("../../../tests/samples.bin").unwrap());
|
||||
c.bench_function("decode samples.bin via buffered file", |b| {
|
||||
b.iter_with_large_drop(|| {
|
||||
fh.seek(io::SeekFrom::Start(0)).ok();
|
||||
IOBinarySource::new(&mut fh)
|
||||
.packed_iovalues()
|
||||
.demand_next(true)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn bench_encoder(c: &mut Criterion) {
|
||||
let mut fh = File::open("../../../tests/samples.bin").unwrap();
|
||||
let v = IOBinarySource::new(&mut fh)
|
||||
.packed_iovalues()
|
||||
.demand_next(true)
|
||||
.unwrap();
|
||||
c.bench_function("encode samples.bin", |b| {
|
||||
b.iter_with_large_drop(|| PackedWriter::encode_iovalue(&v).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn bench_de(c: &mut Criterion) {
|
||||
let mut fh = File::open("../../../tests/samples.bin").unwrap();
|
||||
let mut bs = vec![];
|
||||
fh.read_to_end(&mut bs).ok();
|
||||
c.bench_function("deserialize samples.bin", |b| {
|
||||
b.iter_with_large_drop(|| de::from_bytes::<TestCases>(&bs[..]).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn bench_ser(c: &mut Criterion) {
|
||||
let mut fh = File::open("../../../tests/samples.bin").unwrap();
|
||||
let v: TestCases = de::from_read(&mut fh).unwrap();
|
||||
c.bench_function("serialize samples.bin", |b| {
|
||||
b.iter_with_large_drop(|| {
|
||||
let mut bs = vec![];
|
||||
ser::to_writer(&mut PackedWriter::new(&mut bs), &v).unwrap();
|
||||
bs
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn bench_decoder_de(c: &mut Criterion) {
|
||||
let mut fh = File::open("../../../tests/samples.bin").unwrap();
|
||||
let mut bs = vec![];
|
||||
fh.read_to_end(&mut bs).ok();
|
||||
c.bench_function("decode-then-deserialize samples.bin", |b| {
|
||||
b.iter_with_large_drop(|| {
|
||||
value::de::from_value::<TestCases>(&annotated_iovalue_from_bytes(&bs[..]).unwrap())
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn bench_ser_encoder(c: &mut Criterion) {
|
||||
let mut fh = File::open("../../../tests/samples.bin").unwrap();
|
||||
let v: TestCases = de::from_read(&mut fh).unwrap();
|
||||
c.bench_function("serialize-then-encode samples.bin", |b| {
|
||||
b.iter_with_large_drop(|| PackedWriter::encode_iovalue(&value::ser::to_value(&v)).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn large_testdata_decoder_with_ann(c: &mut Criterion) {
|
||||
c.bench_function("decode testdata.bin with annotations", |b| {
|
||||
let mut fh = File::open("benches/testdata.bin").unwrap();
|
||||
let mut bs = vec![];
|
||||
fh.read_to_end(&mut bs).ok();
|
||||
b.iter(|| {
|
||||
let mut src = BytesBinarySource::new(&bs[..]);
|
||||
let mut r = src.packed_iovalues();
|
||||
while let Some(_) = r.next(true).unwrap() {}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn large_testdata_decoder_without_ann(c: &mut Criterion) {
|
||||
c.bench_function("decode testdata.bin without annotations", |b| {
|
||||
let mut fh = File::open("benches/testdata.bin").unwrap();
|
||||
let mut bs = vec![];
|
||||
fh.read_to_end(&mut bs).ok();
|
||||
b.iter(|| {
|
||||
let mut src = BytesBinarySource::new(&bs[..]);
|
||||
let mut r = src.packed_iovalues();
|
||||
while let Some(_) = r.next(false).unwrap() {}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn large_testdata_encoder(c: &mut Criterion) {
|
||||
c.bench_function("encode testdata.bin", |b| {
|
||||
let mut fh = io::BufReader::new(File::open("benches/testdata.bin").unwrap());
|
||||
let mut vs = vec![];
|
||||
let mut src = IOBinarySource::new(&mut fh);
|
||||
let mut r = src.packed_iovalues();
|
||||
while let Some(v) = r.next(true).unwrap() {
|
||||
vs.push(v);
|
||||
}
|
||||
b.iter_with_large_drop(|| {
|
||||
let mut bs = vec![];
|
||||
let mut w = PackedWriter::new(&mut bs);
|
||||
let mut enc = IOValueDomainCodec;
|
||||
for v in &vs {
|
||||
w.write(&mut enc, v).unwrap();
|
||||
}
|
||||
bs
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
codec,
|
||||
bench_decoder_bytes,
|
||||
bench_decoder_file,
|
||||
bench_decoder_buffered_file,
|
||||
bench_encoder
|
||||
);
|
||||
criterion_group!(serde, bench_de, bench_ser);
|
||||
criterion_group!(codec_then_serde, bench_decoder_de, bench_ser_encoder);
|
||||
criterion_group! {
|
||||
name = large_testdata;
|
||||
config = Criterion::default().sample_size(10);
|
||||
targets = large_testdata_decoder_with_ann, large_testdata_decoder_without_ann, large_testdata_encoder
|
||||
}
|
||||
criterion_main!(codec, serde, codec_then_serde, large_testdata);
|
Binary file not shown.
|
@ -1,31 +0,0 @@
|
|||
For a value `V`, we write `«V»` for the binary encoding of `V`.
|
||||
|
||||
```text
|
||||
«#f» = [0x80]
|
||||
«#t» = [0x81]
|
||||
|
||||
«@W V» = [0x85] ++ «W» ++ «V»
|
||||
«#!V» = [0x86] ++ «V»
|
||||
|
||||
«V» if V ∈ Double = [0x87, 0x08] ++ binary64(V)
|
||||
|
||||
«V» if V ∈ SignedInteger = [0xB0] ++ varint(|intbytes(V)|) ++ intbytes(V)
|
||||
«V» if V ∈ String = [0xB1] ++ varint(|utf8(V)|) ++ utf8(V)
|
||||
«V» if V ∈ ByteString = [0xB2] ++ varint(|V|) ++ V
|
||||
«V» if V ∈ Symbol = [0xB3] ++ varint(|utf8(V)|) ++ utf8(V)
|
||||
|
||||
«<L F_1...F_m>» = [0xB4] ++ «L» ++ «F_1» ++...++ «F_m» ++ [0x84]
|
||||
«[X_1...X_m]» = [0xB5] ++ «X_1» ++...++ «X_m» ++ [0x84]
|
||||
«#{E_1...E_m}» = [0xB6] ++ «E_1» ++...++ «E_m» ++ [0x84]
|
||||
«{K_1:V_1...K_m:V_m}» = [0xB7] ++ «K_1» ++ «V_1» ++...++ «K_m» ++ «V_m» ++ [0x84]
|
||||
|
||||
varint(n) = [n] if n < 128
|
||||
[(n & 127) | 128] ++ varint(n >> 7) if n ≥ 128
|
||||
|
||||
intbytes(n) = the empty sequence if n = 0, otherwise signedBigEndian(n)
|
||||
|
||||
signedBigEndian(n) = [n & 255] if -128 ≤ n ≤ 127
|
||||
signedBigEndian(n >> 8) ++ [n & 255] otherwise
|
||||
```
|
||||
|
||||
The function `binary64(D)` yields the big-endian 8-byte IEEE 754 binary representation of `D`.
|
|
@ -1,48 +0,0 @@
|
|||
```text
|
||||
Document := Value ws
|
||||
Value := ws (Record | Collection | Embedded | Annotated | Atom)
|
||||
Collection := Sequence | Dictionary | Set
|
||||
|
||||
Record := `<` Value+ ws `>`
|
||||
Sequence := `[` (commas Value)* commas `]`
|
||||
Set := `#{` (commas Value)* commas `}`
|
||||
Dictionary := `{` (commas Value ws `:` Value)* commas `}`
|
||||
commas := (ws `,`)* ws
|
||||
|
||||
Embedded := `#!` Value
|
||||
Annotated := Annotation Value
|
||||
Annotation := `@` Value | `#` ((space | tab) linecomment) (cr | lf)
|
||||
|
||||
Atom := Boolean | ByteString | String | QuotedSymbol | Symbol | Number
|
||||
Boolean := `#t` | `#f`
|
||||
ByteString := `#"` binchar* `"`
|
||||
| `#x"` (ws hex hex)* ws `"`
|
||||
| `#[` (ws base64char)* ws `]`
|
||||
String := `"` («any unicode scalar except `\` or `"`» | escaped | `\"`)* `"`
|
||||
QuotedSymbol := `|` («any unicode scalar except `\` or `|`» | escaped | `\|`)* `|`
|
||||
Symbol := (`A`..`Z` | `a`..`z` | `0`..`9` | sympunct | symuchar)+
|
||||
Number := Double | SignedInteger
|
||||
Double := flt | `#xd"` (ws hex hex)8 ws `"`
|
||||
SignedInteger := int
|
||||
|
||||
escaped := `\\` | `\/` | `\b` | `\f` | `\n` | `\r` | `\t` | `\u` hex hex hex hex
|
||||
binescaped := `\\` | `\/` | `\b` | `\f` | `\n` | `\r` | `\t` | `\x` hex hex
|
||||
binchar := «any scalar ≥32 and ≤126, except `\` or `"`» | binescaped | `\"`
|
||||
base64char := `A`..`Z` | `a`..`z` | `0`..`9` | `+` | `/` | `-` | `_` | `=`
|
||||
sympunct := `~` | `!` | `$` | `%` | `^` | `&` | `*` | `?`
|
||||
| `_` | `=` | `+` | `-` | `/` | `.`
|
||||
symuchar := «any scalar value ≥128 whose Unicode category is
|
||||
Lu, Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd, Nl, No, Pc,
|
||||
Pd, Po, Sc, Sm, Sk, So, or Co»
|
||||
|
||||
flt := int ( frac exp | frac | exp )
|
||||
int := (`-`|`+`) (`0`..`9`)+
|
||||
frac := `.` (`0`..`9`)+
|
||||
exp := (`e`|`E`) (`-`|`+`) (`0`..`9`)+
|
||||
hex := `A`..`F` | `a`..`f` | `0`..`9`
|
||||
|
||||
ws := (space | tab | cr | lf)*
|
||||
delimiter := ws | `<` | `>` | `[` | `]` | `{` | `}`
|
||||
| `#` | `:` | `"` | `|` | `@` | `;` | `,`
|
||||
linecomment := «any unicode scalar except cr or lf»*
|
||||
```
|
|
@ -1,17 +0,0 @@
|
|||
```text
|
||||
Value = Atom
|
||||
| Compound
|
||||
| Embedded
|
||||
|
||||
Atom = Boolean
|
||||
| Double
|
||||
| SignedInteger
|
||||
| String
|
||||
| ByteString
|
||||
| Symbol
|
||||
|
||||
Compound = Record
|
||||
| Sequence
|
||||
| Set
|
||||
| Dictionary
|
||||
```
|
|
@ -1,12 +0,0 @@
|
|||
*Preserves* is a data model, with associated serialization formats.
|
||||
|
||||
It supports *records* with user-defined *labels*, embedded
|
||||
*references*, and the usual suite of atomic and compound data types,
|
||||
including *binary* data as a distinct type from text strings. Its
|
||||
*annotations* allow separation of data from metadata such as comments,
|
||||
trace information, and provenance information.
|
||||
|
||||
Preserves departs from many other data languages in defining how to
|
||||
*compare* two values. Comparison is based on the data model, not on
|
||||
syntax or on data structures of any particular implementation
|
||||
language.
|
|
@ -1,10 +0,0 @@
|
|||
BINARY_FILES=known-data.bin unknown-data.bin
|
||||
|
||||
all: $(BINARY_FILES)
|
||||
|
||||
%.bin: %.txt Makefile
|
||||
racket ../../../racket/preserves/preserves/tool.rkt --atob --no-annotations < $< > $@.tmp || (rm -f $@.tmp; false)
|
||||
mv $@.tmp $@
|
||||
|
||||
clean:
|
||||
rm -f $(BINARY_FILES)
|
|
@ -1,57 +0,0 @@
|
|||
use preserves::{
|
||||
de,
|
||||
value::{self, BinarySource, IOBinarySource, Reader},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
enum Fruit {
|
||||
Apple(Colour),
|
||||
Pear(Variety),
|
||||
Banana(Weight, Colour, u8),
|
||||
|
||||
// Peach,
|
||||
#[serde(other)]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename = "kilograms")]
|
||||
struct Weight(f32);
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
struct Colour {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
enum Variety {
|
||||
Conference,
|
||||
Bosc,
|
||||
European,
|
||||
Anjou,
|
||||
}
|
||||
|
||||
fn try_file(kind: &str, path: &str) -> io::Result<()> {
|
||||
let fruits_value = IOBinarySource::new(&mut File::open(path)?)
|
||||
.packed_iovalues()
|
||||
.demand_next(true)?;
|
||||
println!("{:#?}", fruits_value);
|
||||
|
||||
let fruits1: Vec<Fruit> = value::de::from_value(&fruits_value)?;
|
||||
println!("(via generic decoding) {}: {:?}", kind, fruits1);
|
||||
|
||||
let fruits2: Vec<Fruit> = de::from_read(&mut File::open(path)?)?;
|
||||
println!("(direct from binary) {}: {:?}\n", kind, fruits2);
|
||||
|
||||
assert_eq!(fruits1, fruits2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
try_file("KNOWN", "examples/known-data.bin")?;
|
||||
try_file("UNKNOWN", "examples/unknown-data.bin")?;
|
||||
Ok(())
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
µ´³Apple´³Colour±red„„´³Apple´³Colour±green„„´³Banana´³ kilogramsƒ?¾¸Që…¸„´³Colour±brownish„•„´³Pear´³
|
||||
Conference„„„
|
|
@ -1,7 +0,0 @@
|
|||
@<EmacsMode "-*- preserves -*-">
|
||||
[
|
||||
<Apple <Colour "red">>
|
||||
<Apple <Colour "green">>
|
||||
<Banana <kilograms 0.12> <Colour "brownish"> 5>
|
||||
<Pear <Conference>>
|
||||
]
|
|
@ -1,2 +0,0 @@
|
|||
µ´³Apple´³Colour±red„„´³Peach„´³Apple´³Colour±green„„´³Banana´³ kilogramsƒ?¾¸Që…¸„´³Colour±brownish„•·³ripeness´³Squishy„„„´³Pear´³
|
||||
Conference„„„
|
|
@ -1,8 +0,0 @@
|
|||
@<EmacsMode "-*- preserves -*-">
|
||||
[
|
||||
<Apple <Colour "red">>
|
||||
<Peach>
|
||||
<Apple <Colour "green">>
|
||||
<Banana <kilograms 0.12> <Colour "brownish"> 5 {ripeness: <Squishy>}>
|
||||
<Pear <Conference>>
|
||||
]
|
|
@ -1,479 +0,0 @@
|
|||
//! Support for Serde deserialization of Preserves terms described by Rust data types.
|
||||
|
||||
use serde::de::{DeserializeSeed, EnumAccess, MapAccess, SeqAccess, VariantAccess, Visitor};
|
||||
use serde::Deserialize;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::value::boundary as B;
|
||||
use super::value::reader::{BytesBinarySource, IOBinarySource, Reader};
|
||||
use super::value::{IOValue, IOValueDomainCodec, PackedReader, TextReader, ViaCodec};
|
||||
|
||||
pub use super::error::Error;
|
||||
|
||||
/// A [std::result::Result] type including [Error], the Preserves Serde deserialization error
|
||||
/// type, as its error.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Serde deserializer for Preserves-encoded Rust data. Use [Deserializer::from_reader] to
|
||||
/// construct instances, or [from_bytes]/[from_text]/[from_read]/[from_reader] etc to
|
||||
/// deserialize single terms directly.
|
||||
pub struct Deserializer<'de, 'r, R: Reader<'de, IOValue>> {
|
||||
/// The underlying Preserves [reader][crate::value::reader::Reader].
|
||||
pub read: &'r mut R,
|
||||
phantom: PhantomData<&'de ()>,
|
||||
}
|
||||
|
||||
/// Deserialize a `T` from `bytes`, which must contain a Preserves [machine-oriented binary
|
||||
/// syntax][crate::value::packed] term corresponding to the Serde serialization of a `T`.
|
||||
pub fn from_bytes<'de, T>(bytes: &'de [u8]) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
from_reader(&mut PackedReader::new(
|
||||
&mut BytesBinarySource::new(bytes),
|
||||
IOValueDomainCodec,
|
||||
))
|
||||
}
|
||||
|
||||
/// Deserialize a `T` from `text`, which must contain a Preserves [text
|
||||
/// syntax][crate::value::text] term corresponding to the Serde serialization of a `T`.
|
||||
pub fn from_text<'de, T>(text: &'de str) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
from_reader(&mut TextReader::new(
|
||||
&mut BytesBinarySource::new(text.as_bytes()),
|
||||
ViaCodec::new(IOValueDomainCodec),
|
||||
))
|
||||
}
|
||||
|
||||
/// Deserialize a `T` from `read`, which must yield a Preserves [machine-oriented binary
|
||||
/// syntax][crate::value::packed] term corresponding to the Serde serialization of a `T`.
|
||||
pub fn from_read<'de, 'r, IOR: io::Read + io::Seek, T>(read: &'r mut IOR) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
from_reader(&mut PackedReader::new(
|
||||
&mut IOBinarySource::new(read),
|
||||
IOValueDomainCodec,
|
||||
))
|
||||
}
|
||||
|
||||
/// Deserialize a `T` from `read`, which must yield a Preserves term corresponding to the Serde
|
||||
/// serialization of a `T`.
|
||||
pub fn from_reader<'r, 'de, R: Reader<'de, IOValue>, T>(read: &'r mut R) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
let mut de = Deserializer::from_reader(read);
|
||||
let t = T::deserialize(&mut de)?;
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
impl<'r, 'de, R: Reader<'de, IOValue>> Deserializer<'de, 'r, R> {
|
||||
/// Construct a Deserializer from `read`, a Preserves [reader][crate::value::Reader].
|
||||
pub fn from_reader(read: &'r mut R) -> Self {
|
||||
Deserializer {
|
||||
read,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'de, 'a, R: Reader<'de, IOValue>> serde::de::Deserializer<'de>
|
||||
for &'a mut Deserializer<'de, 'r, R>
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
// Won't support this here -- use value::de::Deserializer for this
|
||||
Err(Error::CannotDeserializeAny)
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_bool(self.read.next_boolean()?)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i8(self.read.next_i8()?)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i16(self.read.next_i16()?)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i32(self.read.next_i32()?)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i64(self.read.next_i64()?)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u8(self.read.next_u8()?)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u16(self.read.next_u16()?)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u32(self.read.next_u32()?)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u64(self.read.next_u64()?)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f32(self.read.next_f64()? as f32)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f64(self.read.next_f64()?)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_char(self.read.next_char()?)
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.read.next_str()? {
|
||||
Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
|
||||
Cow::Owned(s) => visitor.visit_str(&s),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.read.next_bytestring()? {
|
||||
Cow::Borrowed(bs) => visitor.visit_borrowed_bytes(bs),
|
||||
Cow::Owned(bs) => visitor.visit_bytes(&bs),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_byte_buf(self.read.next_bytestring()?.into_owned())
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if let Some(mut b) = self.read.open_option()? {
|
||||
self.read
|
||||
.ensure_more_expected(&mut b, &B::Item::RecordField)?;
|
||||
let result = visitor.visit_some(&mut *self)?;
|
||||
self.read.ensure_complete(b, &B::Item::RecordField)?;
|
||||
Ok(result)
|
||||
} else {
|
||||
Ok(visitor.visit_none::<Error>()?)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let b = self.read.open_simple_record("tuple", Some(0))?;
|
||||
let result = visitor.visit_unit::<Error>()?;
|
||||
self.read.ensure_complete(b, &B::Item::RecordField)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let b = self.read.open_simple_record(name, Some(0))?;
|
||||
let result = visitor.visit_unit::<Error>()?;
|
||||
self.read.ensure_complete(b, &B::Item::RecordField)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match super::value::magic::transmit_input_value(name, || {
|
||||
Ok(self.read.demand_next(true)?)
|
||||
})? {
|
||||
Some(v) => visitor.visit_u64(v),
|
||||
None => {
|
||||
let mut b = self.read.open_simple_record(name, Some(1))?;
|
||||
self.read
|
||||
.ensure_more_expected(&mut b, &B::Item::RecordField)?;
|
||||
let result = visitor.visit_newtype_struct(&mut *self)?;
|
||||
self.read.ensure_complete(b, &B::Item::RecordField)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
// Hack around serde's model: Deserialize *sets* as sequences,
|
||||
// too, and reconstruct them as Rust Sets on the visitor side.
|
||||
let i = self.read.open_sequence_or_set()?;
|
||||
visitor.visit_seq(Seq::new(self, B::Type::default(), i))
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let b = self.read.open_simple_record("tuple", Some(len))?;
|
||||
let mut seq = Seq::new(self, b, B::Item::RecordField);
|
||||
let result = visitor.visit_seq(&mut seq)?;
|
||||
seq.skip_remainder()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let b = self.read.open_simple_record(name, Some(len))?;
|
||||
let mut seq = Seq::new(self, b, B::Item::RecordField);
|
||||
let result = visitor.visit_seq(&mut seq)?;
|
||||
seq.skip_remainder()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.read.open_dictionary()?;
|
||||
let mut seq = Seq::new(self, B::Type::default(), B::Item::DictionaryKey);
|
||||
let result = visitor.visit_map(&mut seq)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let b = self.read.open_simple_record(name, Some(fields.len()))?;
|
||||
let mut seq = Seq::new(self, b, B::Item::RecordField);
|
||||
let result = visitor.visit_seq(&mut seq)?;
|
||||
seq.skip_remainder()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_enum(self)
|
||||
}
|
||||
|
||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.read.next_symbol()? {
|
||||
Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
|
||||
Cow::Owned(s) => visitor.visit_str(&s),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct Seq<'de, 'r, 'a, R: Reader<'de, IOValue>> {
|
||||
b: B::Type,
|
||||
i: B::Item,
|
||||
de: &'a mut Deserializer<'de, 'r, R>,
|
||||
}
|
||||
|
||||
impl<'de, 'r, 'a, R: Reader<'de, IOValue>> Seq<'de, 'r, 'a, R> {
|
||||
fn new(de: &'a mut Deserializer<'de, 'r, R>, b: B::Type, i: B::Item) -> Self {
|
||||
Seq { b, i, de }
|
||||
}
|
||||
|
||||
fn skip_remainder(&mut self) -> Result<()> {
|
||||
while !self.de.read.close_compound(&mut self.b, &self.i)? {
|
||||
self.de.read.skip_value()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_item<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
match self.de.read.close_compound(&mut self.b, &self.i)? {
|
||||
true => Ok(None),
|
||||
false => Ok(Some(seed.deserialize(&mut *self.de)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'r, 'a, R: Reader<'de, IOValue>> SeqAccess<'de> for Seq<'de, 'r, 'a, R> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
Ok(self.next_item(seed)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'r, 'a, R: Reader<'de, IOValue>> MapAccess<'de> for Seq<'de, 'r, 'a, R> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
|
||||
where
|
||||
K: DeserializeSeed<'de>,
|
||||
{
|
||||
self.i = B::Item::DictionaryKey;
|
||||
self.next_item(seed)
|
||||
}
|
||||
|
||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
self.i = B::Item::DictionaryValue;
|
||||
match self.next_item(seed)? {
|
||||
Some(item) => Ok(item),
|
||||
None => Err(Error::MissingItem),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'r, 'a, R: Reader<'de, IOValue>> EnumAccess<'de> for &'a mut Deserializer<'de, 'r, R> {
|
||||
type Error = Error;
|
||||
type Variant = Seq<'de, 'r, 'a, R>;
|
||||
|
||||
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
let b = self.read.open_record(None)?;
|
||||
let variant = seed.deserialize(&mut *self)?;
|
||||
Ok((variant, Seq::new(self, b, B::Item::RecordField)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'r, 'a, R: Reader<'de, IOValue>> VariantAccess<'de> for Seq<'de, 'r, 'a, R> {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(mut self) -> Result<()> {
|
||||
self.skip_remainder()
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<T>(mut self, seed: T) -> Result<T::Value>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
match self.next_item(seed)? {
|
||||
None => Err(Error::MissingItem),
|
||||
Some(v) => {
|
||||
self.skip_remainder()?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tuple_variant<V>(mut self, _len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let result = visitor.visit_seq(&mut self)?;
|
||||
self.skip_remainder()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn struct_variant<V>(mut self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let result = visitor.visit_seq(&mut self)?;
|
||||
self.skip_remainder()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
//! Serde and plain-Preserves codec errors.
|
||||
|
||||
use num::bigint::BigInt;
|
||||
use std::convert::From;
|
||||
use std::io;
|
||||
|
||||
/// Representation of parse, deserialization, and other conversion errors.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Generic IO error.
|
||||
Io(io::Error),
|
||||
/// Generic message for the user.
|
||||
Message(String),
|
||||
/// Invalid unicode scalar `n` found during interpretation of a `<UnicodeScalar n>` record
|
||||
/// as a Rust `char`.
|
||||
InvalidUnicodeScalar(u32),
|
||||
/// Preserves supports arbitrary integers; when these are converted to specific Rust
|
||||
/// machine word types, sometimes they exceed the available range.
|
||||
NumberOutOfRange(BigInt),
|
||||
/// Serde has limited support for deserializing free-form data; this error is signalled
|
||||
/// when one of the limits is hit.
|
||||
CannotDeserializeAny,
|
||||
/// Syntax error: missing closing delimiter (`)`, `]`, `}`, `>` in text syntax; `0x84` in binary syntax; etc.)
|
||||
MissingCloseDelimiter,
|
||||
/// Signalled when an expected term is not present.
|
||||
MissingItem,
|
||||
/// Signalled when what was received did not match expectations.
|
||||
Expected(ExpectedKind, Received),
|
||||
#[doc(hidden)] // TODO remove this enum variant? It isn't used
|
||||
StreamingSerializationUnsupported,
|
||||
}
|
||||
|
||||
/// Used in [Error::Expected] to indicate what was received.
|
||||
#[derive(Debug)]
|
||||
pub enum Received {
|
||||
#[doc(hidden)] // TODO remove this enum variant? It isn't used
|
||||
ReceivedSomethingElse,
|
||||
/// Received a record with the given label symbol text.
|
||||
ReceivedRecordWithLabel(String),
|
||||
/// Received some other value, described in the `String`
|
||||
ReceivedOtherValue(String),
|
||||
}
|
||||
|
||||
/// Used in [Error::Expected] to indicate what was expected.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ExpectedKind {
|
||||
Boolean,
|
||||
Float,
|
||||
Double,
|
||||
|
||||
SignedIntegerI128,
|
||||
SignedIntegerU128,
|
||||
SignedInteger,
|
||||
String,
|
||||
ByteString,
|
||||
Symbol,
|
||||
|
||||
/// Expected a record, either of a specific arity (length) or of no specific arity
|
||||
Record(Option<usize>),
|
||||
/// Expected a record with a symbol label with text `String`, perhaps of some specific arity
|
||||
SimpleRecord(String, Option<usize>),
|
||||
Sequence,
|
||||
Set,
|
||||
Dictionary,
|
||||
|
||||
Embedded,
|
||||
|
||||
SequenceOrSet, // Because of hacking up serde's data model: see open_sequence_or_set etc.
|
||||
|
||||
Option,
|
||||
UnicodeScalar,
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for io::Error {
|
||||
fn from(e: Error) -> Self {
|
||||
match e {
|
||||
Error::Io(ioe) => ioe,
|
||||
Error::Message(str) => io::Error::new(io::ErrorKind::Other, str),
|
||||
_ => io::Error::new(io::ErrorKind::Other, e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||
Self::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||
Self::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/// True iff `e` is `Error::Io`
|
||||
pub fn is_io_error(e: &Error) -> bool {
|
||||
matches!(e, Error::Io(_))
|
||||
}
|
||||
|
||||
/// Produce the generic "end of file" error, `Error::Io(`[io_eof]`())`
|
||||
pub fn eof() -> Error {
|
||||
Error::Io(io_eof())
|
||||
}
|
||||
|
||||
/// True iff `e` is an "end of file" error; see [is_eof_io_error]
|
||||
pub fn is_eof_error(e: &Error) -> bool {
|
||||
if let Error::Io(ioe) = e {
|
||||
is_eof_io_error(ioe)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce a syntax error bearing the message `s`
|
||||
pub fn syntax_error(s: &str) -> Error {
|
||||
Error::Io(io_syntax_error(s))
|
||||
}
|
||||
|
||||
/// True iff `e` is a syntax error; see [is_syntax_io_error]
|
||||
pub fn is_syntax_error(e: &Error) -> bool {
|
||||
if let Error::Io(ioe) = e {
|
||||
is_syntax_io_error(ioe)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/// Produce an [io::Error] of [io::ErrorKind::UnexpectedEof].
|
||||
pub fn io_eof() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::UnexpectedEof, "EOF")
|
||||
}
|
||||
|
||||
/// True iff `e` is [io::ErrorKind::UnexpectedEof]
|
||||
pub fn is_eof_io_error(e: &io::Error) -> bool {
|
||||
matches!(e.kind(), io::ErrorKind::UnexpectedEof)
|
||||
}
|
||||
|
||||
/// Produce a syntax error ([io::ErrorKind::InvalidData]) bearing the message `s`
|
||||
pub fn io_syntax_error(s: &str) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::InvalidData, s)
|
||||
}
|
||||
|
||||
/// True iff `e` is an [io::ErrorKind::InvalidData] (a syntax error)
|
||||
pub fn is_syntax_io_error(e: &io::Error) -> bool {
|
||||
matches!(e.kind(), io::ErrorKind::InvalidData)
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
//! Utilities for producing and flexibly parsing strings containing hexadecimal binary data.
|
||||
|
||||
/// Utility for parsing hex binary data from strings.
|
||||
pub enum HexParser {
|
||||
/// "Liberal" parsing simply ignores characters that are not (case-insensitive) hex digits.
|
||||
Liberal,
|
||||
/// "Whitespace allowed" parsing ignores whitespace, but fails a parse on anything other
|
||||
/// than hex or whitespace.
|
||||
WhitespaceAllowed,
|
||||
/// "Strict" parsing accepts only (case-insensitive) hex digits; no whitespace, no other
|
||||
/// characters.
|
||||
Strict,
|
||||
}
|
||||
|
||||
/// Utility for formatting binary data as hex.
|
||||
pub enum HexFormatter {
|
||||
/// Produces LF-separated lines with a maximum of `usize` hex digits in each line.
|
||||
Lines(usize),
|
||||
/// Simply packs hex digits in as tightly as possible.
|
||||
Packed,
|
||||
}
|
||||
|
||||
/// Convert a number 0..15 to a hex digit [char].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if given `v` outside the range 0..15 inclusive.
|
||||
///
|
||||
pub fn hexdigit(v: u8) -> char {
|
||||
char::from_digit(v as u32, 16).expect("hexadecimal digit value")
|
||||
}
|
||||
|
||||
impl HexParser {
|
||||
/// Decode `s` according to the given rules for `self`; see [HexParser].
|
||||
/// If the parse fails, yield `None`.
|
||||
pub fn decode(&self, s: &str) -> Option<Vec<u8>> {
|
||||
let mut result = Vec::new();
|
||||
let mut buf: u8 = 0;
|
||||
let mut buf_full = false;
|
||||
for c in s.chars() {
|
||||
match c.to_digit(16) {
|
||||
None => match self {
|
||||
HexParser::Liberal => (),
|
||||
HexParser::WhitespaceAllowed => {
|
||||
if !c.is_whitespace() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
HexParser::Strict => return None,
|
||||
},
|
||||
Some(nibble) => {
|
||||
if buf_full {
|
||||
result.push(buf << 4 | (nibble as u8));
|
||||
buf_full = false;
|
||||
} else {
|
||||
buf = nibble as u8;
|
||||
buf_full = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if buf_full {
|
||||
None // odd number of hexits
|
||||
} else {
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HexFormatter {
|
||||
/// Encode `bs` according to the given rules for `self; see [HexFormatter].
|
||||
pub fn encode(&self, bs: &[u8]) -> String {
|
||||
match self {
|
||||
HexFormatter::Lines(max_line_length) => {
|
||||
let mut lines = Vec::new();
|
||||
let mut line = String::new();
|
||||
for b in bs {
|
||||
if line.len() + 2 > *max_line_length {
|
||||
lines.push(std::mem::take(&mut line));
|
||||
}
|
||||
line.push(hexdigit(b >> 4));
|
||||
line.push(hexdigit(b & 15));
|
||||
}
|
||||
lines.push(std::mem::take(&mut line));
|
||||
lines.join("\n")
|
||||
}
|
||||
HexFormatter::Packed => {
|
||||
let mut result = String::new();
|
||||
for b in bs {
|
||||
result.push(hexdigit(b >> 4));
|
||||
result.push(hexdigit(b & 15));
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_decode_packed() {
|
||||
let s = "01ab00ff";
|
||||
assert_eq!(HexParser::Strict.decode(s), Some(vec![1, 171, 0, 255]));
|
||||
assert_eq!(
|
||||
HexParser::WhitespaceAllowed.decode(s),
|
||||
Some(vec![1, 171, 0, 255])
|
||||
);
|
||||
assert_eq!(HexParser::Liberal.decode(s), Some(vec![1, 171, 0, 255]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_whitespace() {
|
||||
let s = "01ab 00ff";
|
||||
assert_eq!(HexParser::Strict.decode(s), None);
|
||||
assert_eq!(
|
||||
HexParser::WhitespaceAllowed.decode(s),
|
||||
Some(vec![1, 171, 0, 255])
|
||||
);
|
||||
assert_eq!(HexParser::Liberal.decode(s), Some(vec![1, 171, 0, 255]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_liberal() {
|
||||
let s = "01ab zz 00ff";
|
||||
assert_eq!(HexParser::Strict.decode(s), None);
|
||||
assert_eq!(HexParser::WhitespaceAllowed.decode(s), None);
|
||||
assert_eq!(HexParser::Liberal.decode(s), Some(vec![1, 171, 0, 255]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_lines() {
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(10).encode(&vec![0x5a; 11]),
|
||||
"5a5a5a5a5a\n5a5a5a5a5a\n5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(10).encode(&vec![0x5a; 10]),
|
||||
"5a5a5a5a5a\n5a5a5a5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(10).encode(&vec![0x5a; 9]),
|
||||
"5a5a5a5a5a\n5a5a5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(9).encode(&vec![0x5a; 11]),
|
||||
"5a5a5a5a\n5a5a5a5a\n5a5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(9).encode(&vec![0x5a; 10]),
|
||||
"5a5a5a5a\n5a5a5a5a\n5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(9).encode(&vec![0x5a; 9]),
|
||||
"5a5a5a5a\n5a5a5a5a\n5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(8).encode(&vec![0x5a; 11]),
|
||||
"5a5a5a5a\n5a5a5a5a\n5a5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(8).encode(&vec![0x5a; 10]),
|
||||
"5a5a5a5a\n5a5a5a5a\n5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Lines(8).encode(&vec![0x5a; 9]),
|
||||
"5a5a5a5a\n5a5a5a5a\n5a"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_packed() {
|
||||
assert_eq!(
|
||||
HexFormatter::Packed.encode(&vec![0x5a; 11]),
|
||||
"5a5a5a5a5a5a5a5a5a5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Packed.encode(&vec![0x5a; 10]),
|
||||
"5a5a5a5a5a5a5a5a5a5a"
|
||||
);
|
||||
assert_eq!(
|
||||
HexFormatter::Packed.encode(&vec![0x5a; 9]),
|
||||
"5a5a5a5a5a5a5a5a5a"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,810 +0,0 @@
|
|||
#![doc = concat!(
|
||||
include_str!("../README.md"),
|
||||
"# What is Preserves?\n\n",
|
||||
include_str!("../doc/what-is-preserves.md"),
|
||||
)]
|
||||
|
||||
pub mod de;
|
||||
pub mod error;
|
||||
pub mod hex;
|
||||
pub mod ser;
|
||||
pub mod set;
|
||||
pub mod symbol;
|
||||
pub mod value;
|
||||
|
||||
#[cfg(test)]
|
||||
mod dom {
|
||||
use super::value::*;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug, Hash, Clone, Ord, PartialEq, Eq, PartialOrd)]
|
||||
pub enum Dom {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
impl Domain for Dom {}
|
||||
|
||||
impl std::str::FromStr for Dom {
|
||||
type Err = io::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"One" => Ok(Dom::One),
|
||||
"Two" => Ok(Dom::Two),
|
||||
_ => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"cannot parse preserves test domain",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dom_as_preserves(v: &Dom) -> io::Result<UnwrappedIOValue> {
|
||||
Ok(match v {
|
||||
Dom::One => Value::bytestring(vec![255, 255, 255, 255]),
|
||||
Dom::Two => Value::symbol(&format!("Dom::{:?}", v)),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one() {
|
||||
let v: PlainValue<_> = Value::from(vec![
|
||||
Value::from(1).wrap(),
|
||||
Value::Embedded(Dom::One).wrap(),
|
||||
Value::from(2).wrap(),
|
||||
])
|
||||
.wrap();
|
||||
assert_eq!(
|
||||
PackedWriter::encode_iovalue(&v.copy_via(&mut dom_as_preserves).unwrap()).unwrap(),
|
||||
[0xb5, 0xb0, 0x01, 0x01, 0xb2, 0x04, 255, 255, 255, 255, 0xb0, 0x01, 0x02, 0x84]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two() {
|
||||
let v: PlainValue<_> = Value::from(vec![
|
||||
Value::from(1).wrap(),
|
||||
Value::Embedded(Dom::Two).wrap(),
|
||||
Value::from(2).wrap(),
|
||||
])
|
||||
.wrap();
|
||||
assert_eq!(
|
||||
PackedWriter::encode_iovalue(&v.copy_via(&mut dom_as_preserves).unwrap()).unwrap(),
|
||||
[0xb5, 0xb0, 0x01, 0x01, 0xb3, 0x08, 68, 111, 109, 58, 58, 84, 119, 111, 0xb0, 0x01, 0x02, 0x84]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ieee754_section_5_10_total_order_tests {
|
||||
use super::dom::Dom;
|
||||
use std::cmp::Ordering::{Equal, Greater, Less};
|
||||
|
||||
use crate::value::{PlainValue, Value};
|
||||
fn d(val: f64) -> Value<PlainValue<Dom>> {
|
||||
Value::from(val)
|
||||
}
|
||||
|
||||
// TODO: Test cases with a few different signalling and non-signalling NaNs
|
||||
|
||||
#[test]
|
||||
fn case64_a_1() {
|
||||
assert_eq!(d(1.0).cmp(&d(2.0)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_2() {
|
||||
assert_eq!(d(-1.0).cmp(&d(1.0)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_3() {
|
||||
assert_eq!(d(0.0).cmp(&d(1.0)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_4() {
|
||||
assert_eq!(d(-1.0).cmp(&d(0.0)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_5() {
|
||||
assert_eq!(d(-1e32).cmp(&d(-1e31)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_6() {
|
||||
assert_eq!(d(-1e32).cmp(&d(1e33)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_7() {
|
||||
assert_eq!(d(std::f64::NEG_INFINITY).cmp(&d(std::f64::INFINITY)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_8() {
|
||||
assert_eq!(d(std::f64::NEG_INFINITY).cmp(&d(0.0)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_9() {
|
||||
assert_eq!(d(std::f64::NEG_INFINITY).cmp(&d(1.0)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_10() {
|
||||
assert_eq!(d(std::f64::NEG_INFINITY).cmp(&d(1e33)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_11() {
|
||||
assert_eq!(d(0.0).cmp(&d(std::f64::INFINITY)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_12() {
|
||||
assert_eq!(d(1.0).cmp(&d(std::f64::INFINITY)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_a_13() {
|
||||
assert_eq!(d(1e33).cmp(&d(std::f64::INFINITY)), Less)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case64_b_1() {
|
||||
assert_eq!(d(2.0).cmp(&d(1.0)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_2() {
|
||||
assert_eq!(d(1.0).cmp(&d(-1.0)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_3() {
|
||||
assert_eq!(d(1.0).cmp(&d(0.0)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_4() {
|
||||
assert_eq!(d(0.0).cmp(&d(-1.0)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_5() {
|
||||
assert_eq!(d(-1e31).cmp(&d(-1e32)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_6() {
|
||||
assert_eq!(d(1e33).cmp(&d(-1e32)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_7() {
|
||||
assert_eq!(
|
||||
d(std::f64::INFINITY).cmp(&d(std::f64::NEG_INFINITY)),
|
||||
Greater
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_8() {
|
||||
assert_eq!(d(std::f64::INFINITY).cmp(&d(0.0)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_9() {
|
||||
assert_eq!(d(std::f64::INFINITY).cmp(&d(1.0)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_10() {
|
||||
assert_eq!(d(std::f64::INFINITY).cmp(&d(1e33)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_11() {
|
||||
assert_eq!(d(0.0).cmp(&d(std::f64::NEG_INFINITY)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_12() {
|
||||
assert_eq!(d(1.0).cmp(&d(std::f64::NEG_INFINITY)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_b_13() {
|
||||
assert_eq!(d(1e33).cmp(&d(std::f64::NEG_INFINITY)), Greater)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case64_c1() {
|
||||
assert_eq!(d(-0.0).cmp(&d(0.0)), Less)
|
||||
}
|
||||
#[test]
|
||||
fn case64_c2() {
|
||||
assert_eq!(d(0.0).cmp(&d(-0.0)), Greater)
|
||||
}
|
||||
#[test]
|
||||
fn case64_c3_1() {
|
||||
assert_eq!(d(-0.0).cmp(&d(-0.0)), Equal)
|
||||
}
|
||||
#[test]
|
||||
fn case64_c3_2() {
|
||||
assert_eq!(d(0.0).cmp(&d(0.0)), Equal)
|
||||
}
|
||||
#[test]
|
||||
fn case64_c3_3() {
|
||||
assert_eq!(d(1.0).cmp(&d(1.0)), Equal)
|
||||
}
|
||||
#[test]
|
||||
fn case64_c3_4() {
|
||||
assert_eq!(d(-1.0).cmp(&d(-1.0)), Equal)
|
||||
}
|
||||
#[test]
|
||||
fn case64_c3_5() {
|
||||
assert_eq!(d(-1e32).cmp(&d(-1e32)), Equal)
|
||||
}
|
||||
#[test]
|
||||
fn case64_c3_6() {
|
||||
assert_eq!(d(1e33).cmp(&d(1e33)), Equal)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod value_tests {
|
||||
use super::dom::Dom;
|
||||
use crate::value::{repr::Record, signed_integer::SignedInteger, PlainValue, Value};
|
||||
|
||||
type VV = Value<PlainValue<Dom>>;
|
||||
|
||||
#[test]
|
||||
fn boolean_mut() {
|
||||
let mut b = VV::Boolean(true);
|
||||
assert!(b.is_boolean());
|
||||
*(b.as_boolean_mut().unwrap()) = false;
|
||||
assert_eq!(b, VV::Boolean(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_mut() {
|
||||
let mut f = VV::from(1.0);
|
||||
assert!(f.is_f64());
|
||||
*(f.as_f64_mut().unwrap()) = 123.45;
|
||||
assert_eq!(f, VV::from(123.45));
|
||||
assert_eq!(
|
||||
(f.as_f64().unwrap() - 123.45).abs() < std::f64::EPSILON,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signedinteger_mut() {
|
||||
let mut i = VV::from(123);
|
||||
assert!(i.is_signedinteger());
|
||||
*(i.as_signedinteger_mut().unwrap()) = SignedInteger::from(234i128);
|
||||
assert_eq!(i, VV::from(234));
|
||||
assert_eq!(i.as_i().unwrap(), 234);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_mut() {
|
||||
let mut s = VV::from("hello, world!");
|
||||
assert!(s.is_string());
|
||||
s.as_string_mut().unwrap().replace_range(7..12, "there");
|
||||
assert_eq!(s, VV::from("hello, there!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_mut() {
|
||||
let mut b = VV::from(&b"hello, world!"[..]);
|
||||
assert!(b.is_bytestring());
|
||||
b.as_bytestring_mut()
|
||||
.unwrap()
|
||||
.splice(7..12, Vec::from(&b"there"[..]));
|
||||
assert_eq!(b, VV::from(&b"hello, there!"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_mut() {
|
||||
let mut s = VV::symbol("abcd");
|
||||
assert!(s.is_symbol());
|
||||
s.as_symbol_mut().unwrap().replace_range(..2, "AB");
|
||||
assert_eq!(s, VV::symbol("ABcd"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_mut() {
|
||||
let says = VV::symbol("says").wrap();
|
||||
let mut r = VV::Record(Record(vec![
|
||||
says.clone(),
|
||||
VV::from("Tony").wrap(),
|
||||
VV::from("Hello!").wrap(),
|
||||
]));
|
||||
assert_eq!(r.as_record_mut(Some(0)), None);
|
||||
assert_eq!(r.as_record_mut(Some(1)), None);
|
||||
assert!(r.as_record_mut(Some(2)).is_some());
|
||||
assert_eq!(r.as_record_mut(Some(3)), None);
|
||||
r.as_record_mut(None).unwrap().fields_mut()[0] = VV::from("Alice").wrap();
|
||||
assert_eq!(
|
||||
r,
|
||||
VV::Record(Record(vec![
|
||||
says,
|
||||
VV::from("Alice").wrap(),
|
||||
VV::from("Hello!").wrap()
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sequence_mut() {
|
||||
let mut s = VV::Sequence(vec![
|
||||
VV::from(1).wrap(),
|
||||
VV::from(2).wrap(),
|
||||
VV::from(3).wrap(),
|
||||
]);
|
||||
let r = VV::Sequence(vec![
|
||||
VV::from(1).wrap(),
|
||||
VV::from(99).wrap(),
|
||||
VV::from(3).wrap(),
|
||||
]);
|
||||
s.as_sequence_mut().unwrap()[1] = VV::from(99).wrap();
|
||||
assert_eq!(r, s);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod decoder_tests {
|
||||
use crate::de::from_bytes;
|
||||
use crate::error::{is_eof_io_error, Error, ExpectedKind};
|
||||
use crate::value::{BinarySource, BytesBinarySource, ConfiguredReader, NestedValue, Value};
|
||||
|
||||
fn expect_number_out_of_range<T: core::fmt::Debug>(r: Result<T, Error>) {
|
||||
match r {
|
||||
Ok(v) => panic!("Expected NumberOutOfRange, but got a parse of {:?}", v),
|
||||
Err(Error::NumberOutOfRange(_)) => (),
|
||||
Err(e) => panic!("Expected NumberOutOfRange, but got an error of {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_expected<T: core::fmt::Debug>(k: ExpectedKind, r: Result<T, Error>) {
|
||||
match r {
|
||||
Ok(v) => panic!("Expected Expected({:?}), but got a parse of {:?}", k, v),
|
||||
Err(Error::Expected(k1, _)) if k1 == k => (),
|
||||
Err(e) => panic!("Expected Expected({:?}), but got an error of {:?}", k, e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_annotations_noskip() {
|
||||
let buf = &b"\x85\xB0\x01\x02\xB0\x01\x01"[..];
|
||||
let mut src = BytesBinarySource::new(&buf);
|
||||
let mut d = ConfiguredReader::new(src.packed_iovalues());
|
||||
let v = d.demand_next().unwrap();
|
||||
assert_eq!(v.annotations().slice().len(), 1);
|
||||
assert_eq!(v.annotations().slice()[0], Value::from(2).wrap());
|
||||
assert_eq!(v.value(), &Value::from(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_annotations_skip() {
|
||||
let buf = &b"\x85\xB0\x01\x02\xB0\x01\x01"[..];
|
||||
let mut src = BytesBinarySource::new(&buf);
|
||||
let mut d = ConfiguredReader::new(src.packed_iovalues());
|
||||
d.set_read_annotations(false);
|
||||
let v = d.demand_next().unwrap();
|
||||
assert_eq!(v.annotations().slice().len(), 0);
|
||||
assert_eq!(v.value(), &Value::from(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_values_buf_advanced() {
|
||||
let buf = &b"\xb4\xb3\x04Ping\x84\xb4\xb3\x04Pong\x84"[..];
|
||||
assert_eq!(buf.len(), 16);
|
||||
let mut src = BytesBinarySource::new(&buf);
|
||||
let mut d = ConfiguredReader::new(src.packed_iovalues());
|
||||
assert_eq!(d.reader.source.index, 0);
|
||||
assert_eq!(
|
||||
d.demand_next().unwrap().value(),
|
||||
&Value::simple_record0("Ping")
|
||||
);
|
||||
assert_eq!(d.reader.source.index, 8);
|
||||
assert_eq!(
|
||||
d.demand_next().unwrap().value(),
|
||||
&Value::simple_record0("Pong")
|
||||
);
|
||||
assert_eq!(d.reader.source.index, 16);
|
||||
assert!(d.next().is_none());
|
||||
assert!(if let Err(e) = d.demand_next() {
|
||||
is_eof_io_error(&e)
|
||||
} else {
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_i8_format_a_positive() {
|
||||
assert_eq!(from_bytes::<i8>(b"\xB0\x01\x01").unwrap(), 1)
|
||||
}
|
||||
#[test]
|
||||
fn direct_i8_format_a_zero() {
|
||||
assert_eq!(from_bytes::<i8>(b"\xB0\x00").unwrap(), 0)
|
||||
}
|
||||
#[test]
|
||||
fn direct_i8_format_a_negative() {
|
||||
assert_eq!(from_bytes::<i8>(b"\xB0\x01\xff").unwrap(), -1)
|
||||
}
|
||||
#[test]
|
||||
fn direct_i8_format_b() {
|
||||
assert_eq!(from_bytes::<i8>(b"\xb0\x01\xfe").unwrap(), -2)
|
||||
}
|
||||
#[test]
|
||||
fn direct_i8_format_b_too_long() {
|
||||
assert_eq!(from_bytes::<i8>(b"\xb0\x03\xff\xff\xfe").unwrap(), -2)
|
||||
}
|
||||
#[test]
|
||||
fn direct_i8_format_b_much_too_long() {
|
||||
assert_eq!(
|
||||
from_bytes::<i8>(b"\xb0\x0a\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe").unwrap(),
|
||||
-2
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_u8_format_a_positive() {
|
||||
assert_eq!(from_bytes::<u8>(b"\xB0\x01\x01").unwrap(), 1)
|
||||
}
|
||||
#[test]
|
||||
fn direct_u8_format_a_zero() {
|
||||
assert_eq!(from_bytes::<u8>(b"\xB0\x00").unwrap(), 0)
|
||||
}
|
||||
#[test]
|
||||
fn direct_u8_format_b() {
|
||||
assert_eq!(from_bytes::<u8>(b"\xb0\x011").unwrap(), 49)
|
||||
}
|
||||
#[test]
|
||||
fn direct_u8_format_b_too_long() {
|
||||
assert_eq!(from_bytes::<u8>(b"\xb0\x04\0\0\01").unwrap(), 49)
|
||||
}
|
||||
#[test]
|
||||
fn direct_u8_format_b_much_too_long() {
|
||||
assert_eq!(from_bytes::<u8>(b"\xb0\x0a\0\0\0\0\0\0\0\0\01").unwrap(), 49)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_i16_format_a() {
|
||||
assert_eq!(from_bytes::<i16>(b"\xB0\x01\xfe").unwrap(), -2)
|
||||
}
|
||||
#[test]
|
||||
fn direct_i16_format_b() {
|
||||
assert_eq!(from_bytes::<i16>(b"\xb0\x02\xfe\xff").unwrap(), -257)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_u8_wrong_format() {
|
||||
expect_expected(
|
||||
ExpectedKind::SignedInteger,
|
||||
from_bytes::<u8>(b"\xb1\x05bogus"),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_u8_format_b_too_large() {
|
||||
expect_number_out_of_range(from_bytes::<u8>(b"\xb0\x04\0\011"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_i8_format_b_too_large() {
|
||||
expect_number_out_of_range(from_bytes::<i8>(b"\xb0\x02\xfe\xff"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_i16_format_b_too_large() {
|
||||
expect_number_out_of_range(from_bytes::<i16>(b"\xb0\x03\xfe\xff\xff"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_i32_format_b_ok() {
|
||||
assert_eq!(from_bytes::<i32>(b"\xb0\x03\xfe\xff\xff").unwrap(), -65537);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_i32_format_b_ok_2() {
|
||||
assert_eq!(
|
||||
from_bytes::<i32>(b"\xb0\x04\xfe\xff\xff\xff").unwrap(),
|
||||
-16777217
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_i64_format_b() {
|
||||
assert_eq!(from_bytes::<i64>(b"\xb0\x01\xff").unwrap(), -1);
|
||||
assert_eq!(from_bytes::<i64>(b"\xb0\x03\xff\xff\xff").unwrap(), -1);
|
||||
assert_eq!(
|
||||
from_bytes::<i64>(b"\xb0\x0a\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff").unwrap(),
|
||||
-1
|
||||
);
|
||||
assert_eq!(from_bytes::<i64>(b"\xb0\x01\xfe").unwrap(), -2);
|
||||
assert_eq!(from_bytes::<i64>(b"\xb0\x03\xff\xfe\xff").unwrap(), -257);
|
||||
assert_eq!(from_bytes::<i64>(b"\xb0\x03\xfe\xff\xff").unwrap(), -65537);
|
||||
assert_eq!(
|
||||
from_bytes::<i64>(b"\xb0\x0a\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff").unwrap(),
|
||||
-16777217
|
||||
);
|
||||
assert_eq!(
|
||||
from_bytes::<i64>(b"\xb0\x0a\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff").unwrap(),
|
||||
-72057594037927937
|
||||
);
|
||||
expect_number_out_of_range(from_bytes::<i64>(
|
||||
b"\xb0\x0a\xff\xff\x0e\xff\xff\xff\xff\xff\xff\xff",
|
||||
));
|
||||
expect_number_out_of_range(from_bytes::<i64>(
|
||||
b"\xb0\x09\xff\x0e\xff\xff\xff\xff\xff\xff\xff",
|
||||
));
|
||||
expect_number_out_of_range(from_bytes::<i64>(
|
||||
b"\xb0\x09\x80\x0e\xff\xff\xff\xff\xff\xff\xff",
|
||||
));
|
||||
expect_number_out_of_range(from_bytes::<i64>(
|
||||
b"\xb0\x0a\xff\x00\x0e\xff\xff\xff\xff\xff\xff\xff",
|
||||
));
|
||||
assert_eq!(
|
||||
from_bytes::<i64>(b"\xb0\x08\xfe\xff\xff\xff\xff\xff\xff\xff").unwrap(),
|
||||
-72057594037927937
|
||||
);
|
||||
assert_eq!(
|
||||
from_bytes::<i64>(b"\xb0\x08\x0e\xff\xff\xff\xff\xff\xff\xff").unwrap(),
|
||||
1080863910568919039
|
||||
);
|
||||
assert_eq!(
|
||||
from_bytes::<i64>(b"\xb0\x08\x80\0\0\0\0\0\0\0").unwrap(),
|
||||
-9223372036854775808
|
||||
);
|
||||
assert_eq!(from_bytes::<i64>(b"\xb0\x08\0\0\0\0\0\0\0\0").unwrap(), 0);
|
||||
assert_eq!(from_bytes::<i64>(b"\xB0\x00").unwrap(), 0);
|
||||
assert_eq!(
|
||||
from_bytes::<i64>(b"\xb0\x08\x7f\xff\xff\xff\xff\xff\xff\xff").unwrap(),
|
||||
9223372036854775807
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_u64_format_b() {
|
||||
expect_number_out_of_range(from_bytes::<u64>(b"\xb0\x01\xff"));
|
||||
assert_eq!(from_bytes::<u64>(b"\xb0\x02\0\xff").unwrap(), 255);
|
||||
expect_number_out_of_range(from_bytes::<u64>(b"\xb0\x03\xff\xff\xff"));
|
||||
assert_eq!(from_bytes::<u64>(b"\xb0\x04\0\xff\xff\xff").unwrap(), 0xffffff);
|
||||
expect_number_out_of_range(from_bytes::<u64>(
|
||||
b"\xb0\x0a\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
|
||||
));
|
||||
assert_eq!(from_bytes::<u64>(b"\xb0\x01\x02").unwrap(), 2);
|
||||
assert_eq!(from_bytes::<u64>(b"\xb0\x03\x00\x01\x00").unwrap(), 256);
|
||||
assert_eq!(from_bytes::<u64>(b"\xb0\x03\x01\x00\x00").unwrap(), 65536);
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x0a\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00").unwrap(),
|
||||
16777216
|
||||
);
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x0a\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00").unwrap(),
|
||||
72057594037927936
|
||||
);
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x0a\x00\x00\xf2\x00\x00\x00\x00\x00\x00\x00").unwrap(),
|
||||
0xf200000000000000
|
||||
);
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x0a\x00\x00\x72\x00\x00\x00\x00\x00\x00\x00").unwrap(),
|
||||
0x7200000000000000
|
||||
);
|
||||
expect_number_out_of_range(from_bytes::<u64>(
|
||||
b"\xb0\x0a\x00\xf2\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
));
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x09\x00\xf2\x00\x00\x00\x00\x00\x00\x00").unwrap(),
|
||||
0xf200000000000000
|
||||
);
|
||||
expect_number_out_of_range(from_bytes::<u64>(
|
||||
b"\xb0\x09\x7f\xf2\x00\x00\x00\x00\x00\x00\x00",
|
||||
));
|
||||
expect_number_out_of_range(from_bytes::<u64>(
|
||||
b"\xb0\x0a\x00\xff\xf2\x00\x00\x00\x00\x00\x00\x00",
|
||||
));
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x08\x01\x00\x00\x00\x00\x00\x00\x00").unwrap(),
|
||||
72057594037927936
|
||||
);
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x08\x0e\xff\xff\xff\xff\xff\xff\xff").unwrap(),
|
||||
1080863910568919039
|
||||
);
|
||||
expect_number_out_of_range(from_bytes::<u64>(b"\xb0\x08\x80\0\0\0\0\0\0\0"));
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x09\0\x80\0\0\0\0\0\0\0").unwrap(),
|
||||
9223372036854775808
|
||||
);
|
||||
assert_eq!(from_bytes::<u64>(b"\xb0\x08\0\0\0\0\0\0\0\0").unwrap(), 0);
|
||||
assert_eq!(from_bytes::<u64>(b"\xB0\x00").unwrap(), 0);
|
||||
assert_eq!(
|
||||
from_bytes::<u64>(b"\xb0\x08\x7f\xff\xff\xff\xff\xff\xff\xff").unwrap(),
|
||||
9223372036854775807
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod formatting_tests {
|
||||
use super::dom::Dom;
|
||||
use super::value::ArcValue;
|
||||
use super::value::IOValue;
|
||||
use super::value::Value;
|
||||
|
||||
#[test]
|
||||
fn format_debug_and_parse() {
|
||||
let v = "[1, {z: 2, a: #!\"One\"}, 3]"
|
||||
.parse::<Value<ArcValue<Dom>>>()
|
||||
.unwrap();
|
||||
assert_eq!(format!("{:?}", &v), "[1, {a: #!\"One\", z: 2}, 3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_pretty_debug_and_parse() {
|
||||
let v = "[1, {z: 2, a: #!\"One\"}, 3]"
|
||||
.parse::<Value<ArcValue<Dom>>>()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
format!("{:#?}", &v),
|
||||
concat!(
|
||||
"[\n",
|
||||
" 1,\n",
|
||||
" {\n",
|
||||
" a: #!\"One\",\n",
|
||||
" z: 2\n",
|
||||
" },\n",
|
||||
" 3\n",
|
||||
"]"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iovalue_parse() {
|
||||
let v = "[1 @{a:b c:d} @\"foo\" #![2 3] 4]"
|
||||
.parse::<IOValue>()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
format!("{:#?}", &v),
|
||||
concat!(
|
||||
"[\n",
|
||||
" 1,\n",
|
||||
" @{\n",
|
||||
" a: b,\n",
|
||||
" c: d\n",
|
||||
" } @\"foo\" #![\n",
|
||||
" 2,\n",
|
||||
" 3\n",
|
||||
" ],\n",
|
||||
" 4\n",
|
||||
"]"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod serde_tests {
|
||||
use crate::de::from_bytes as deserialize_from_bytes;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::value::de::from_value as deserialize_from_value;
|
||||
use crate::value::packed::PackedWriter;
|
||||
use crate::value::to_value;
|
||||
use crate::value::{IOValue, Map, Set, Value};
|
||||
|
||||
#[test]
|
||||
fn simple_to_value() {
|
||||
use serde::Serialize;
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
struct Colour {
|
||||
red: u8,
|
||||
green: u8,
|
||||
blue: u8,
|
||||
}
|
||||
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
struct SimpleValue<'a>(
|
||||
String,
|
||||
#[serde(with = "crate::symbol")] String,
|
||||
Symbol,
|
||||
#[serde(with = "crate::symbol")] String,
|
||||
Symbol,
|
||||
&'a str,
|
||||
#[serde(with = "serde_bytes")] &'a [u8],
|
||||
#[serde(with = "serde_bytes")] Vec<u8>,
|
||||
Vec<bool>,
|
||||
#[serde(with = "crate::set")] Set<String>,
|
||||
i16,
|
||||
IOValue,
|
||||
Map<String, Colour>,
|
||||
f32,
|
||||
f64,
|
||||
);
|
||||
let mut str_set = Set::new();
|
||||
str_set.insert("one".to_owned());
|
||||
str_set.insert("two".to_owned());
|
||||
str_set.insert("three".to_owned());
|
||||
let mut colours = Map::new();
|
||||
colours.insert(
|
||||
"red".to_owned(),
|
||||
Colour {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
},
|
||||
);
|
||||
colours.insert(
|
||||
"green".to_owned(),
|
||||
Colour {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0,
|
||||
},
|
||||
);
|
||||
colours.insert(
|
||||
"blue".to_owned(),
|
||||
Colour {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255,
|
||||
},
|
||||
);
|
||||
let v = SimpleValue(
|
||||
"hello".to_string(),
|
||||
"sym1".to_string(),
|
||||
Symbol("sym2".to_string()),
|
||||
"sym3".to_string(),
|
||||
Symbol("sym4".to_string()),
|
||||
"world",
|
||||
&b"slice"[..],
|
||||
b"vec".to_vec(),
|
||||
vec![false, true, false, true],
|
||||
str_set,
|
||||
12345,
|
||||
Value::from("hi").wrap(),
|
||||
colours,
|
||||
12.345f32,
|
||||
12.3456789,
|
||||
);
|
||||
println!("== v: {:#?}", v);
|
||||
let w: IOValue = to_value(&v);
|
||||
println!("== w: {:#?}", w);
|
||||
let x = deserialize_from_value(&w).unwrap();
|
||||
println!("== x: {:#?}", &x);
|
||||
assert_eq!(v, x);
|
||||
|
||||
let expected_bytes = vec![
|
||||
0xb4, // Struct
|
||||
0xb3, 0x0b, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75,
|
||||
0x65, // SimpleValue
|
||||
0xb1, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, // "hello"
|
||||
0xb3, 0x04, 0x73, 0x79, 0x6d, 0x31, // sym1
|
||||
0xb3, 0x04, 0x73, 0x79, 0x6d, 0x32, // sym2
|
||||
0xb3, 0x04, 0x73, 0x79, 0x6d, 0x33, // sym3
|
||||
0xb3, 0x04, 0x73, 0x79, 0x6d, 0x34, // sym4
|
||||
0xb1, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, // "world"
|
||||
0xb2, 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, // #"slice"
|
||||
0xb2, 0x03, 0x76, 0x65, 0x63, // #"vec"
|
||||
0xb5, // Sequence
|
||||
0x80, // false
|
||||
0x81, // true
|
||||
0x80, // false
|
||||
0x81, // true
|
||||
0x84, 0xb6, // Set
|
||||
0xb1, 0x03, 0x6f, 0x6e, 0x65, 0xb1, 0x03, 0x74, 0x77, 0x6f, 0xb1, 0x05, 0x74, 0x68,
|
||||
0x72, 0x65, 0x65, 0x84, 0xb0, 0x02, 0x30, 0x39, // 12345
|
||||
0xb1, 0x02, 0x68, 0x69, // "hi"
|
||||
0xb7, // Dictionary
|
||||
0xb1, 0x03, 0x72, 0x65, 0x64, // "red"
|
||||
0xb4, 0xb3, 0x06, 0x43, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0xb0, 0x02, 0x00, 0xff, 0xb0, 0x00, 0xb0, 0x00,
|
||||
0x84, 0xb1, 0x04, 0x62, 0x6c, 0x75, 0x65, // "blue"
|
||||
0xb4, 0xb3, 0x06, 0x43, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0xb0, 0x00, 0xb0, 0x00, 0xb0, 0x02, 0x00, 0xff,
|
||||
0x84, 0xb1, 0x05, 0x67, 0x72, 0x65, 0x65, 0x6e, // "green"
|
||||
0xb4, 0xb3, 0x06, 0x43, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0xb0, 0x00, 0xb0, 0x02, 0x00, 0xff, 0xb0, 0x00,
|
||||
0x84, 0x84,
|
||||
0x87, 0x08, 0x40, 0x28, 0xb0, 0xa3, 0xe0, 0x00, 0x00, 0x00, // 12.345f32
|
||||
0x87, 0x08, 0x40, 0x28, 0xb0, 0xfc, 0xd3, 0x24, 0xd5, 0xa2, // 12.3456789
|
||||
0x84,
|
||||
];
|
||||
|
||||
let y = deserialize_from_bytes(&expected_bytes).unwrap();
|
||||
println!("== y: {:#?}", &y);
|
||||
assert_eq!(v, y);
|
||||
|
||||
let v_bytes_1 = PackedWriter::encode_iovalue(&w).unwrap();
|
||||
println!("== w bytes = {:?}", v_bytes_1);
|
||||
assert_eq!(expected_bytes, v_bytes_1);
|
||||
|
||||
let mut v_bytes_2 = Vec::new();
|
||||
v.serialize(&mut crate::ser::Serializer::new(&mut PackedWriter::new(
|
||||
&mut v_bytes_2,
|
||||
)))
|
||||
.unwrap();
|
||||
println!("== v bytes = {:?}", v_bytes_2);
|
||||
assert_eq!(v_bytes_1, v_bytes_2);
|
||||
}
|
||||
}
|
|
@ -1,458 +0,0 @@
|
|||
//! Support for Serde serialization of Rust data types into Preserves terms.
|
||||
|
||||
use super::value::boundary as B;
|
||||
use super::value::writer::{CompoundWriter, Writer};
|
||||
use super::value::IOValueDomainCodec;
|
||||
use serde::Serialize;
|
||||
|
||||
pub use super::error::Error;
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Serde serializer for Preserves-encoding Rust data. Construct via [Serializer::new], and use
|
||||
/// with [serde::Serialize::serialize] methods.
|
||||
pub struct Serializer<'w, W: Writer> {
|
||||
/// The underlying Preserves [writer][crate::value::writer::Writer].
|
||||
pub write: &'w mut W,
|
||||
}
|
||||
|
||||
impl<'w, W: Writer> Serializer<'w, W> {
|
||||
/// Construct a new [Serializer] targetting the given
|
||||
/// [writer][crate::value::writer::Writer].
|
||||
pub fn new(write: &'w mut W) -> Self {
|
||||
Serializer { write }
|
||||
}
|
||||
}
|
||||
|
||||
enum SequenceVariant<W: Writer> {
|
||||
Sequence(W::SeqWriter),
|
||||
Record(W::RecWriter),
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeCompound<'a, 'w, W: Writer> {
|
||||
b: B::Type,
|
||||
i: B::Item,
|
||||
ser: &'a mut Serializer<'w, W>,
|
||||
c: SequenceVariant<W>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeDictionary<'a, 'w, W: Writer> {
|
||||
b: B::Type,
|
||||
ser: &'a mut Serializer<'w, W>,
|
||||
d: W::DictWriter,
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::Serializer for &'a mut Serializer<'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
type SerializeSeq = SerializeCompound<'a, 'w, W>;
|
||||
type SerializeTuple = SerializeCompound<'a, 'w, W>;
|
||||
type SerializeTupleStruct = SerializeCompound<'a, 'w, W>;
|
||||
type SerializeTupleVariant = SerializeCompound<'a, 'w, W>;
|
||||
type SerializeMap = SerializeDictionary<'a, 'w, W>;
|
||||
type SerializeStruct = SerializeCompound<'a, 'w, W>;
|
||||
type SerializeStructVariant = SerializeCompound<'a, 'w, W>;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_bool(v)?)
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_i8(v)?)
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_i16(v)?)
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_i32(v)?)
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_i64(v)?)
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_u8(v)?)
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_u16(v)?)
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_u32(v)?)
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_u64(v)?)
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_f64(v as f64)?)
|
||||
}
|
||||
|
||||
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_f64(v)?)
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<Self::Ok> {
|
||||
let mut c = self.write.start_record(Some(1))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol("UnicodeScalar")?;
|
||||
c.boundary(&B::mid(B::Item::RecordLabel, B::Item::RecordField))?;
|
||||
c.write_u32(v as u32)?;
|
||||
c.boundary(&B::end(B::Item::RecordField))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_string(v)?)
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
|
||||
Ok(self.write.write_bytes(v)?)
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok> {
|
||||
let mut c = self.write.start_record(Some(0))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol("None")?;
|
||||
c.boundary(&B::end(B::Item::RecordLabel))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, v: &T) -> Result<Self::Ok>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let mut c = self.write.start_record(Some(1))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol("Some")?;
|
||||
c.boundary(&B::mid(B::Item::RecordLabel, B::Item::RecordField))?;
|
||||
to_writer(&mut c, v)?;
|
||||
c.boundary(&B::end(B::Item::RecordField))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok> {
|
||||
let mut c = self.write.start_record(Some(0))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol("tuple")?;
|
||||
c.boundary(&B::end(B::Item::RecordLabel))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok> {
|
||||
let mut c = self.write.start_record(Some(0))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(name)?;
|
||||
c.boundary(&B::end(B::Item::RecordLabel))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
) -> Result<Self::Ok> {
|
||||
let mut c = self.write.start_record(Some(0))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(variant_name)?;
|
||||
c.boundary(&B::end(B::Item::RecordLabel))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T: ?Sized>(self, name: &'static str, value: &T) -> Result<Self::Ok>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match super::value::magic::receive_output_value(name, value) {
|
||||
Some(v) => Ok(self.write.write(&mut IOValueDomainCodec, &v)?),
|
||||
None => {
|
||||
// TODO: This is apparently discouraged, and we should apparently just serialize `value`?
|
||||
let mut c = self.write.start_record(Some(1))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(name)?;
|
||||
c.boundary(&B::mid(B::Item::RecordLabel, B::Item::RecordField))?;
|
||||
to_writer(&mut c, value)?;
|
||||
c.boundary(&B::end(B::Item::RecordField))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let mut c = self.write.start_record(Some(1))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(variant_name)?;
|
||||
c.boundary(&B::mid(B::Item::RecordLabel, B::Item::RecordField))?;
|
||||
to_writer(&mut c, value)?;
|
||||
c.boundary(&B::end(B::Item::RecordField))?;
|
||||
Ok(self.write.end_record(c)?)
|
||||
}
|
||||
|
||||
fn serialize_seq(self, count: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let c = self.write.start_sequence(count)?;
|
||||
Ok(SerializeCompound::seq(self, c))
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, count: usize) -> Result<Self::SerializeTuple> {
|
||||
let mut c = self.write.start_record(Some(count))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol("tuple")?;
|
||||
Ok(SerializeCompound::rec(self, c))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
count: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
let mut c = self.write.start_record(Some(count))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(name)?;
|
||||
Ok(SerializeCompound::rec(self, c))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
count: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
let mut c = self.write.start_record(Some(count))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(variant_name)?;
|
||||
Ok(SerializeCompound::rec(self, c))
|
||||
}
|
||||
|
||||
fn serialize_map(self, count: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
let d = self.write.start_dictionary(count)?;
|
||||
Ok(SerializeDictionary {
|
||||
b: B::Type::default(),
|
||||
ser: self,
|
||||
d,
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_struct(self, name: &'static str, count: usize) -> Result<Self::SerializeStruct> {
|
||||
let mut c = self.write.start_record(Some(count))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(name)?;
|
||||
Ok(SerializeCompound::rec(self, c))
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
count: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
let mut c = self.write.start_record(Some(count))?;
|
||||
c.boundary(&B::start(B::Item::RecordLabel))?;
|
||||
c.write_symbol(variant_name)?;
|
||||
Ok(SerializeCompound::rec(self, c))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::ser::SerializeMap for SerializeDictionary<'a, 'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.b.opening = Some(B::Item::DictionaryKey);
|
||||
self.d.boundary(&self.b)?;
|
||||
to_writer(&mut self.d, key)?;
|
||||
self.b.shift(None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.b.opening = Some(B::Item::DictionaryValue);
|
||||
self.d.boundary(&self.b)?;
|
||||
to_writer(&mut self.d, value)?;
|
||||
self.b.shift(None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(mut self) -> Result<Self::Ok> {
|
||||
self.d.boundary(&self.b)?;
|
||||
Ok(self.ser.write.end_dictionary(self.d)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> SerializeCompound<'a, 'w, W> {
|
||||
fn seq(ser: &'a mut Serializer<'w, W>, c: W::SeqWriter) -> Self {
|
||||
SerializeCompound {
|
||||
b: B::Type::default(),
|
||||
i: B::Item::SequenceValue,
|
||||
ser,
|
||||
c: SequenceVariant::Sequence(c),
|
||||
}
|
||||
}
|
||||
|
||||
fn rec(ser: &'a mut Serializer<'w, W>, c: W::RecWriter) -> Self {
|
||||
SerializeCompound {
|
||||
b: B::end(B::Item::RecordLabel),
|
||||
i: B::Item::RecordField,
|
||||
ser,
|
||||
c: SequenceVariant::Record(c),
|
||||
}
|
||||
}
|
||||
|
||||
fn extend<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.b.opening = Some(self.i.clone());
|
||||
match &mut self.c {
|
||||
SequenceVariant::Sequence(w) => {
|
||||
w.boundary(&self.b)?;
|
||||
to_writer(w, value)?;
|
||||
}
|
||||
SequenceVariant::Record(w) => {
|
||||
w.boundary(&self.b)?;
|
||||
to_writer(w, value)?;
|
||||
}
|
||||
}
|
||||
self.b.shift(None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn complete(self) -> Result<()> {
|
||||
match self.c {
|
||||
SequenceVariant::Sequence(mut w) => {
|
||||
w.boundary(&self.b)?;
|
||||
Ok(self.ser.write.end_sequence(w)?)
|
||||
}
|
||||
SequenceVariant::Record(mut w) => {
|
||||
w.boundary(&self.b)?;
|
||||
Ok(self.ser.write.end_record(w)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::ser::SerializeStruct for SerializeCompound<'a, 'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, _name: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.extend(value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
self.complete()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::ser::SerializeStructVariant for SerializeCompound<'a, 'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, _name: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.extend(value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
self.complete()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::ser::SerializeTuple for SerializeCompound<'a, 'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.extend(value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
self.complete()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::ser::SerializeTupleStruct for SerializeCompound<'a, 'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.extend(value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
self.complete()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::ser::SerializeTupleVariant for SerializeCompound<'a, 'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.extend(value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
self.complete()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, W: Writer> serde::ser::SerializeSeq for SerializeCompound<'a, 'w, W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.extend(value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
self.complete()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function for directly serializing a Serde-serializable `T` to the given
|
||||
/// `write`, a Preserves [writer][crate::value::writer::Writer].
|
||||
pub fn to_writer<W: Writer, T: Serialize + ?Sized>(write: &mut W, value: &T) -> Result<()> {
|
||||
Ok(value.serialize(&mut Serializer::new(write))?)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
//! Serde support for serializing Rust collections as Preserves sets.
|
||||
//!
|
||||
//! Serde doesn't include sets in its data model, so we do some somewhat awful tricks to force
|
||||
//! things to come out the way we want them.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! Annotate collection-valued fields that you want to (en|de)code as Preserves `Set`s with
|
||||
//! `#[serde(with = "preserves::set")]`:
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[derive(serde::Serialize, serde::Deserialize)]
|
||||
//! struct Example {
|
||||
//! #[serde(with = "preserves::set")]
|
||||
//! items: preserves::value::Set<String>,
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::value::{self, to_value, IOValue, UnwrappedIOValue};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::iter::IntoIterator;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn serialize<S, T, Item>(s: T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
T: IntoIterator<Item = Item>,
|
||||
Item: Serialize,
|
||||
{
|
||||
let s = s.into_iter().map(to_value).collect::<value::Set<IOValue>>();
|
||||
UnwrappedIOValue::from(s).wrap().serialize(serializer)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
// Relies on the way we hack around serde's data model in de.rs and value/de.rs.
|
||||
T::deserialize(deserializer)
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
//! Serde support for serializing Rust data as Preserves symbols.
|
||||
//!
|
||||
//! Serde doesn't include symbols in its data model, so we do some somewhat awful tricks to
|
||||
//! force things to come out the way we want them.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! Either use [Symbol] directly in your data types, or annotate [String]-valued fields that
|
||||
//! you want to (en|de)code as Preserves `Symbol`s with `#[serde(with = "preserves::symbol")]`:
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[derive(serde::Serialize, serde::Deserialize)]
|
||||
//! struct Example {
|
||||
//! sym1: preserves::symbol::Symbol,
|
||||
//! #[serde(with = "preserves::symbol")]
|
||||
//! sym2: String,
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::value::{IOValue, NestedValue};
|
||||
|
||||
/// Wrapper for a string to coerce its Preserves-serialization to `Symbol`.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct Symbol(pub String);
|
||||
|
||||
impl serde::Serialize for Symbol {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
IOValue::symbol(&self.0).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Symbol {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let v = IOValue::deserialize(deserializer)?;
|
||||
let s = v
|
||||
.value()
|
||||
.as_symbol()
|
||||
.ok_or_else(|| serde::de::Error::custom("Expected symbol"))?;
|
||||
Ok(Symbol(s.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn serialize<S>(s: &str, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::Serialize;
|
||||
Symbol(s.to_string()).serialize(serializer)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::Deserialize;
|
||||
Symbol::deserialize(deserializer).map(|v| v.0)
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#![doc(hidden)]
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Type {
|
||||
pub closing: Option<Item>,
|
||||
pub opening: Option<Item>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Item {
|
||||
Annotation,
|
||||
AnnotatedValue,
|
||||
DictionaryKey,
|
||||
DictionaryValue,
|
||||
RecordField,
|
||||
RecordLabel,
|
||||
SequenceValue,
|
||||
SetValue,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
#[inline]
|
||||
pub fn shift(&mut self, i: Option<Item>) {
|
||||
let tmp = std::mem::replace(&mut self.opening, i);
|
||||
self.closing = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(i: Item) -> Type {
|
||||
Type {
|
||||
closing: None,
|
||||
opening: Some(i),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mid(c: Item, o: Item) -> Type {
|
||||
Type {
|
||||
closing: Some(c),
|
||||
opening: Some(o),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(i: Item) -> Type {
|
||||
Type {
|
||||
closing: Some(i),
|
||||
opening: None,
|
||||
}
|
||||
}
|
|
@ -1,465 +0,0 @@
|
|||
//! Support Serde deserialization of Rust data types from Preserves *values* (not syntax).
|
||||
|
||||
use crate::error::{Error, ExpectedKind, Received};
|
||||
use crate::value::repr::Double;
|
||||
use crate::value::{IOValue, Map, NestedValue, UnwrappedIOValue, Value};
|
||||
use serde::de::{DeserializeSeed, EnumAccess, MapAccess, SeqAccess, VariantAccess, Visitor};
|
||||
use serde::Deserialize;
|
||||
use std::iter::Iterator;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Serde deserializer for constructing Rust data from an in-memory Preserves value. Use
|
||||
/// [Deserializer::from_value] to construct instances, or [from_value] to deserialize single
|
||||
/// values directly.
|
||||
pub struct Deserializer<'de> {
|
||||
input: &'de IOValue,
|
||||
}
|
||||
|
||||
/// Deserialize a `T` from `v`, a Preserves [IOValue].
|
||||
pub fn from_value<'a, T>(v: &'a IOValue) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'a>,
|
||||
{
|
||||
let mut de = Deserializer::from_value(v);
|
||||
let t = T::deserialize(&mut de)?;
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> {
|
||||
/// Construct a Deserializer from `v`, an [IOValue].
|
||||
pub fn from_value(v: &'de IOValue) -> Self {
|
||||
Deserializer { input: v }
|
||||
}
|
||||
|
||||
fn check<'a, T, F>(&'a mut self, f: F, k: ExpectedKind) -> Result<T>
|
||||
where
|
||||
F: FnOnce(&'de UnwrappedIOValue) -> Option<T>,
|
||||
{
|
||||
f(self.input.value()).ok_or_else(|| {
|
||||
Error::Expected(k, Received::ReceivedOtherValue(format!("{:?}", self.input)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.input.value();
|
||||
match v {
|
||||
Value::Boolean(b) => visitor.visit_bool(*b),
|
||||
Value::Double(Double(d)) => visitor.visit_f64(*d),
|
||||
Value::String(ref s) => visitor.visit_str(&s),
|
||||
Value::ByteString(_) => self.deserialize_bytes(visitor),
|
||||
Value::Record(_) => {
|
||||
if v.is_simple_record("tuple", Some(0)) {
|
||||
self.deserialize_unit(visitor)
|
||||
} else if v.is_simple_record("UnicodeScalar", Some(1)) {
|
||||
self.deserialize_char(visitor)
|
||||
} else if v.is_simple_record("None", Some(0)) || v.is_simple_record("Some", Some(1))
|
||||
{
|
||||
self.deserialize_option(visitor)
|
||||
} else if v.is_simple_record("tuple", None) {
|
||||
visitor.visit_seq(VecSeq::new(
|
||||
self,
|
||||
v.as_simple_record("tuple", None).unwrap().iter(),
|
||||
))
|
||||
} else {
|
||||
Err(Error::CannotDeserializeAny)
|
||||
}
|
||||
}
|
||||
Value::Sequence(ref v) => visitor.visit_seq(VecSeq::new(self, v.iter())),
|
||||
Value::Dictionary(ref d) => visitor.visit_map(DictMap::new(self, d)),
|
||||
_ => match v.as_i64() {
|
||||
Some(i) => visitor.visit_i64(i),
|
||||
None => match v.as_u64() {
|
||||
Some(u) => visitor.visit_u64(u),
|
||||
None => match v.as_signedinteger() {
|
||||
Some(n) => Err(Error::NumberOutOfRange(n.into())),
|
||||
None => Err(Error::CannotDeserializeAny),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_bool(self.input.value().to_boolean()?)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i8(self.input.value().to_i8()?)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i16(self.input.value().to_i16()?)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i32(self.input.value().to_i32()?)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i64(self.input.value().to_i64()?)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u8(self.input.value().to_u8()?)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u16(self.input.value().to_u16()?)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u32(self.input.value().to_u32()?)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u64(self.input.value().to_u64()?)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f32(self.input.value().to_f64()? as f32)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f64(self.input.value().to_f64()?)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_char(self.input.value().to_char()?)
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let s: &'de str = &self.input.value().to_string()?;
|
||||
visitor.visit_borrowed_str(s)
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let bs: &'de [u8] = &self.input.value().to_bytestring()?;
|
||||
visitor.visit_borrowed_bytes(bs)
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_byte_buf(self.input.value().to_bytestring()?.clone())
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.input.value().to_option()? {
|
||||
None => visitor.visit_none(),
|
||||
Some(v) => {
|
||||
self.input = v;
|
||||
visitor.visit_some(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let _fs = self.input.value().to_simple_record("tuple", Some(0))?;
|
||||
visitor.visit_unit()
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let _fs = self.input.value().to_simple_record(name, Some(0))?;
|
||||
visitor.visit_unit()
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match super::magic::transmit_input_value(name, || Ok(self.input.clone()))? {
|
||||
Some(v) => visitor.visit_u64(v),
|
||||
None => {
|
||||
let fs = self.input.value().to_simple_record(name, Some(1))?;
|
||||
self.input = &fs[0];
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.input.value().as_sequence() {
|
||||
Some(vs) => visitor.visit_seq(VecSeq::new(self, vs.iter())),
|
||||
None => {
|
||||
// Hack around serde's model: Deserialize *sets* as
|
||||
// sequences, too, and reconstruct them as Rust Sets
|
||||
// on the visitor side.
|
||||
visitor.visit_seq(VecSeq::new(self, self.input.value().to_set()?.iter()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let fs = self.input.value().to_simple_record("tuple", Some(len))?;
|
||||
visitor.visit_seq(VecSeq::new(self, fs.iter()))
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let fs = self.input.value().to_simple_record(name, Some(len))?;
|
||||
visitor.visit_seq(VecSeq::new(self, fs.iter()))
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let d = self.input.value().to_dictionary()?;
|
||||
visitor.visit_map(DictMap::new(self, d))
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let fs = self
|
||||
.input
|
||||
.value()
|
||||
.to_simple_record(name, Some(fields.len()))?;
|
||||
visitor.visit_seq(VecSeq::new(self, fs.iter()))
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_enum(self)
|
||||
}
|
||||
|
||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_str(&self.input.value().to_symbol()?)
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct VecSeq<'a, 'de: 'a, I: Iterator<Item = &'de IOValue>> {
|
||||
iter: I,
|
||||
de: &'a mut Deserializer<'de>,
|
||||
}
|
||||
|
||||
impl<'de, 'a, I: Iterator<Item = &'de IOValue>> VecSeq<'a, 'de, I> {
|
||||
fn new(de: &'a mut Deserializer<'de>, iter: I) -> Self {
|
||||
VecSeq { iter, de }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a, I: Iterator<Item = &'de IOValue>> SeqAccess<'de> for VecSeq<'a, 'de, I> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
match self.iter.next() {
|
||||
None => Ok(None),
|
||||
Some(v) => {
|
||||
self.de.input = v;
|
||||
Ok(Some(seed.deserialize(&mut *self.de)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct DictMap<'a, 'de: 'a> {
|
||||
pending: Option<&'de IOValue>,
|
||||
iter: Box<dyn Iterator<Item = (&'de IOValue, &'de IOValue)> + 'a>,
|
||||
de: &'a mut Deserializer<'de>,
|
||||
}
|
||||
|
||||
impl<'de, 'a> DictMap<'a, 'de> {
|
||||
fn new(de: &'a mut Deserializer<'de>, d: &'de Map<IOValue, IOValue>) -> Self {
|
||||
DictMap {
|
||||
pending: None,
|
||||
iter: Box::new(d.iter()),
|
||||
de,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> MapAccess<'de> for DictMap<'a, 'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
|
||||
where
|
||||
K: DeserializeSeed<'de>,
|
||||
{
|
||||
match self.iter.next() {
|
||||
None => Ok(None),
|
||||
Some((k, v)) => {
|
||||
self.pending = Some(v);
|
||||
self.de.input = k;
|
||||
Ok(Some(seed.deserialize(&mut *self.de)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
let v = self.pending.unwrap();
|
||||
self.pending = None;
|
||||
self.de.input = v;
|
||||
Ok(seed.deserialize(&mut *self.de)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'de> EnumAccess<'de> for &'a mut Deserializer<'de> {
|
||||
type Error = Error;
|
||||
type Variant = Self;
|
||||
|
||||
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
let r = self.check(|v| v.as_record(None), ExpectedKind::Record(None))?;
|
||||
let v = self.input;
|
||||
self.input = r.label();
|
||||
let variant = seed.deserialize(&mut *self)?;
|
||||
self.input = v;
|
||||
Ok((variant, self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'de> VariantAccess<'de> for &'a mut Deserializer<'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
let r = self.check(|v| v.as_record(Some(1)), ExpectedKind::Record(Some(1)))?;
|
||||
self.input = &r.fields()[0];
|
||||
seed.deserialize(&mut *self)
|
||||
}
|
||||
|
||||
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_seq(VecSeq::new(
|
||||
self,
|
||||
self.input.value().as_record(None).unwrap().fields().iter(),
|
||||
))
|
||||
}
|
||||
|
||||
fn struct_variant<V>(self, fields: &'static [&'static str], visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_seq(VecSeq::new(
|
||||
self,
|
||||
self.input
|
||||
.value()
|
||||
.as_record(Some(fields.len()))
|
||||
.unwrap()
|
||||
.fields()
|
||||
.iter(),
|
||||
))
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
//! Traits for working with Preserves [embedded
|
||||
//! values](https://preserves.dev/preserves.html#embeddeds).
|
||||
|
||||
use std::io;
|
||||
|
||||
use super::packed;
|
||||
use super::BinarySource;
|
||||
use super::BytesBinarySource;
|
||||
use super::Embeddable;
|
||||
use super::IOValue;
|
||||
use super::NestedValue;
|
||||
use super::Reader;
|
||||
use super::Writer;
|
||||
|
||||
/// Implementations parse [IOValue]s to their own particular [Embeddable] values of type `D`.
|
||||
pub trait DomainParse<D: Embeddable> {
|
||||
fn parse_embedded(&mut self, v: &IOValue) -> io::Result<D>;
|
||||
}
|
||||
|
||||
/// Implementations read and parse from `src` to produce [Embeddable] values of type `D`.
|
||||
pub trait DomainDecode<D: Embeddable> {
|
||||
fn decode_embedded<'de, 'src, S: BinarySource<'de>>(
|
||||
&mut self,
|
||||
src: &'src mut S,
|
||||
read_annotations: bool,
|
||||
) -> io::Result<D>;
|
||||
}
|
||||
|
||||
/// Implementations unparse and write `D`s to `w`, a [writer][crate::value::writer::Writer].
|
||||
pub trait DomainEncode<D: Embeddable> {
|
||||
fn encode_embedded<W: Writer>(&mut self, w: &mut W, d: &D) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<'a, D: Embeddable, T: DomainParse<D>> DomainParse<D> for &'a mut T {
|
||||
fn parse_embedded(&mut self, v: &IOValue) -> io::Result<D> {
|
||||
(**self).parse_embedded(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: Embeddable, T: DomainDecode<D>> DomainDecode<D> for &'a mut T {
|
||||
fn decode_embedded<'de, 'src, S: BinarySource<'de>>(
|
||||
&mut self,
|
||||
src: &'src mut S,
|
||||
read_annotations: bool,
|
||||
) -> io::Result<D> {
|
||||
(**self).decode_embedded(src, read_annotations)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience codec: use this as embedded codec for encoding (only) when embedded values
|
||||
/// should be serialized as Preserves `String`s holding their Rust [std::fmt::Debug]
|
||||
/// representation.
|
||||
pub struct DebugDomainEncode;
|
||||
|
||||
impl<D: Embeddable> DomainEncode<D> for DebugDomainEncode {
|
||||
fn encode_embedded<W: Writer>(&mut self, w: &mut W, d: &D) -> io::Result<()> {
|
||||
d.debug_encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience codec: use this as embedded codec for decoding (only) when embedded values are
|
||||
/// expected to conform to the syntax implicit in their [std::str::FromStr] implementation.
|
||||
pub struct FromStrDomainParse;
|
||||
|
||||
impl<Err: Into<io::Error>, D: Embeddable + std::str::FromStr<Err = Err>> DomainParse<D>
|
||||
for FromStrDomainParse
|
||||
{
|
||||
fn parse_embedded(&mut self, v: &IOValue) -> io::Result<D> {
|
||||
Ok(D::from_str(v.value().to_string()?).map_err(|e| e.into())?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this as embedded codec when embedded data are already [IOValue]s that can be directly
|
||||
/// serialized and deserialized without further transformation.
|
||||
pub struct IOValueDomainCodec;
|
||||
|
||||
impl DomainDecode<IOValue> for IOValueDomainCodec {
|
||||
fn decode_embedded<'de, 'src, S: BinarySource<'de>>(
|
||||
&mut self,
|
||||
src: &'src mut S,
|
||||
read_annotations: bool,
|
||||
) -> io::Result<IOValue> {
|
||||
packed::PackedReader::new(src, IOValueDomainCodec).demand_next(read_annotations)
|
||||
}
|
||||
}
|
||||
|
||||
impl DomainEncode<IOValue> for IOValueDomainCodec {
|
||||
fn encode_embedded<W: Writer>(&mut self, w: &mut W, d: &IOValue) -> io::Result<()> {
|
||||
w.write(self, d)
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this as embedded codec to forbid use of embedded values; an [io::Error] is signalled.
|
||||
pub struct NoEmbeddedDomainCodec;
|
||||
|
||||
impl<D: Embeddable> DomainDecode<D> for NoEmbeddedDomainCodec {
|
||||
fn decode_embedded<'de, 'src, S: BinarySource<'de>>(
|
||||
&mut self,
|
||||
_src: &'src mut S,
|
||||
_read_annotations: bool,
|
||||
) -> io::Result<D> {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"Embedded values not supported here",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Embeddable> DomainEncode<D> for NoEmbeddedDomainCodec {
|
||||
fn encode_embedded<W: Writer>(&mut self, _w: &mut W, _d: &D) -> io::Result<()> {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"Embedded values not supported here",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// If some `C` implements [DomainDecode] but not [DomainParse], or vice versa, use `ViaCodec`
|
||||
/// to promote the one to the other. Construct instances with [ViaCodec::new].
|
||||
pub struct ViaCodec<C>(C);
|
||||
|
||||
impl<C> ViaCodec<C> {
|
||||
/// Constructs a `ViaCodec` wrapper around an underlying codec of type `C`.
|
||||
pub fn new(c: C) -> Self {
|
||||
ViaCodec(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Embeddable, C: DomainDecode<D>> DomainParse<D> for ViaCodec<C> {
|
||||
fn parse_embedded(&mut self, v: &IOValue) -> io::Result<D> {
|
||||
let bs = packed::PackedWriter::encode_iovalue(v)?;
|
||||
self.0
|
||||
.decode_embedded(&mut BytesBinarySource::new(&bs), true)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Embeddable, C: DomainParse<D>> DomainDecode<D> for ViaCodec<C> {
|
||||
fn decode_embedded<'de, 'src, S: BinarySource<'de>>(
|
||||
&mut self,
|
||||
src: &'src mut S,
|
||||
read_annotations: bool,
|
||||
) -> io::Result<D> {
|
||||
let v = src
|
||||
.packed(IOValueDomainCodec)
|
||||
.demand_next(read_annotations)?;
|
||||
self.0.parse_embedded(&v)
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
#![doc(hidden)]
|
||||
|
||||
//! A horrifying hack to Serde-serialize [IOValue] instances to Preserves *as themselves*.
|
||||
//!
|
||||
//! Frankly I think this portion of the codebase might not survive for long. I can't think of a
|
||||
//! better way of achieving this, but the drawbacks of having this functionality are *severe*.
|
||||
//!
|
||||
//! See <https://gitlab.com/preserves/preserves/-/issues/42>.
|
||||
|
||||
use super::repr::IOValue;
|
||||
|
||||
pub static MAGIC: &str = "$____Preserves_Serde_Magic";
|
||||
|
||||
struct IOValueVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for IOValueVisitor {
|
||||
type Value = IOValue;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "a magic encoding of an embedded Preserves Value")
|
||||
}
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
let b = unsafe { Box::from_raw(v as *mut IOValue) };
|
||||
Ok(*b)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn output_value<S: serde::Serializer>(serializer: S, v: IOValue) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_newtype_struct(MAGIC, &(Box::into_raw(Box::new(v)) as u64))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn input_value<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<IOValue, D::Error> {
|
||||
deserializer.deserialize_newtype_struct(MAGIC, IOValueVisitor)
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#[inline]
|
||||
pub fn receive_output_value<T: ?Sized>(name: &'static str, magic_value: &T) -> Option<IOValue> {
|
||||
if name == MAGIC {
|
||||
let b =
|
||||
unsafe { Box::from_raw(*((magic_value as *const T) as *const u64) as *mut IOValue) };
|
||||
let v: IOValue = *b;
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn transmit_input_value<F>(name: &'static str, f: F) -> Result<Option<u64>, crate::error::Error>
|
||||
where
|
||||
F: FnOnce() -> Result<IOValue, crate::error::Error>,
|
||||
{
|
||||
if name == MAGIC {
|
||||
let b: Box<IOValue> = Box::new(f()?);
|
||||
Ok(Some(Box::into_raw(b) as u64))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
//! Implements the Preserves
|
||||
//! [merge](https://preserves.dev/preserves.html#appendix-merging-values) of values.
|
||||
|
||||
use super::Map;
|
||||
use super::NestedValue;
|
||||
use super::Record;
|
||||
use super::Value;
|
||||
|
||||
/// Merge two sequences of values according to [the
|
||||
/// specification](https://preserves.dev/preserves.html#appendix-merging-values).
|
||||
pub fn merge_seqs<N: NestedValue>(mut a: Vec<N>, mut b: Vec<N>) -> Option<Vec<N>> {
|
||||
if a.len() > b.len() {
|
||||
std::mem::swap(&mut a, &mut b);
|
||||
}
|
||||
let mut r = vec![];
|
||||
let mut bi = b.into_iter();
|
||||
for av in a.into_iter() {
|
||||
r.push(merge2(av, bi.next().unwrap())?);
|
||||
}
|
||||
r.extend(bi);
|
||||
Some(r)
|
||||
}
|
||||
|
||||
/// Merge two values according to [the
|
||||
/// specification](https://preserves.dev/preserves.html#appendix-merging-values).
|
||||
pub fn merge2<N: NestedValue>(v: N, w: N) -> Option<N> {
|
||||
let (mut v_anns, v_val) = v.pieces();
|
||||
let (w_anns, w_val) = w.pieces();
|
||||
v_anns.modify(|anns| anns.extend(w_anns.to_vec().into_iter()));
|
||||
if v_val == w_val {
|
||||
Some(N::wrap(v_anns, v_val))
|
||||
} else {
|
||||
let maybe_merged = match v_val {
|
||||
Value::Record(rv) => Some(Value::Record(Record(merge_seqs(
|
||||
rv.0,
|
||||
w_val.into_record()?.0,
|
||||
)?))),
|
||||
Value::Sequence(vs) => Some(Value::Sequence(merge_seqs(vs, w_val.into_sequence()?)?)),
|
||||
Value::Set(_vs) => None, // unsure how to merge sets
|
||||
Value::Dictionary(vs) => {
|
||||
let mut ws = w_val.into_dictionary()?;
|
||||
let mut rs = Map::new();
|
||||
for (k, vv) in vs.into_iter() {
|
||||
match ws.remove(&k) {
|
||||
Some(wv) => {
|
||||
rs.insert(k, merge2(vv, wv)?);
|
||||
}
|
||||
None => {
|
||||
rs.insert(k, vv);
|
||||
}
|
||||
}
|
||||
}
|
||||
rs.extend(ws.into_iter());
|
||||
Some(Value::Dictionary(rs))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
maybe_merged.map(|vw| N::wrap(v_anns, vw))
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge several values into a single value according to [the
|
||||
/// specification](https://preserves.dev/preserves.html#appendix-merging-values).
|
||||
pub fn merge<N: NestedValue, I: IntoIterator<Item = N>>(vs: I) -> Option<N> {
|
||||
let mut vs = vs.into_iter();
|
||||
let mut v = vs.next().expect("at least one value in merge()");
|
||||
for w in vs {
|
||||
match merge2(v, w) {
|
||||
Some(merged) => v = merged,
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
Some(v)
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
//! # Representing, reading, and writing Preserves `Value`s as Rust data
|
||||
//!
|
||||
//! ```
|
||||
//! use preserves::value::{IOValue, text, packed};
|
||||
//! let v: IOValue = text::iovalue_from_str("<hi>")?;
|
||||
//! let w: IOValue = packed::iovalue_from_bytes(b"\xb4\xb3\x02hi\x84")?;
|
||||
//! assert_eq!(v, w);
|
||||
//! assert_eq!(text::TextWriter::encode_iovalue(&v)?, "<hi>");
|
||||
//! assert_eq!(packed::PackedWriter::encode_iovalue(&v)?, b"\xb4\xb3\x02hi\x84");
|
||||
//! # Ok::<(), std::io::Error>(())
|
||||
//! ```
|
||||
//!
|
||||
//! Preserves `Value`s are categorized in the following way. The core representation type,
|
||||
//! [crate::value::repr::Value], reflects this structure. However, most of the time you will
|
||||
//! work with [IOValue] or some other implementation of trait [NestedValue], which augments an
|
||||
//! underlying [Value] with [*annotations*][crate::value::repr::Annotations] (e.g. comments) and fixes a strategy
|
||||
//! for memory management.
|
||||
//!
|
||||
#![doc = include_str!("../../doc/value-grammar.md")]
|
||||
//!
|
||||
//! ## Memory management
|
||||
//!
|
||||
//! Each implementation of [NestedValue] chooses a different point in the space of possible
|
||||
//! approaches to memory management for `Value`s.
|
||||
//!
|
||||
//! ##### `IOValue`
|
||||
//!
|
||||
//! The most commonly-used and versatile implementation, [IOValue], uses [std::sync::Arc] for
|
||||
//! internal links in compound `Value`s. Unlike many of the other implementations of
|
||||
//! [NestedValue], [IOValue] doesn't offer flexibility in the Rust data type to be used for
|
||||
//! Preserves [embedded values](https://preserves.dev/preserves.html#embeddeds): instead,
|
||||
//! embedded values in an [IOValue] are themselves [IOValue]s.
|
||||
//!
|
||||
//! ##### `ArcValue<D>`, `RcValue<D>`, and `PlainValue<D>`
|
||||
//!
|
||||
//! For control over the Rust type to use for embedded values, choose [ArcValue], [RcValue], or
|
||||
//! [PlainValue]. Use [ArcValue] when you wish to transfer values among threads. [RcValue] is
|
||||
//! more niche; it may be useful for complex terms that do not need to cross thread boundaries.
|
||||
//! [PlainValue] is even more niche: it does not use a reference-counted pointer type, meaning
|
||||
//! it does not offer any kind of aliasing or sharing among subterms at all.
|
||||
//!
|
||||
//! # Parsing, pretty-printing, encoding and decoding `Value`s
|
||||
//!
|
||||
//! Modules [reader] and [writer] supply generic [Reader] and [Writer] traits for parsing and
|
||||
//! unparsing Preserves data. Implementations of [Reader] and [Writer] connect Preserves data
|
||||
//! to specific transfer syntaxes:
|
||||
//!
|
||||
//! - module [packed] supplies tools for working with the machine-oriented binary syntax
|
||||
//! - module [text] supplies tools for working with human-readable text syntax
|
||||
|
||||
pub mod boundary;
|
||||
pub mod de;
|
||||
pub mod domain;
|
||||
pub mod magic;
|
||||
pub mod merge;
|
||||
pub mod packed;
|
||||
pub mod reader;
|
||||
pub mod repr;
|
||||
pub mod ser;
|
||||
pub mod signed_integer;
|
||||
pub mod suspendable;
|
||||
pub mod text;
|
||||
pub mod writer;
|
||||
|
||||
pub use de::from_value;
|
||||
pub use de::Deserializer;
|
||||
pub use domain::DebugDomainEncode;
|
||||
pub use domain::DomainDecode;
|
||||
pub use domain::DomainEncode;
|
||||
pub use domain::DomainParse;
|
||||
pub use domain::FromStrDomainParse;
|
||||
pub use domain::IOValueDomainCodec;
|
||||
pub use domain::NoEmbeddedDomainCodec;
|
||||
pub use domain::ViaCodec;
|
||||
pub use merge::merge;
|
||||
pub use packed::PackedReader;
|
||||
pub use packed::PackedWriter;
|
||||
pub use reader::BinarySource;
|
||||
pub use reader::BytesBinarySource;
|
||||
pub use reader::ConfiguredReader;
|
||||
pub use reader::IOBinarySource;
|
||||
pub use reader::Reader;
|
||||
pub use reader::Token;
|
||||
pub use repr::AnnotatedValue;
|
||||
pub use repr::ArcValue;
|
||||
pub use repr::AtomClass;
|
||||
pub use repr::CompoundClass;
|
||||
pub use repr::Domain;
|
||||
pub use repr::Double;
|
||||
pub use repr::DummyValue;
|
||||
pub use repr::Embeddable;
|
||||
pub use repr::IOValue;
|
||||
pub use repr::Map;
|
||||
pub use repr::NestedValue;
|
||||
pub use repr::PlainValue;
|
||||
pub use repr::RcValue;
|
||||
pub use repr::Record;
|
||||
pub use repr::Set;
|
||||
pub use repr::UnwrappedIOValue;
|
||||
pub use repr::Value;
|
||||
pub use repr::ValueClass;
|
||||
pub use ser::to_value;
|
||||
pub use ser::Serializer;
|
||||
pub use text::TextReader;
|
||||
pub use text::ToplevelWhitespaceMode;
|
||||
pub use text::TextWriter;
|
||||
pub use writer::Writer;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn invert_map<A, B>(m: &Map<A, B>) -> Map<B, A>
|
||||
where
|
||||
A: Clone,
|
||||
B: Clone + Ord,
|
||||
{
|
||||
m.iter().map(|(a, b)| (b.clone(), a.clone())).collect()
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
//! Definitions of the tags used in the binary encoding.
|
||||
|
||||
use std::convert::{From, TryFrom};
|
||||
use std::io;
|
||||
|
||||
/// Rust representation of tags used in the binary encoding.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Tag {
|
||||
False,
|
||||
True,
|
||||
End,
|
||||
Annotation,
|
||||
Embedded,
|
||||
Ieee754,
|
||||
SignedInteger,
|
||||
String,
|
||||
ByteString,
|
||||
Symbol,
|
||||
Record,
|
||||
Sequence,
|
||||
Set,
|
||||
Dictionary,
|
||||
}
|
||||
|
||||
/// Error value representing failure to decode a byte into a [Tag].
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct InvalidTag(pub u8);
|
||||
|
||||
impl From<InvalidTag> for io::Error {
|
||||
fn from(v: InvalidTag) -> Self {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Invalid Preserves tag {}", v.0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InvalidTag> for crate::error::Error {
|
||||
fn from(v: InvalidTag) -> Self {
|
||||
crate::error::Error::Io(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Tag {
|
||||
type Error = InvalidTag;
|
||||
fn try_from(v: u8) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
0x80 => Ok(Self::False),
|
||||
0x81 => Ok(Self::True),
|
||||
0x84 => Ok(Self::End),
|
||||
0x85 => Ok(Self::Annotation),
|
||||
0x86 => Ok(Self::Embedded),
|
||||
0x87 => Ok(Self::Ieee754),
|
||||
0xb0 => Ok(Self::SignedInteger),
|
||||
0xb1 => Ok(Self::String),
|
||||
0xb2 => Ok(Self::ByteString),
|
||||
0xb3 => Ok(Self::Symbol),
|
||||
0xb4 => Ok(Self::Record),
|
||||
0xb5 => Ok(Self::Sequence),
|
||||
0xb6 => Ok(Self::Set),
|
||||
0xb7 => Ok(Self::Dictionary),
|
||||
_ => Err(InvalidTag(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tag> for u8 {
|
||||
fn from(v: Tag) -> Self {
|
||||
match v {
|
||||
Tag::False => 0x80,
|
||||
Tag::True => 0x81,
|
||||
Tag::End => 0x84,
|
||||
Tag::Annotation => 0x85,
|
||||
Tag::Embedded => 0x86,
|
||||
Tag::Ieee754 => 0x87,
|
||||
Tag::SignedInteger => 0xb0,
|
||||
Tag::String => 0xb1,
|
||||
Tag::ByteString => 0xb2,
|
||||
Tag::Symbol => 0xb3,
|
||||
Tag::Record => 0xb4,
|
||||
Tag::Sequence => 0xb5,
|
||||
Tag::Set => 0xb6,
|
||||
Tag::Dictionary => 0xb7,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
//! Implements the Preserves [machine-oriented binary
|
||||
//! syntax](https://preserves.dev/preserves-binary.html).
|
||||
//!
|
||||
//! The main entry points for reading are functions [iovalue_from_bytes],
|
||||
//! [annotated_iovalue_from_bytes], [from_bytes], and [annotated_from_bytes].
|
||||
//!
|
||||
//! The main entry points for writing are [PackedWriter::encode_iovalue] and
|
||||
//! [PackedWriter::encode].
|
||||
//!
|
||||
//! # Summary of Binary Syntax
|
||||
#![doc = include_str!("../../../doc/cheatsheet-binary-plaintext.md")]
|
||||
|
||||
pub mod constants;
|
||||
pub mod reader;
|
||||
pub mod writer;
|
||||
|
||||
pub use reader::PackedReader;
|
||||
pub use writer::PackedWriter;
|
||||
|
||||
use std::io;
|
||||
|
||||
use super::{BinarySource, DomainDecode, IOValue, IOValueDomainCodec, NestedValue, Reader};
|
||||
|
||||
/// Reads a value from the given byte vector `bs` using the binary encoding, discarding
|
||||
/// annotations.
|
||||
pub fn from_bytes<N: NestedValue, Dec: DomainDecode<N::Embedded>>(
|
||||
bs: &[u8],
|
||||
decode_embedded: Dec,
|
||||
) -> io::Result<N> {
|
||||
super::BytesBinarySource::new(bs)
|
||||
.packed(decode_embedded)
|
||||
.demand_next(false)
|
||||
}
|
||||
|
||||
/// Reads an [IOValue] from the given byte vector `bs` using the binary encoding, discarding
|
||||
/// annotations.
|
||||
pub fn iovalue_from_bytes(bs: &[u8]) -> io::Result<IOValue> {
|
||||
from_bytes(bs, IOValueDomainCodec)
|
||||
}
|
||||
|
||||
/// As [from_bytes], but includes annotations.
|
||||
pub fn annotated_from_bytes<N: NestedValue, Dec: DomainDecode<N::Embedded>>(
|
||||
bs: &[u8],
|
||||
decode_embedded: Dec,
|
||||
) -> io::Result<N> {
|
||||
super::BytesBinarySource::new(bs)
|
||||
.packed(decode_embedded)
|
||||
.demand_next(true)
|
||||
}
|
||||
|
||||
/// As [iovalue_from_bytes], but includes annotations.
|
||||
pub fn annotated_iovalue_from_bytes(bs: &[u8]) -> io::Result<IOValue> {
|
||||
annotated_from_bytes(bs, IOValueDomainCodec)
|
||||
}
|
|
@ -1,616 +0,0 @@
|
|||
//! Implementation of [Reader] for the binary encoding.
|
||||
|
||||
use crate::error::{self, io_syntax_error, is_eof_io_error, ExpectedKind, Received};
|
||||
|
||||
use num::bigint::BigInt;
|
||||
use num::traits::cast::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::super::{
|
||||
boundary as B,
|
||||
reader::{BinarySource, Reader, ReaderResult, Token},
|
||||
repr::Annotations,
|
||||
signed_integer::SignedInteger,
|
||||
CompoundClass, DomainDecode, Map, NestedValue, Record, Set, Value,
|
||||
};
|
||||
use super::constants::Tag;
|
||||
|
||||
/// The binary encoding Preserves reader.
|
||||
pub struct PackedReader<
|
||||
'de,
|
||||
'src,
|
||||
N: NestedValue,
|
||||
Dec: DomainDecode<N::Embedded>,
|
||||
S: BinarySource<'de>,
|
||||
> {
|
||||
/// Underlying source of bytes.
|
||||
pub source: &'src mut S,
|
||||
/// Decoder for producing Rust values embedded in the binary data.
|
||||
pub decode_embedded: Dec,
|
||||
phantom: PhantomData<&'de N>,
|
||||
}
|
||||
|
||||
impl<'de, 'src, N: NestedValue, Dec: DomainDecode<N::Embedded>, S: BinarySource<'de>>
|
||||
BinarySource<'de> for PackedReader<'de, 'src, N, Dec, S>
|
||||
{
|
||||
type Mark = S::Mark;
|
||||
#[inline(always)]
|
||||
fn mark(&mut self) -> io::Result<Self::Mark> {
|
||||
self.source.mark()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
|
||||
self.source.restore(mark)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn skip(&mut self) -> io::Result<()> {
|
||||
self.source.skip()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn peek(&mut self) -> io::Result<u8> {
|
||||
self.source.peek()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn readbytes(&mut self, count: usize) -> io::Result<Cow<'de, [u8]>> {
|
||||
self.source.readbytes(count)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn readbytes_into(&mut self, bs: &mut [u8]) -> io::Result<()> {
|
||||
self.source.readbytes_into(bs)
|
||||
}
|
||||
}
|
||||
|
||||
fn out_of_range<I: Into<BigInt>>(i: I) -> error::Error {
|
||||
error::Error::NumberOutOfRange(i.into())
|
||||
}
|
||||
|
||||
impl<'de, 'src, N: NestedValue, Dec: DomainDecode<N::Embedded>, S: BinarySource<'de>>
|
||||
PackedReader<'de, 'src, N, Dec, S>
|
||||
{
|
||||
/// Construct a new reader from a byte source and embedded-value decoder.
|
||||
#[inline(always)]
|
||||
pub fn new(source: &'src mut S, decode_embedded: Dec) -> Self {
|
||||
PackedReader {
|
||||
source,
|
||||
decode_embedded,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read(&mut self) -> io::Result<u8> {
|
||||
let v = self.peek()?;
|
||||
self.skip()?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn expected(&mut self, k: ExpectedKind) -> error::Error {
|
||||
match self.demand_next(true) {
|
||||
Ok(v) => error::Error::Expected(k, Received::ReceivedOtherValue(format!("{:?}", v))),
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn varint(&mut self) -> io::Result<usize> {
|
||||
let mut shift = 0;
|
||||
let mut acc: usize = 0;
|
||||
loop {
|
||||
let v = self.read()?;
|
||||
acc |= ((v & 0x7f) as usize) << shift;
|
||||
shift += 7;
|
||||
if v & 0x80 == 0 {
|
||||
return Ok(acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn peekend(&mut self) -> io::Result<bool> {
|
||||
if self.peek()? == Tag::End.into() {
|
||||
self.skip()?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_next_nonannotation<T, F>(&mut self, f: F) -> ReaderResult<T>
|
||||
where
|
||||
F: FnOnce(&mut Self, Tag) -> ReaderResult<T>,
|
||||
{
|
||||
let m = self.source.mark()?;
|
||||
loop {
|
||||
match Tag::try_from(self.peek()?)? {
|
||||
Tag::Annotation => {
|
||||
self.skip()?;
|
||||
self.skip_value()?;
|
||||
}
|
||||
tag => match f(self, tag) {
|
||||
Ok(v) => return Ok(v),
|
||||
Err(e) => {
|
||||
self.source.restore(&m)?;
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_atomic(&mut self, expected_tag: Tag, k: ExpectedKind) -> ReaderResult<Cow<'de, [u8]>> {
|
||||
self.try_next_nonannotation(|r, actual_tag| {
|
||||
if actual_tag == expected_tag {
|
||||
r.skip()?;
|
||||
let count = r.varint()?;
|
||||
Ok(r.readbytes(count)?)
|
||||
} else {
|
||||
Err(r.expected(k))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn next_compound(&mut self, expected_tag: Tag, k: ExpectedKind) -> ReaderResult<()> {
|
||||
self.try_next_nonannotation(|r, actual_tag| {
|
||||
if actual_tag == expected_tag {
|
||||
r.skip()?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(r.expected(k))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_signed_integer(&mut self, count: usize) -> io::Result<SignedInteger> {
|
||||
if count == 0 {
|
||||
return Ok(SignedInteger::from(0_i128));
|
||||
}
|
||||
|
||||
if count > 16 {
|
||||
let bs = self.readbytes(count)?;
|
||||
if (bs[0] & 0x80) == 0 {
|
||||
// Positive or zero.
|
||||
let mut i = 0;
|
||||
while i < count && bs[i] == 0 {
|
||||
i += 1;
|
||||
}
|
||||
if count - i <= 16 {
|
||||
Ok(SignedInteger::from(u128::from_be_bytes(
|
||||
bs[bs.len() - 16..].try_into().unwrap(),
|
||||
)))
|
||||
} else {
|
||||
Ok(SignedInteger::from(Cow::Owned(BigInt::from_bytes_be(
|
||||
num::bigint::Sign::Plus,
|
||||
&bs[i..],
|
||||
))))
|
||||
}
|
||||
} else {
|
||||
// Negative.
|
||||
let mut i = 0;
|
||||
while i < count && bs[i] == 0xff {
|
||||
i += 1;
|
||||
}
|
||||
if count - i <= 16 {
|
||||
Ok(SignedInteger::from(i128::from_be_bytes(
|
||||
bs[bs.len() - 16..].try_into().unwrap(),
|
||||
)))
|
||||
} else {
|
||||
Ok(SignedInteger::from(Cow::Owned(
|
||||
BigInt::from_signed_bytes_be(&bs),
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let first_byte = self.read()?;
|
||||
let prefix_byte = if (first_byte & 0x80) == 0 { 0x00 } else { 0xff };
|
||||
let mut bs = [prefix_byte; 16];
|
||||
bs[16 - count] = first_byte;
|
||||
self.readbytes_into(&mut bs[16 - (count - 1)..])?;
|
||||
Ok(SignedInteger::from(i128::from_be_bytes(bs)))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn next_unsigned<T: FromPrimitive, F>(&mut self, f: F) -> ReaderResult<T>
|
||||
where
|
||||
F: FnOnce(u128) -> Option<T>,
|
||||
{
|
||||
self.try_next_nonannotation(|r, tag| {
|
||||
match tag {
|
||||
Tag::SignedInteger => {
|
||||
r.skip()?;
|
||||
let count = r.varint()?;
|
||||
let n = &r.read_signed_integer(count)?;
|
||||
let i = n.try_into().map_err(|_| out_of_range(n))?;
|
||||
f(i).ok_or_else(|| out_of_range(i))
|
||||
}
|
||||
_ => Err(r.expected(ExpectedKind::SignedInteger)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn next_signed<T: FromPrimitive, F>(&mut self, f: F) -> ReaderResult<T>
|
||||
where
|
||||
F: FnOnce(i128) -> Option<T>,
|
||||
{
|
||||
self.try_next_nonannotation(|r, tag| {
|
||||
match tag {
|
||||
Tag::SignedInteger => {
|
||||
r.skip()?;
|
||||
let count = r.varint()?;
|
||||
let n = &r.read_signed_integer(count)?;
|
||||
let i = n.try_into().map_err(|_| out_of_range(n))?;
|
||||
f(i).ok_or_else(|| out_of_range(i))
|
||||
}
|
||||
_ => Err(r.expected(ExpectedKind::SignedInteger)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gather_annotations(&mut self) -> io::Result<Vec<N>> {
|
||||
let mut annotations = vec![self.demand_next(true)?];
|
||||
while Tag::try_from(self.peek()?)? == Tag::Annotation {
|
||||
self.skip()?;
|
||||
annotations.push(self.demand_next(true)?);
|
||||
}
|
||||
Ok(annotations)
|
||||
}
|
||||
|
||||
fn skip_annotations(&mut self) -> io::Result<()> {
|
||||
self.skip_value()?;
|
||||
while Tag::try_from(self.peek()?)? == Tag::Annotation {
|
||||
self.skip()?;
|
||||
self.skip_value()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_upto_end(&mut self, read_annotations: bool) -> io::Result<Option<N>> {
|
||||
match self.peekend()? {
|
||||
true => Ok(None),
|
||||
false => Ok(Some(self.demand_next(read_annotations)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'src, N: NestedValue, Dec: DomainDecode<N::Embedded>, S: BinarySource<'de>> Reader<'de, N>
|
||||
for PackedReader<'de, 'src, N, Dec, S>
|
||||
{
|
||||
fn next(&mut self, read_annotations: bool) -> io::Result<Option<N>> {
|
||||
match self.peek() {
|
||||
Err(e) if is_eof_io_error(&e) => return Ok(None),
|
||||
Err(e) => return Err(e),
|
||||
Ok(_) => (),
|
||||
}
|
||||
Ok(Some(match Tag::try_from(self.read()?)? {
|
||||
Tag::False => N::new(false),
|
||||
Tag::True => N::new(true),
|
||||
Tag::Annotation => {
|
||||
if read_annotations {
|
||||
let mut annotations = self.gather_annotations()?;
|
||||
let (existing_annotations, v) = self.demand_next(read_annotations)?.pieces();
|
||||
annotations.extend_from_slice(existing_annotations.slice());
|
||||
N::wrap(Annotations::new(Some(annotations)), v)
|
||||
} else {
|
||||
self.skip_annotations()?;
|
||||
self.demand_next(read_annotations)?
|
||||
}
|
||||
}
|
||||
Tag::Embedded => Value::Embedded(
|
||||
self.decode_embedded
|
||||
.decode_embedded(self.source, read_annotations)?,
|
||||
).wrap(),
|
||||
Tag::Ieee754 => match self.varint()? {
|
||||
8 => {
|
||||
let mut bs = [0; 8];
|
||||
self.readbytes_into(&mut bs)?;
|
||||
Value::from(f64::from_bits(u64::from_be_bytes(bs))).wrap()
|
||||
}
|
||||
_ => return Err(io_syntax_error("Invalid IEEE754 size"))
|
||||
}
|
||||
Tag::SignedInteger => {
|
||||
let count = self.varint()?;
|
||||
let n = self.read_signed_integer(count)?;
|
||||
Value::SignedInteger(n).wrap()
|
||||
}
|
||||
Tag::String => {
|
||||
let count = self.varint()?;
|
||||
Value::String(decodestr(self.readbytes(count)?)?.into_owned()).wrap()
|
||||
}
|
||||
Tag::ByteString => {
|
||||
let count = self.varint()?;
|
||||
Value::ByteString(self.readbytes(count)?.into_owned()).wrap()
|
||||
}
|
||||
Tag::Symbol => {
|
||||
let count = self.varint()?;
|
||||
Value::Symbol(decodestr(self.readbytes(count)?)?.into_owned()).wrap()
|
||||
}
|
||||
Tag::Record => {
|
||||
let mut vs = Vec::new();
|
||||
while let Some(v) = self.next_upto_end(read_annotations)? {
|
||||
vs.push(v);
|
||||
}
|
||||
if vs.is_empty() {
|
||||
return Err(io_syntax_error("Too few elements in encoded record"));
|
||||
}
|
||||
Value::Record(Record(vs)).wrap()
|
||||
}
|
||||
Tag::Sequence => {
|
||||
let mut vs = Vec::new();
|
||||
while let Some(v) = self.next_upto_end(read_annotations)? {
|
||||
vs.push(v);
|
||||
}
|
||||
Value::Sequence(vs).wrap()
|
||||
}
|
||||
Tag::Set => {
|
||||
let mut s = Set::new();
|
||||
while let Some(v) = self.next_upto_end(read_annotations)? {
|
||||
s.insert(v);
|
||||
}
|
||||
Value::Set(s).wrap()
|
||||
}
|
||||
Tag::Dictionary => {
|
||||
let mut d = Map::new();
|
||||
while let Some(k) = self.next_upto_end(read_annotations)? {
|
||||
match self.next_upto_end(read_annotations)? {
|
||||
Some(v) => {
|
||||
d.insert(k, v);
|
||||
}
|
||||
None => return Err(io_syntax_error("Missing dictionary value")),
|
||||
}
|
||||
}
|
||||
Value::Dictionary(d).wrap()
|
||||
}
|
||||
tag @ Tag::End => {
|
||||
return Err(io_syntax_error(&format!("Invalid tag: {:?}", tag)));
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn open_record(&mut self, arity: Option<usize>) -> ReaderResult<B::Type> {
|
||||
self.next_compound(Tag::Record, ExpectedKind::Record(arity))?;
|
||||
let mut b = B::Type::default();
|
||||
self.ensure_more_expected(&mut b, &B::Item::RecordLabel)?;
|
||||
Ok(b)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn open_sequence_or_set(&mut self) -> ReaderResult<B::Item> {
|
||||
self.try_next_nonannotation(|r, tag| {
|
||||
match tag {
|
||||
Tag::Sequence => {
|
||||
r.skip()?;
|
||||
Ok(B::Item::SequenceValue)
|
||||
}
|
||||
Tag::Set => {
|
||||
r.skip()?;
|
||||
Ok(B::Item::SetValue)
|
||||
}
|
||||
_ => Err(r.expected(ExpectedKind::SequenceOrSet)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn open_sequence(&mut self) -> ReaderResult<()> {
|
||||
self.next_compound(Tag::Sequence, ExpectedKind::Sequence)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn open_set(&mut self) -> ReaderResult<()> {
|
||||
self.next_compound(Tag::Set, ExpectedKind::Set)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn open_dictionary(&mut self) -> ReaderResult<()> {
|
||||
self.next_compound(Tag::Dictionary, ExpectedKind::Dictionary)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn boundary(&mut self, _b: &B::Type) -> ReaderResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn close_compound(&mut self, _b: &mut B::Type, _i: &B::Item) -> ReaderResult<bool> {
|
||||
Ok(self.peekend()?)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn open_embedded(&mut self) -> ReaderResult<()> {
|
||||
self.next_compound(Tag::Embedded, ExpectedKind::Embedded)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn close_embedded(&mut self) -> ReaderResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type Mark = S::Mark;
|
||||
|
||||
#[inline(always)]
|
||||
fn mark(&mut self) -> io::Result<Self::Mark> {
|
||||
self.source.mark()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
|
||||
self.source.restore(mark)
|
||||
}
|
||||
|
||||
fn next_token(&mut self, read_embedded_annotations: bool) -> io::Result<Token<N>> {
|
||||
loop {
|
||||
return Ok(match Tag::try_from(self.peek()?)? {
|
||||
Tag::Embedded => {
|
||||
self.skip()?;
|
||||
Token::Embedded(
|
||||
self.decode_embedded
|
||||
.decode_embedded(self.source, read_embedded_annotations)?,
|
||||
)
|
||||
}
|
||||
Tag::False
|
||||
| Tag::True
|
||||
| Tag::Ieee754
|
||||
| Tag::SignedInteger
|
||||
| Tag::String
|
||||
| Tag::ByteString
|
||||
| Tag::Symbol => Token::Atom(self.demand_next(false)?),
|
||||
|
||||
Tag::Record => {
|
||||
self.skip()?;
|
||||
Token::Compound(CompoundClass::Record)
|
||||
}
|
||||
Tag::Sequence => {
|
||||
self.skip()?;
|
||||
Token::Compound(CompoundClass::Sequence)
|
||||
}
|
||||
Tag::Set => {
|
||||
self.skip()?;
|
||||
Token::Compound(CompoundClass::Set)
|
||||
}
|
||||
Tag::Dictionary => {
|
||||
self.skip()?;
|
||||
Token::Compound(CompoundClass::Dictionary)
|
||||
}
|
||||
|
||||
Tag::End => {
|
||||
self.skip()?;
|
||||
Token::End
|
||||
}
|
||||
|
||||
Tag::Annotation => {
|
||||
self.skip()?;
|
||||
self.skip_value()?;
|
||||
continue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn next_annotations_and_token(&mut self) -> io::Result<(Vec<N>, Token<N>)> {
|
||||
match Tag::try_from(self.peek()?)? {
|
||||
Tag::Annotation => {
|
||||
self.skip()?;
|
||||
let annotations = self.gather_annotations()?;
|
||||
Ok((annotations, self.next_token(true)?))
|
||||
}
|
||||
_ => Ok((Vec::new(), self.next_token(true)?)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn next_boolean(&mut self) -> ReaderResult<bool> {
|
||||
self.try_next_nonannotation(|r, tag| {
|
||||
match tag {
|
||||
Tag::False => {
|
||||
r.skip()?;
|
||||
Ok(false)
|
||||
},
|
||||
Tag::True => {
|
||||
r.skip()?;
|
||||
Ok(true)
|
||||
},
|
||||
_ => Err(r.expected(ExpectedKind::Boolean)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn next_signedinteger(&mut self) -> ReaderResult<SignedInteger> {
|
||||
self.try_next_nonannotation(|r, tag| {
|
||||
match tag {
|
||||
Tag::SignedInteger => {
|
||||
r.skip()?;
|
||||
let count = r.varint()?;
|
||||
Ok(r.read_signed_integer(count)?)
|
||||
}
|
||||
_ => Err(r.expected(ExpectedKind::SignedInteger)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn next_i8(&mut self) -> ReaderResult<i8> {
|
||||
self.next_signed(|n| n.to_i8())
|
||||
}
|
||||
fn next_i16(&mut self) -> ReaderResult<i16> {
|
||||
self.next_signed(|n| n.to_i16())
|
||||
}
|
||||
fn next_i32(&mut self) -> ReaderResult<i32> {
|
||||
self.next_signed(|n| n.to_i32())
|
||||
}
|
||||
fn next_i64(&mut self) -> ReaderResult<i64> {
|
||||
self.next_signed(|n| n.to_i64())
|
||||
}
|
||||
fn next_i128(&mut self) -> ReaderResult<i128> {
|
||||
self.next_signed(|n| n.to_i128())
|
||||
}
|
||||
|
||||
fn next_u8(&mut self) -> ReaderResult<u8> {
|
||||
self.next_unsigned(|n| n.to_u8())
|
||||
}
|
||||
fn next_u16(&mut self) -> ReaderResult<u16> {
|
||||
self.next_unsigned(|n| n.to_u16())
|
||||
}
|
||||
fn next_u32(&mut self) -> ReaderResult<u32> {
|
||||
self.next_unsigned(|n| n.to_u32())
|
||||
}
|
||||
fn next_u64(&mut self) -> ReaderResult<u64> {
|
||||
self.next_unsigned(|n| n.to_u64())
|
||||
}
|
||||
fn next_u128(&mut self) -> ReaderResult<u128> {
|
||||
self.next_unsigned(|n| n.to_u128())
|
||||
}
|
||||
|
||||
fn next_f64(&mut self) -> ReaderResult<f64> {
|
||||
self.try_next_nonannotation(|r, tag| {
|
||||
if tag == Tag::Ieee754 {
|
||||
r.skip()?;
|
||||
match r.varint()? {
|
||||
8 => {
|
||||
let mut bs = [0; 8];
|
||||
r.readbytes_into(&mut bs)?;
|
||||
Ok(f64::from_bits(u64::from_be_bytes(bs)))
|
||||
}
|
||||
_ => Err(io_syntax_error("Invalid IEEE754 size"))?,
|
||||
}
|
||||
} else {
|
||||
Err(r.expected(ExpectedKind::Double))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn next_str(&mut self) -> ReaderResult<Cow<'de, str>> {
|
||||
Ok(decodestr(
|
||||
self.next_atomic(Tag::String, ExpectedKind::Symbol)?,
|
||||
)?)
|
||||
}
|
||||
|
||||
fn next_bytestring(&mut self) -> ReaderResult<Cow<'de, [u8]>> {
|
||||
self.next_atomic(Tag::ByteString, ExpectedKind::Symbol)
|
||||
}
|
||||
|
||||
fn next_symbol(&mut self) -> ReaderResult<Cow<'de, str>> {
|
||||
Ok(decodestr(
|
||||
self.next_atomic(Tag::Symbol, ExpectedKind::Symbol)?,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn decodestr(cow: Cow<'_, [u8]>) -> io::Result<Cow<'_, str>> {
|
||||
match cow {
|
||||
Cow::Borrowed(bs) => Ok(Cow::Borrowed(
|
||||
std::str::from_utf8(bs).map_err(|_| io_syntax_error("Invalid UTF-8"))?,
|
||||
)),
|
||||
Cow::Owned(bs) => Ok(Cow::Owned(
|
||||
String::from_utf8(bs).map_err(|_| io_syntax_error("Invalid UTF-8"))?,
|
||||
)),
|
||||
}
|
||||
}
|
|
@ -1,583 +0,0 @@
|
|||
//! Implementation of [Writer] for the binary encoding.
|
||||
|
||||
use super::super::boundary as B;
|
||||
use super::super::suspendable::Suspendable;
|
||||
use super::super::DomainEncode;
|
||||
use super::super::IOValue;
|
||||
use super::super::IOValueDomainCodec;
|
||||
use super::super::NestedValue;
|
||||
use super::constants::Tag;
|
||||
use num::bigint::BigInt;
|
||||
use num::cast::ToPrimitive;
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use super::super::writer::{varint, CompoundWriter, Writer};
|
||||
|
||||
/// The binary encoding Preserves writer.
|
||||
pub struct PackedWriter<W: io::Write>(Suspendable<W>);
|
||||
|
||||
impl PackedWriter<&mut Vec<u8>> {
|
||||
/// Encodes `v` to a byte vector.
|
||||
#[inline(always)]
|
||||
pub fn encode<N: NestedValue, Enc: DomainEncode<N::Embedded>>(
|
||||
enc: &mut Enc,
|
||||
v: &N,
|
||||
) -> io::Result<Vec<u8>> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
PackedWriter::new(&mut buf).write(enc, v)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Encodes `v` to a byte vector.
|
||||
#[inline(always)]
|
||||
pub fn encode_iovalue(v: &IOValue) -> io::Result<Vec<u8>> {
|
||||
Self::encode(&mut IOValueDomainCodec, v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> PackedWriter<W> {
|
||||
/// Construct a writer from the given byte sink `write`.
|
||||
#[inline(always)]
|
||||
pub fn new(write: W) -> Self {
|
||||
PackedWriter(Suspendable::new(write))
|
||||
}
|
||||
|
||||
/// Retrieve a mutable reference to the underlying byte sink.
|
||||
#[inline(always)]
|
||||
pub fn w(&mut self) -> &mut W {
|
||||
self.0.deref_mut()
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn write_byte(&mut self, b: u8) -> io::Result<()> {
|
||||
self.w().write_all(&[b])
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn write_integer(&mut self, bs: &[u8]) -> io::Result<()> {
|
||||
self.write_atom(Tag::SignedInteger, bs)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn write_atom(&mut self, tag: Tag, bs: &[u8]) -> io::Result<()> {
|
||||
self.write_byte(tag.into())?;
|
||||
varint(&mut self.w(), bs.len().try_into().unwrap())?;
|
||||
self.w().write_all(bs)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn suspend(&mut self) -> Self {
|
||||
PackedWriter(self.0.suspend())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn resume(&mut self, other: Self) {
|
||||
self.0.resume(other.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct BinaryOrderWriter(Vec<Vec<u8>>);
|
||||
|
||||
impl BinaryOrderWriter {
|
||||
#[inline(always)]
|
||||
fn new() -> Self {
|
||||
BinaryOrderWriter(vec![vec![]])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn pop(&mut self) -> PackedWriter<Vec<u8>> {
|
||||
PackedWriter::new(self.0.pop().unwrap())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push(&mut self, w: PackedWriter<Vec<u8>>) {
|
||||
self.0.push(w.0.take())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn items(&self) -> &Vec<Vec<u8>> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn items_mut(&mut self) -> &mut Vec<Vec<u8>> {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn buffer(&mut self) -> &mut Vec<u8> {
|
||||
self.0.last_mut().unwrap()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finish<W: WriteWriter>(mut self, w: &mut W) -> io::Result<()> {
|
||||
if !self.buffer().is_empty() {
|
||||
panic!("Missing final boundary()");
|
||||
}
|
||||
self.items_mut().pop();
|
||||
self.items_mut().sort();
|
||||
for bs in self.items() {
|
||||
w.write_raw_bytes(&bs)?;
|
||||
}
|
||||
w.write_tag(Tag::End)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait WriteWriter: Writer {
|
||||
fn write_raw_bytes(&mut self, v: &[u8]) -> io::Result<()>;
|
||||
|
||||
#[inline(always)]
|
||||
fn write_tag(&mut self, tag: Tag) -> io::Result<()> {
|
||||
self.write_raw_bytes(&[tag.into()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> WriteWriter for PackedWriter<W> {
|
||||
#[inline(always)]
|
||||
fn write_raw_bytes(&mut self, v: &[u8]) -> io::Result<()> {
|
||||
self.w().write_all(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteWriter for BinaryOrderWriter {
|
||||
#[inline(always)]
|
||||
fn write_raw_bytes(&mut self, v: &[u8]) -> io::Result<()> {
|
||||
use io::Write;
|
||||
self.buffer().write_all(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> CompoundWriter for PackedWriter<W> {
|
||||
#[inline(always)]
|
||||
fn boundary(&mut self, b: &B::Type) -> io::Result<()> {
|
||||
if let Some(B::Item::Annotation) = b.opening {
|
||||
self.write_tag(Tag::Annotation)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CompoundWriter for BinaryOrderWriter {
|
||||
#[inline(always)]
|
||||
fn boundary(&mut self, b: &B::Type) -> io::Result<()> {
|
||||
match b.closing {
|
||||
Some(B::Item::DictionaryValue)
|
||||
| Some(B::Item::RecordField)
|
||||
| Some(B::Item::SequenceValue)
|
||||
| Some(B::Item::SetValue) => self.items_mut().push(vec![]),
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! binary_order_writer_method {
|
||||
(mut $n:ident ($($argname:ident : $argty:ty),*) -> $retty:ty) =>
|
||||
(#[inline(always)] fn $n (&mut self, $($argname : $argty),*) -> $retty {
|
||||
(&mut PackedWriter::new(self.buffer())).$n($($argname),*)
|
||||
});
|
||||
}
|
||||
|
||||
impl Writer for BinaryOrderWriter {
|
||||
type AnnWriter = PackedWriter<Vec<u8>>;
|
||||
type RecWriter = PackedWriter<Vec<u8>>;
|
||||
type SeqWriter = PackedWriter<Vec<u8>>;
|
||||
type SetWriter = BinaryOrderWriter;
|
||||
type DictWriter = BinaryOrderWriter;
|
||||
type EmbeddedWriter = PackedWriter<Vec<u8>>;
|
||||
|
||||
#[inline(always)]
|
||||
fn start_annotations(&mut self) -> io::Result<Self::AnnWriter> {
|
||||
Ok(self.pop())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn end_annotations(&mut self, ann: Self::AnnWriter) -> io::Result<()> {
|
||||
self.push(ann);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
binary_order_writer_method!(mut write_bool(v: bool) -> io::Result<()>);
|
||||
|
||||
binary_order_writer_method!(mut write_f64(v: f64) -> io::Result<()>);
|
||||
|
||||
binary_order_writer_method!(mut write_i8(v: i8) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_u8(v: u8) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_i16(v: i16) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_u16(v: u16) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_i32(v: i32) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_u32(v: u32) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_i64(v: i64) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_u64(v: u64) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_i128(v: i128) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_u128(v: u128) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_int(v: &BigInt) -> io::Result<()>);
|
||||
|
||||
binary_order_writer_method!(mut write_string(v: &str) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_bytes(v: &[u8]) -> io::Result<()>);
|
||||
binary_order_writer_method!(mut write_symbol(v: &str) -> io::Result<()>);
|
||||
|
||||
#[inline(always)]
|
||||
fn start_record(&mut self, _field_count: Option<usize>) -> io::Result<Self::RecWriter> {
|
||||
self.write_tag(Tag::Record)?;
|
||||
Ok(self.pop())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn end_record(&mut self, rec: Self::RecWriter) -> io::Result<()> {
|
||||
self.push(rec);
|
||||
self.write_tag(Tag::End)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_sequence(&mut self, _item_count: Option<usize>) -> io::Result<Self::SeqWriter> {
|
||||
self.write_tag(Tag::Sequence)?;
|
||||
Ok(self.pop())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn end_sequence(&mut self, seq: Self::SeqWriter) -> io::Result<()> {
|
||||
self.push(seq);
|
||||
self.write_tag(Tag::End)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_set(&mut self, _item_count: Option<usize>) -> io::Result<Self::SetWriter> {
|
||||
self.write_tag(Tag::Set)?;
|
||||
Ok(BinaryOrderWriter::new())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn end_set(&mut self, set: Self::SetWriter) -> io::Result<()> {
|
||||
set.finish(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_dictionary(&mut self, _entry_count: Option<usize>) -> io::Result<Self::DictWriter> {
|
||||
self.write_tag(Tag::Dictionary)?;
|
||||
Ok(BinaryOrderWriter::new())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn end_dictionary(&mut self, dict: Self::DictWriter) -> io::Result<()> {
|
||||
dict.finish(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_embedded(&mut self) -> io::Result<Self::EmbeddedWriter> {
|
||||
self.write_tag(Tag::Embedded)?;
|
||||
Ok(self.pop())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn end_embedded(&mut self, ptr: Self::EmbeddedWriter) -> io::Result<()> {
|
||||
self.push(ptr);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fits_in_bytes {
|
||||
($v:ident, $limit:literal) => {{
|
||||
let bits = $limit * 8 - 1;
|
||||
$v >= -(1 << bits) && $v < (1 << bits)
|
||||
}};
|
||||
}
|
||||
|
||||
impl<W: io::Write> Writer for PackedWriter<W> {
|
||||
type AnnWriter = Self;
|
||||
type RecWriter = Self;
|
||||
type SeqWriter = Self;
|
||||
type SetWriter = BinaryOrderWriter;
|
||||
type DictWriter = BinaryOrderWriter;
|
||||
type EmbeddedWriter = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn start_annotations(&mut self) -> io::Result<Self::AnnWriter> {
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn end_annotations(&mut self, ann: Self::AnnWriter) -> io::Result<()> {
|
||||
self.resume(ann);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_bool(&mut self, v: bool) -> io::Result<()> {
|
||||
self.write_tag(if v { Tag::True } else { Tag::False })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_f64(&mut self, v: f64) -> io::Result<()> {
|
||||
self.write_tag(Tag::Ieee754)?;
|
||||
self.write_byte(8)?;
|
||||
self.write_raw_bytes(&u64::to_be_bytes(f64::to_bits(v)))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_i8(&mut self, v: i8) -> io::Result<()> {
|
||||
if v == 0 {
|
||||
return self.write_integer(&[]);
|
||||
}
|
||||
self.write_integer(&[v as u8])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_u8(&mut self, v: u8) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i8(w);
|
||||
}
|
||||
self.write_integer(&[0, v])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_i16(&mut self, v: i16) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i8(w);
|
||||
}
|
||||
self.write_integer(&[(v >> 8) as u8, (v & 255) as u8])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_u16(&mut self, v: u16) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i16(w);
|
||||
}
|
||||
self.write_integer(&[0, (v >> 8) as u8, (v & 255) as u8])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_i32(&mut self, v: i32) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i16(w);
|
||||
}
|
||||
if fits_in_bytes!(v, 3) {
|
||||
return self.write_integer(&[(v >> 16) as u8, (v >> 8) as u8, (v & 255) as u8]);
|
||||
}
|
||||
self.write_integer(&[
|
||||
(v >> 24) as u8,
|
||||
(v >> 16) as u8,
|
||||
(v >> 8) as u8,
|
||||
(v & 255) as u8,
|
||||
])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_u32(&mut self, v: u32) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i32(w);
|
||||
}
|
||||
self.write_integer(&[
|
||||
0,
|
||||
(v >> 24) as u8,
|
||||
(v >> 16) as u8,
|
||||
(v >> 8) as u8,
|
||||
(v & 255) as u8,
|
||||
])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_i64(&mut self, v: i64) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i32(w);
|
||||
}
|
||||
if fits_in_bytes!(v, 5) {
|
||||
return self.write_integer(&[
|
||||
(v >> 32) as u8,
|
||||
(v >> 24) as u8,
|
||||
(v >> 16) as u8,
|
||||
(v >> 8) as u8,
|
||||
(v & 255) as u8,
|
||||
]);
|
||||
}
|
||||
if fits_in_bytes!(v, 6) {
|
||||
return self.write_integer(&[
|
||||
(v >> 40) as u8,
|
||||
(v >> 32) as u8,
|
||||
(v >> 24) as u8,
|
||||
(v >> 16) as u8,
|
||||
(v >> 8) as u8,
|
||||
(v & 255) as u8,
|
||||
]);
|
||||
}
|
||||
if fits_in_bytes!(v, 7) {
|
||||
return self.write_integer(&[
|
||||
(v >> 48) as u8,
|
||||
(v >> 40) as u8,
|
||||
(v >> 32) as u8,
|
||||
(v >> 24) as u8,
|
||||
(v >> 16) as u8,
|
||||
(v >> 8) as u8,
|
||||
(v & 255) as u8,
|
||||
]);
|
||||
}
|
||||
self.write_integer(&[
|
||||
(v >> 56) as u8,
|
||||
(v >> 48) as u8,
|
||||
(v >> 40) as u8,
|
||||
(v >> 32) as u8,
|
||||
(v >> 24) as u8,
|
||||
(v >> 16) as u8,
|
||||
(v >> 8) as u8,
|
||||
(v & 255) as u8,
|
||||
])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_u64(&mut self, v: u64) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i64(w);
|
||||
}
|
||||
self.write_integer(&[
|
||||
0,
|
||||
(v >> 56) as u8,
|
||||
(v >> 48) as u8,
|
||||
(v >> 40) as u8,
|
||||
(v >> 32) as u8,
|
||||
(v >> 24) as u8,
|
||||
(v >> 16) as u8,
|
||||
(v >> 8) as u8,
|
||||
(v & 255) as u8,
|
||||
])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_i128(&mut self, v: i128) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i64(w);
|
||||
}
|
||||
let bs: [u8; 16] = v.to_be_bytes();
|
||||
if fits_in_bytes!(v, 9) {
|
||||
return self.write_integer(&bs[7..]);
|
||||
}
|
||||
if fits_in_bytes!(v, 10) {
|
||||
return self.write_integer(&bs[6..]);
|
||||
}
|
||||
if fits_in_bytes!(v, 11) {
|
||||
return self.write_integer(&bs[5..]);
|
||||
}
|
||||
if fits_in_bytes!(v, 12) {
|
||||
return self.write_integer(&bs[4..]);
|
||||
}
|
||||
if fits_in_bytes!(v, 13) {
|
||||
return self.write_integer(&bs[3..]);
|
||||
}
|
||||
if fits_in_bytes!(v, 14) {
|
||||
return self.write_integer(&bs[2..]);
|
||||
}
|
||||
if fits_in_bytes!(v, 15) {
|
||||
return self.write_integer(&bs[1..]);
|
||||
}
|
||||
self.write_integer(&bs)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_u128(&mut self, v: u128) -> io::Result<()> {
|
||||
if let Ok(w) = v.try_into() {
|
||||
return self.write_i128(w);
|
||||
}
|
||||
let bs: [u8; 16] = v.to_be_bytes();
|
||||
self.write_tag(Tag::SignedInteger)?;
|
||||
varint(&mut self.w(), 17)?;
|
||||
self.write_byte(0)?;
|
||||
self.write_raw_bytes(&bs)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_int(&mut self, v: &BigInt) -> io::Result<()> {
|
||||
match v.to_i8() {
|
||||
Some(n) => self.write_i8(n),
|
||||
None => match v.to_i128() {
|
||||
Some(n) => self.write_i128(n),
|
||||
None => self.write_integer(&v.to_signed_bytes_be()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_string(&mut self, v: &str) -> io::Result<()> {
|
||||
self.write_atom(Tag::String, v.as_bytes())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_bytes(&mut self, v: &[u8]) -> io::Result<()> {
|
||||
self.write_atom(Tag::ByteString, v)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_symbol(&mut self, v: &str) -> io::Result<()> {
|
||||
self.write_atom(Tag::Symbol, v.as_bytes())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_record(&mut self, _field_count: Option<usize>) -> io::Result<Self::RecWriter> {
|
||||
self.write_tag(Tag::Record)?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn end_record(&mut self, rec: Self::RecWriter) -> io::Result<()> {
|
||||
self.resume(rec);
|
||||
self.write_tag(Tag::End)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_sequence(&mut self, _item_count: Option<usize>) -> io::Result<Self::SeqWriter> {
|
||||
self.write_tag(Tag::Sequence)?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn end_sequence(&mut self, seq: Self::SeqWriter) -> io::Result<()> {
|
||||
self.resume(seq);
|
||||
self.write_tag(Tag::End)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_set(&mut self, _item_count: Option<usize>) -> io::Result<Self::SetWriter> {
|
||||
self.write_tag(Tag::Set)?;
|
||||
Ok(BinaryOrderWriter::new())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn end_set(&mut self, set: Self::SetWriter) -> io::Result<()> {
|
||||
set.finish(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_dictionary(&mut self, _entry_count: Option<usize>) -> io::Result<Self::DictWriter> {
|
||||
self.write_tag(Tag::Dictionary)?;
|
||||
Ok(BinaryOrderWriter::new())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn end_dictionary(&mut self, dict: Self::DictWriter) -> io::Result<()> {
|
||||
dict.finish(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn start_embedded(&mut self) -> io::Result<Self::EmbeddedWriter> {
|
||||
self.write_tag(Tag::Embedded)?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn end_embedded(&mut self, ann: Self::EmbeddedWriter) -> io::Result<()> {
|
||||
self.resume(ann);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
|
@ -1,567 +0,0 @@
|
|||
//! Generic [Reader] trait for parsing Preserves [Value][crate::value::repr::Value]s,
|
||||
//! implemented by code that provides each specific transfer syntax.
|
||||
|
||||
use crate::error::{self, io_eof, ExpectedKind, Received};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::boundary as B;
|
||||
use super::signed_integer::SignedInteger;
|
||||
use super::CompoundClass;
|
||||
use super::DomainDecode;
|
||||
use super::DomainParse;
|
||||
use super::Double;
|
||||
use super::IOValue;
|
||||
use super::IOValueDomainCodec;
|
||||
use super::NestedValue;
|
||||
use super::ViaCodec;
|
||||
|
||||
pub type ReaderResult<T> = std::result::Result<T, error::Error>;
|
||||
|
||||
/// Tokens produced when performing
|
||||
/// [SAX](https://en.wikipedia.org/wiki/Simple_API_for_XML)-style reading of terms.
|
||||
pub enum Token<N: NestedValue> {
|
||||
/// An embedded value was seen and completely decoded.
|
||||
Embedded(N::Embedded),
|
||||
/// An atomic value was seen and completely decoded.
|
||||
Atom(N),
|
||||
/// A compound value has been opened; its contents follow, and it will be terminated by
|
||||
/// [Token::End].
|
||||
Compound(CompoundClass),
|
||||
/// Closes a previously-opened compound value.
|
||||
End,
|
||||
}
|
||||
|
||||
/// Generic parser for Preserves.
|
||||
pub trait Reader<'de, N: NestedValue> {
|
||||
/// Retrieve the next parseable value or an indication of end-of-input.
|
||||
///
|
||||
/// Yields `Ok(Some(...))` if a complete value is available, `Ok(None)` if the end of
|
||||
/// stream has been reached, or `Err(...)` for parse or IO errors, including
|
||||
/// incomplete/partial input. See also [Reader::demand_next].
|
||||
fn next(&mut self, read_annotations: bool) -> io::Result<Option<N>>;
|
||||
|
||||
// Hiding these from the documentation for the moment because I don't want to have to
|
||||
// document the whole Boundary thing.
|
||||
#[doc(hidden)]
|
||||
fn open_record(&mut self, arity: Option<usize>) -> ReaderResult<B::Type>;
|
||||
#[doc(hidden)]
|
||||
fn open_sequence_or_set(&mut self) -> ReaderResult<B::Item>;
|
||||
#[doc(hidden)]
|
||||
fn open_sequence(&mut self) -> ReaderResult<()>;
|
||||
#[doc(hidden)]
|
||||
fn open_set(&mut self) -> ReaderResult<()>;
|
||||
#[doc(hidden)]
|
||||
fn open_dictionary(&mut self) -> ReaderResult<()>;
|
||||
#[doc(hidden)]
|
||||
fn boundary(&mut self, b: &B::Type) -> ReaderResult<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
// close_compound implies a b.shift(...) and a self.boundary(b).
|
||||
fn close_compound(&mut self, b: &mut B::Type, i: &B::Item) -> ReaderResult<bool>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn open_embedded(&mut self) -> ReaderResult<()>;
|
||||
#[doc(hidden)]
|
||||
fn close_embedded(&mut self) -> ReaderResult<()>;
|
||||
|
||||
/// Allows structured backtracking to an earlier stage in a parse. Useful for layering
|
||||
/// parser combinators atop a Reader.
|
||||
type Mark;
|
||||
/// Retrieve a marker for the current position in the input.
|
||||
fn mark(&mut self) -> io::Result<Self::Mark>;
|
||||
/// Seek the input to a previously-saved position.
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()>;
|
||||
|
||||
/// Get the next [SAX](https://en.wikipedia.org/wiki/Simple_API_for_XML)-style event,
|
||||
/// discarding annotations.
|
||||
///
|
||||
/// The `read_embedded_annotations` controls whether annotations are also skipped on
|
||||
/// *embedded* values or not.
|
||||
fn next_token(&mut self, read_embedded_annotations: bool) -> io::Result<Token<N>>;
|
||||
/// Get the next [SAX](https://en.wikipedia.org/wiki/Simple_API_for_XML)-style event, plus
|
||||
/// a vector containing any annotations that preceded it.
|
||||
fn next_annotations_and_token(&mut self) -> io::Result<(Vec<N>, Token<N>)>;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/// Skips the next available complete value. Yields an error if no such value exists.
|
||||
fn skip_value(&mut self) -> io::Result<()> {
|
||||
// TODO efficient skipping in specific impls of this trait
|
||||
let _ = self.demand_next(false)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieve the next parseable value, treating end-of-input as an error.
|
||||
///
|
||||
/// Yields `Ok(...)` if a complete value is available or `Err(...)` for parse or IO errors,
|
||||
/// including incomplete/partial input or end of stream. See also [Reader::next].
|
||||
fn demand_next(&mut self, read_annotations: bool) -> io::Result<N> {
|
||||
self.next(read_annotations)?.ok_or_else(io_eof)
|
||||
}
|
||||
|
||||
/// Yields the next value, if it is a `Boolean`, or an error otherwise.
|
||||
fn next_boolean(&mut self) -> ReaderResult<bool> {
|
||||
self.demand_next(false)?.value().to_boolean()
|
||||
}
|
||||
|
||||
/// Yields the next value, if it is a `Double`, or an error otherwise.
|
||||
fn next_double(&mut self) -> ReaderResult<Double> {
|
||||
Ok(self.demand_next(false)?.value().to_double()?.to_owned())
|
||||
}
|
||||
|
||||
/// Yields the next value, if it is a `SignedInteger`, or an error otherwise.
|
||||
fn next_signedinteger(&mut self) -> ReaderResult<SignedInteger> {
|
||||
Ok(self
|
||||
.demand_next(false)?
|
||||
.value()
|
||||
.to_signedinteger()?
|
||||
.to_owned())
|
||||
}
|
||||
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [i8], or an error
|
||||
/// otherwise.
|
||||
fn next_i8(&mut self) -> ReaderResult<i8> {
|
||||
self.demand_next(false)?.value().to_i8()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [u8], or an error
|
||||
/// otherwise.
|
||||
fn next_u8(&mut self) -> ReaderResult<u8> {
|
||||
self.demand_next(false)?.value().to_u8()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [i16], or an error
|
||||
/// otherwise.
|
||||
fn next_i16(&mut self) -> ReaderResult<i16> {
|
||||
self.demand_next(false)?.value().to_i16()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [u16], or an error
|
||||
/// otherwise.
|
||||
fn next_u16(&mut self) -> ReaderResult<u16> {
|
||||
self.demand_next(false)?.value().to_u16()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [i32], or an error
|
||||
/// otherwise.
|
||||
fn next_i32(&mut self) -> ReaderResult<i32> {
|
||||
self.demand_next(false)?.value().to_i32()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [u32], or an error
|
||||
/// otherwise.
|
||||
fn next_u32(&mut self) -> ReaderResult<u32> {
|
||||
self.demand_next(false)?.value().to_u32()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [i64], or an error
|
||||
/// otherwise.
|
||||
fn next_i64(&mut self) -> ReaderResult<i64> {
|
||||
self.demand_next(false)?.value().to_i64()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [u64], or an error
|
||||
/// otherwise.
|
||||
fn next_u64(&mut self) -> ReaderResult<u64> {
|
||||
self.demand_next(false)?.value().to_u64()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [i128], or an error
|
||||
/// otherwise.
|
||||
fn next_i128(&mut self) -> ReaderResult<i128> {
|
||||
self.demand_next(false)?.value().to_i128()
|
||||
}
|
||||
/// Yields the next value, if it is a `SignedInteger` that fits in [u128], or an error
|
||||
/// otherwise.
|
||||
fn next_u128(&mut self) -> ReaderResult<u128> {
|
||||
self.demand_next(false)?.value().to_u128()
|
||||
}
|
||||
/// Yields the next value as an [f64], if it is a `Double`, or an error otherwise.
|
||||
fn next_f64(&mut self) -> ReaderResult<f64> {
|
||||
self.demand_next(false)?.value().to_f64()
|
||||
}
|
||||
/// Yields the next value as a [char], if it is parseable by
|
||||
/// [Value::to_char][crate::value::Value::to_char], or an error otherwise.
|
||||
fn next_char(&mut self) -> ReaderResult<char> {
|
||||
self.demand_next(false)?.value().to_char()
|
||||
}
|
||||
|
||||
/// Yields the next value, if it is a `String`, or an error otherwise.
|
||||
fn next_str(&mut self) -> ReaderResult<Cow<'de, str>> {
|
||||
Ok(Cow::Owned(
|
||||
self.demand_next(false)?.value().to_string()?.to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Yields the next value, if it is a `ByteString`, or an error otherwise.
|
||||
fn next_bytestring(&mut self) -> ReaderResult<Cow<'de, [u8]>> {
|
||||
Ok(Cow::Owned(
|
||||
self.demand_next(false)?.value().to_bytestring()?.to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Yields the next value, if it is a `Symbol`, or an error otherwise.
|
||||
fn next_symbol(&mut self) -> ReaderResult<Cow<'de, str>> {
|
||||
Ok(Cow::Owned(
|
||||
self.demand_next(false)?.value().to_symbol()?.to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn open_option(&mut self) -> ReaderResult<Option<B::Type>> {
|
||||
let b = self.open_record(None)?;
|
||||
let label: &str = &self.next_symbol()?;
|
||||
match label {
|
||||
"None" => {
|
||||
self.ensure_complete(b, &B::Item::RecordField)?;
|
||||
Ok(None)
|
||||
}
|
||||
"Some" => Ok(Some(b)),
|
||||
_ => Err(error::Error::Expected(
|
||||
ExpectedKind::Option,
|
||||
Received::ReceivedRecordWithLabel(label.to_owned()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn open_simple_record(&mut self, name: &str, arity: Option<usize>) -> ReaderResult<B::Type> {
|
||||
let b = self.open_record(arity)?;
|
||||
let label: &str = &self.next_symbol()?;
|
||||
if label == name {
|
||||
Ok(b)
|
||||
} else {
|
||||
Err(error::Error::Expected(
|
||||
ExpectedKind::SimpleRecord(name.to_owned(), arity),
|
||||
Received::ReceivedRecordWithLabel(label.to_owned()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a [ConfiguredReader] set with the given value for `read_annotations`.
|
||||
fn configured(self, read_annotations: bool) -> ConfiguredReader<'de, N, Self>
|
||||
where
|
||||
Self: std::marker::Sized,
|
||||
{
|
||||
ConfiguredReader {
|
||||
reader: self,
|
||||
read_annotations,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn ensure_more_expected(&mut self, b: &mut B::Type, i: &B::Item) -> ReaderResult<()> {
|
||||
if !self.close_compound(b, i)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error::Error::MissingItem)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn ensure_complete(&mut self, mut b: B::Type, i: &B::Item) -> ReaderResult<()> {
|
||||
if !self.close_compound(&mut b, i)? {
|
||||
Err(error::Error::MissingCloseDelimiter)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'de, N: NestedValue, R: Reader<'de, N>> Reader<'de, N> for &'r mut R {
|
||||
fn next(&mut self, read_annotations: bool) -> io::Result<Option<N>> {
|
||||
(*self).next(read_annotations)
|
||||
}
|
||||
|
||||
fn open_record(&mut self, arity: Option<usize>) -> ReaderResult<B::Type> {
|
||||
(*self).open_record(arity)
|
||||
}
|
||||
|
||||
fn open_sequence_or_set(&mut self) -> ReaderResult<B::Item> {
|
||||
(*self).open_sequence_or_set()
|
||||
}
|
||||
|
||||
fn open_sequence(&mut self) -> ReaderResult<()> {
|
||||
(*self).open_sequence()
|
||||
}
|
||||
|
||||
fn open_set(&mut self) -> ReaderResult<()> {
|
||||
(*self).open_set()
|
||||
}
|
||||
|
||||
fn open_dictionary(&mut self) -> ReaderResult<()> {
|
||||
(*self).open_dictionary()
|
||||
}
|
||||
|
||||
fn boundary(&mut self, b: &B::Type) -> ReaderResult<()> {
|
||||
(*self).boundary(b)
|
||||
}
|
||||
|
||||
fn close_compound(&mut self, b: &mut B::Type, i: &B::Item) -> ReaderResult<bool> {
|
||||
(*self).close_compound(b, i)
|
||||
}
|
||||
|
||||
fn open_embedded(&mut self) -> ReaderResult<()> {
|
||||
(*self).open_embedded()
|
||||
}
|
||||
|
||||
fn close_embedded(&mut self) -> ReaderResult<()> {
|
||||
(*self).close_embedded()
|
||||
}
|
||||
|
||||
type Mark = R::Mark;
|
||||
|
||||
fn mark(&mut self) -> io::Result<Self::Mark> {
|
||||
(*self).mark()
|
||||
}
|
||||
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
|
||||
(*self).restore(mark)
|
||||
}
|
||||
|
||||
fn next_token(&mut self, read_embedded_annotations: bool) -> io::Result<Token<N>> {
|
||||
(*self).next_token(read_embedded_annotations)
|
||||
}
|
||||
|
||||
fn next_annotations_and_token(&mut self) -> io::Result<(Vec<N>, Token<N>)> {
|
||||
(*self).next_annotations_and_token()
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic seekable stream of input bytes.
|
||||
pub trait BinarySource<'de>: Sized {
|
||||
/// Allows structured backtracking to an earlier position in an input.
|
||||
type Mark;
|
||||
/// Retrieve a marker for the current position in the input.
|
||||
fn mark(&mut self) -> io::Result<Self::Mark>;
|
||||
/// Seek the input to a previously-saved position.
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()>;
|
||||
|
||||
/// Skip the next byte.
|
||||
fn skip(&mut self) -> io::Result<()>;
|
||||
/// Returns the next byte without advancing over it.
|
||||
fn peek(&mut self) -> io::Result<u8>;
|
||||
/// Returns and consumes the next `count` bytes, which must all be available. Always yields
|
||||
/// exactly `count` bytes or an error.
|
||||
fn readbytes(&mut self, count: usize) -> io::Result<Cow<'de, [u8]>>;
|
||||
/// As [BinarySource::readbytes], but uses `bs` as destination for the read bytes as well
|
||||
/// as taking the size of `bs` as the count of bytes to read.
|
||||
fn readbytes_into(&mut self, bs: &mut [u8]) -> io::Result<()>;
|
||||
|
||||
/// Constructs a [PackedReader][super::PackedReader] that will read from `self`.
|
||||
fn packed<N: NestedValue, Dec: DomainDecode<N::Embedded>>(
|
||||
&mut self,
|
||||
decode_embedded: Dec,
|
||||
) -> super::PackedReader<'de, '_, N, Dec, Self> {
|
||||
super::PackedReader::new(self, decode_embedded)
|
||||
}
|
||||
|
||||
/// Constructs a [PackedReader][super::PackedReader] that will read [IOValue]s from `self`.
|
||||
fn packed_iovalues(
|
||||
&mut self,
|
||||
) -> super::PackedReader<'de, '_, IOValue, IOValueDomainCodec, Self> {
|
||||
self.packed(IOValueDomainCodec)
|
||||
}
|
||||
|
||||
/// Constructs a [TextReader][super::TextReader] that will read from `self`.
|
||||
fn text<N: NestedValue, Dec: DomainParse<N::Embedded>>(
|
||||
&mut self,
|
||||
decode_embedded: Dec,
|
||||
) -> super::TextReader<'de, '_, N, Dec, Self> {
|
||||
super::TextReader::new(self, decode_embedded)
|
||||
}
|
||||
|
||||
/// Constructs a [TextReader][super::TextReader] that will read [IOValue]s from `self`.
|
||||
fn text_iovalues(
|
||||
&mut self,
|
||||
) -> super::TextReader<'de, '_, IOValue, ViaCodec<IOValueDomainCodec>, Self> {
|
||||
self.text::<IOValue, _>(ViaCodec::new(IOValueDomainCodec))
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of [BinarySource] backed by an [`io::Read`]` + `[`io::Seek`] implementation.
|
||||
pub struct IOBinarySource<R: io::Read + io::Seek> {
|
||||
/// The underlying byte source.
|
||||
pub read: R,
|
||||
#[doc(hidden)]
|
||||
/// One-place buffer for peeked bytes.
|
||||
pub buf: Option<u8>,
|
||||
}
|
||||
|
||||
impl<R: io::Read + io::Seek> IOBinarySource<R> {
|
||||
/// Constructs an [IOBinarySource] from the given [`io::Read`]` + `[`io::Seek`]
|
||||
/// implementation.
|
||||
#[inline(always)]
|
||||
pub fn new(read: R) -> Self {
|
||||
IOBinarySource { read, buf: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, R: io::Read + io::Seek> BinarySource<'de> for IOBinarySource<R> {
|
||||
type Mark = u64;
|
||||
|
||||
#[inline(always)]
|
||||
fn mark(&mut self) -> io::Result<Self::Mark> {
|
||||
Ok(self.read.stream_position()? - (if self.buf.is_some() { 1 } else { 0 }))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
|
||||
self.read.seek(io::SeekFrom::Start(*mark))?;
|
||||
self.buf = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn skip(&mut self) -> io::Result<()> {
|
||||
if self.buf.is_none() {
|
||||
unreachable!();
|
||||
}
|
||||
self.buf = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn peek(&mut self) -> io::Result<u8> {
|
||||
match self.buf {
|
||||
Some(b) => Ok(b),
|
||||
None => {
|
||||
let b = &mut [0];
|
||||
match self.read.read(b)? {
|
||||
0 => Err(io_eof()),
|
||||
1 => {
|
||||
self.buf = Some(b[0]);
|
||||
Ok(b[0])
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn readbytes(&mut self, count: usize) -> io::Result<Cow<'de, [u8]>> {
|
||||
if self.buf.is_some() {
|
||||
unreachable!();
|
||||
}
|
||||
let mut bs = vec![0; count];
|
||||
self.read.read_exact(&mut bs)?;
|
||||
Ok(Cow::Owned(bs))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn readbytes_into(&mut self, bs: &mut [u8]) -> io::Result<()> {
|
||||
if self.buf.is_some() {
|
||||
unreachable!();
|
||||
}
|
||||
self.read.read_exact(bs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of [BinarySource] backed by a slice of [u8].
|
||||
pub struct BytesBinarySource<'de> {
|
||||
/// The underlying byte source.
|
||||
pub bytes: &'de [u8],
|
||||
#[doc(hidden)]
|
||||
/// Current position within `bytes`.
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl<'de> BytesBinarySource<'de> {
|
||||
/// Constructs a [BytesBinarySource] from the given `u8` slice.
|
||||
#[inline(always)]
|
||||
pub fn new(bytes: &'de [u8]) -> Self {
|
||||
BytesBinarySource { bytes, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> BinarySource<'de> for BytesBinarySource<'de> {
|
||||
type Mark = usize;
|
||||
|
||||
#[inline(always)]
|
||||
fn mark(&mut self) -> io::Result<Self::Mark> {
|
||||
Ok(self.index)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
|
||||
self.index = *mark;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn skip(&mut self) -> io::Result<()> {
|
||||
if self.index >= self.bytes.len() {
|
||||
unreachable!();
|
||||
}
|
||||
self.index += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn peek(&mut self) -> io::Result<u8> {
|
||||
if self.index >= self.bytes.len() {
|
||||
Err(io_eof())
|
||||
} else {
|
||||
Ok(self.bytes[self.index])
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn readbytes(&mut self, count: usize) -> io::Result<Cow<'de, [u8]>> {
|
||||
if self.index + count > self.bytes.len() {
|
||||
Err(io_eof())
|
||||
} else {
|
||||
let bs = &self.bytes[self.index..self.index + count];
|
||||
self.index += count;
|
||||
Ok(Cow::Borrowed(bs))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn readbytes_into(&mut self, bs: &mut [u8]) -> io::Result<()> {
|
||||
let count = bs.len();
|
||||
if self.index + count > self.bytes.len() {
|
||||
Err(io_eof())
|
||||
} else {
|
||||
bs.copy_from_slice(&self.bytes[self.index..self.index + count]);
|
||||
self.index += count;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A combination of a [Reader] with presets governing its operation.
|
||||
pub struct ConfiguredReader<'de, N: NestedValue, R: Reader<'de, N>> {
|
||||
/// The underlying [Reader].
|
||||
pub reader: R,
|
||||
/// Configuration as to whether to include or discard annotations while reading.
|
||||
pub read_annotations: bool,
|
||||
phantom: PhantomData<&'de N>,
|
||||
}
|
||||
|
||||
impl<'de, N: NestedValue, R: Reader<'de, N>> ConfiguredReader<'de, N, R> {
|
||||
/// Constructs a [ConfiguredReader] based on the given `reader`.
|
||||
pub fn new(reader: R) -> Self {
|
||||
reader.configured(true)
|
||||
}
|
||||
|
||||
/// Updates the `read_annotations` field of `self`.
|
||||
pub fn set_read_annotations(&mut self, read_annotations: bool) {
|
||||
self.read_annotations = read_annotations
|
||||
}
|
||||
|
||||
/// Retrieve the next parseable value, treating end-of-input as an error.
|
||||
///
|
||||
/// Delegates directly to [Reader::demand_next].
|
||||
pub fn demand_next(&mut self) -> io::Result<N> {
|
||||
self.reader.demand_next(self.read_annotations)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, N: NestedValue, R: Reader<'de, N>> std::iter::Iterator for ConfiguredReader<'de, N, R> {
|
||||
type Item = io::Result<N>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.reader.next(self.read_annotations) {
|
||||
Err(e) => Some(Err(e)),
|
||||
Ok(None) => None,
|
||||
Ok(Some(v)) => Some(Ok(v)),
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,377 +0,0 @@
|
|||
//! Support for Serde serialization of Rust data types into Preserves *values* (not syntax).
|
||||
|
||||
use crate::value::{repr::Record, IOValue, Map, Value};
|
||||
use serde::Serialize;
|
||||
|
||||
/// Empty/placeholder type for representing serialization errors: serialization to values
|
||||
/// cannot fail.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {}
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T>(_: T) -> Self
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
impl std::error::Error for Error {}
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, _fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Serde serializer for converting Rust data to in-memory Preserves values, which can then be
|
||||
/// serialized using text or binary syntax, analyzed further, etc.
|
||||
pub struct Serializer;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeDictionary {
|
||||
next_key: Option<IOValue>,
|
||||
items: Map<IOValue, IOValue>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeRecord {
|
||||
r: Record<IOValue>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeSequence {
|
||||
vec: Vec<IOValue>,
|
||||
}
|
||||
|
||||
impl serde::Serializer for Serializer {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
type SerializeSeq = SerializeSequence;
|
||||
type SerializeTuple = SerializeRecord;
|
||||
type SerializeTupleStruct = SerializeRecord;
|
||||
type SerializeTupleVariant = SerializeRecord;
|
||||
type SerializeMap = SerializeDictionary;
|
||||
type SerializeStruct = SerializeRecord;
|
||||
type SerializeStructVariant = SerializeRecord;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v as f64).wrap())
|
||||
}
|
||||
|
||||
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<Self::Ok> {
|
||||
Ok(Value::simple_record1("UnicodeScalar", Value::from(v as u32).wrap()).wrap())
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
|
||||
Ok(Value::from(v).wrap())
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok> {
|
||||
Ok(Value::simple_record0("None").wrap())
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, v: &T) -> Result<Self::Ok>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Ok(Value::simple_record1("Some", to_value(v)).wrap())
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok> {
|
||||
Ok(Value::simple_record0("tuple").wrap())
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok> {
|
||||
Ok(Value::simple_record0(name).wrap())
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
) -> Result<Self::Ok> {
|
||||
Ok(Value::simple_record0(variant_name).wrap())
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T: ?Sized>(self, name: &'static str, value: &T) -> Result<Self::Ok>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match super::magic::receive_output_value(name, value) {
|
||||
Some(v) => Ok(v),
|
||||
None => {
|
||||
// TODO: This is apparently discouraged, and we should apparently just serialize `value`?
|
||||
Ok(Value::simple_record1(name, to_value(value)).wrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Ok(Value::simple_record1(variant_name, to_value(value)).wrap())
|
||||
}
|
||||
|
||||
fn serialize_seq(self, count: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let vec = match count {
|
||||
Some(n) => Vec::with_capacity(n),
|
||||
None => Vec::new(),
|
||||
};
|
||||
Ok(SerializeSequence { vec })
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, count: usize) -> Result<Self::SerializeTuple> {
|
||||
Ok(SerializeRecord {
|
||||
r: Value::simple_record("tuple", count),
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
count: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
Ok(SerializeRecord {
|
||||
r: Value::simple_record(name, count),
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
count: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
Ok(SerializeRecord {
|
||||
r: Value::simple_record(variant_name, count),
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_map(self, _count: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
Ok(SerializeDictionary {
|
||||
next_key: None,
|
||||
items: Map::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_struct(self, name: &'static str, count: usize) -> Result<Self::SerializeStruct> {
|
||||
Ok(SerializeRecord {
|
||||
r: Value::simple_record(name, count),
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant: u32,
|
||||
variant_name: &'static str,
|
||||
count: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
Ok(SerializeRecord {
|
||||
r: Value::simple_record(variant_name, count),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeMap for SerializeDictionary {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.next_key = Some(to_value(key));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let key = self.next_key.take().unwrap();
|
||||
self.items.insert(key, to_value(value));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(Value::from(self.items).wrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl SerializeRecord {
|
||||
fn push<T: ?Sized>(&mut self, value: &T)
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.r.fields_vec_mut().push(to_value(value))
|
||||
}
|
||||
|
||||
fn finish(self) -> IOValue {
|
||||
self.r.finish().wrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeStruct for SerializeRecord {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, _name: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(self.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeStructVariant for SerializeRecord {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, _name: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(self.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTuple for SerializeRecord {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(self.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleStruct for SerializeRecord {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(self.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleVariant for SerializeRecord {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(self.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeSeq for SerializeSequence {
|
||||
type Ok = IOValue;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.vec.push(to_value(value));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(Value::from(self.vec).wrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function for directly converting a Serde-serializable `T` to an [IOValue].
|
||||
pub fn to_value<T>(value: T) -> IOValue
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
value.serialize(Serializer).unwrap()
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
//! Representation of Preserves `SignedInteger`s as [i128]/[u128] (if they fit) or [BigInt] (if
|
||||
//! they don't).
|
||||
|
||||
use num::bigint::BigInt;
|
||||
use num::traits::cast::ToPrimitive;
|
||||
use num::traits::sign::Signed;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::{Ord, Ordering, PartialOrd};
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
|
||||
/// Internal representation of Preserves `SignedInteger`s.
|
||||
///
|
||||
/// Invariant: if I128 can be used, it will be; otherwise, if U128 can be used, it will be;
|
||||
/// otherwise, Big will be used.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum SignedIntegerRepr {
|
||||
I128(i128),
|
||||
U128(u128),
|
||||
Big(Box<BigInt>),
|
||||
}
|
||||
|
||||
/// Main representation of Preserves `SignedInteger`s.
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SignedInteger(SignedIntegerRepr);
|
||||
|
||||
impl fmt::Debug for SignedInteger {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}n", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SignedInteger {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match self.repr() {
|
||||
SignedIntegerRepr::I128(i) => i.fmt(f),
|
||||
SignedIntegerRepr::U128(u) => u.fmt(f),
|
||||
SignedIntegerRepr::Big(n) => n.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for SignedInteger {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match self.repr() {
|
||||
SignedIntegerRepr::I128(i1) => match other.repr() {
|
||||
SignedIntegerRepr::I128(i2) => i1.cmp(i2),
|
||||
SignedIntegerRepr::U128(_) => {
|
||||
if *i1 < 0 {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
}
|
||||
SignedIntegerRepr::Big(n) => {
|
||||
if n.is_negative() {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
}
|
||||
},
|
||||
SignedIntegerRepr::U128(u1) => match other.repr() {
|
||||
SignedIntegerRepr::I128(_) => Ordering::Greater,
|
||||
SignedIntegerRepr::U128(u2) => u1.cmp(u2),
|
||||
SignedIntegerRepr::Big(n) => {
|
||||
if n.is_positive() {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
}
|
||||
},
|
||||
SignedIntegerRepr::Big(n1) => match other.repr() {
|
||||
SignedIntegerRepr::I128(_) | SignedIntegerRepr::U128(_) => {
|
||||
if n1.is_negative() {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
}
|
||||
SignedIntegerRepr::Big(n2) => n1.cmp(n2),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for SignedInteger {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl SignedInteger {
|
||||
/// Extract the internal representation.
|
||||
pub fn repr(&self) -> &SignedIntegerRepr {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Does this `SignedInteger` fit in an [i128]? (See also [the TryFrom instance for
|
||||
/// i128](#impl-TryFrom<%26SignedInteger>-for-i128).)
|
||||
pub fn is_i(&self) -> bool {
|
||||
matches!(self.0, SignedIntegerRepr::I128(_))
|
||||
}
|
||||
|
||||
/// Does this `SignedInteger` fit in a [u128], but not an [i128]? (See also [the TryFrom
|
||||
/// instance for u128](#impl-TryFrom<%26SignedInteger>-for-u128).)
|
||||
pub fn is_u(&self) -> bool {
|
||||
matches!(self.0, SignedIntegerRepr::U128(_))
|
||||
}
|
||||
|
||||
/// Does this `SignedInteger` fit neither in a [u128] nor an [i128]? (See also [the TryFrom
|
||||
/// instance for BigInt](#impl-From<%26'a+SignedInteger>-for-BigInt).)
|
||||
pub fn is_big(&self) -> bool {
|
||||
matches!(self.0, SignedIntegerRepr::Big(_))
|
||||
}
|
||||
}
|
||||
|
||||
//--
|
||||
|
||||
macro_rules! map_integral_type_to_signed_integer {
|
||||
($iN:ident, $uN:ident) => {
|
||||
impl From<$iN> for SignedInteger {
|
||||
fn from(v: $iN) -> Self {
|
||||
SignedInteger(SignedIntegerRepr::I128(v.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$uN> for SignedInteger {
|
||||
fn from(v: $uN) -> Self {
|
||||
SignedInteger(SignedIntegerRepr::I128(v.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&SignedInteger> for $iN {
|
||||
type Error = ();
|
||||
fn try_from(v: &SignedInteger) -> Result<Self, Self::Error> {
|
||||
i128::try_from(v)?.try_into().map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&SignedInteger> for $uN {
|
||||
type Error = ();
|
||||
fn try_from(v: &SignedInteger) -> Result<Self, Self::Error> {
|
||||
u128::try_from(v)?.try_into().map_err(|_| ())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
map_integral_type_to_signed_integer!(i8, u8);
|
||||
map_integral_type_to_signed_integer!(i16, u16);
|
||||
map_integral_type_to_signed_integer!(i32, u32);
|
||||
map_integral_type_to_signed_integer!(i64, u64);
|
||||
|
||||
//--
|
||||
|
||||
impl From<i128> for SignedInteger {
|
||||
fn from(v: i128) -> Self {
|
||||
SignedInteger(SignedIntegerRepr::I128(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for SignedInteger {
|
||||
fn from(v: u128) -> Self {
|
||||
if let Ok(w) = v.try_into() {
|
||||
SignedInteger(SignedIntegerRepr::I128(w))
|
||||
} else {
|
||||
SignedInteger(SignedIntegerRepr::U128(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&SignedInteger> for i128 {
|
||||
type Error = ();
|
||||
fn try_from(v: &SignedInteger) -> Result<Self, Self::Error> {
|
||||
match v.repr() {
|
||||
SignedIntegerRepr::I128(i) => Ok(*i),
|
||||
SignedIntegerRepr::U128(_) => Err(()),
|
||||
SignedIntegerRepr::Big(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&SignedInteger> for u128 {
|
||||
type Error = ();
|
||||
fn try_from(v: &SignedInteger) -> Result<Self, Self::Error> {
|
||||
match v.repr() {
|
||||
SignedIntegerRepr::I128(i) => i.to_u128().ok_or(()),
|
||||
SignedIntegerRepr::U128(u) => Ok(*u),
|
||||
SignedIntegerRepr::Big(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--
|
||||
|
||||
impl From<BigInt> for SignedInteger {
|
||||
fn from(v: BigInt) -> Self {
|
||||
Self::from(Cow::Owned(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SignedInteger> for BigInt {
|
||||
fn from(v: &'a SignedInteger) -> Self {
|
||||
match v.repr() {
|
||||
SignedIntegerRepr::I128(i) => BigInt::from(*i),
|
||||
SignedIntegerRepr::U128(u) => BigInt::from(*u),
|
||||
SignedIntegerRepr::Big(n) => *n.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--
|
||||
|
||||
impl<'a> From<Cow<'a, BigInt>> for SignedInteger {
|
||||
fn from(v: Cow<'a, BigInt>) -> Self {
|
||||
if let Some(w) = v.to_i128() {
|
||||
SignedInteger(SignedIntegerRepr::I128(w))
|
||||
} else if let Some(w) = v.to_u128() {
|
||||
SignedInteger(SignedIntegerRepr::U128(w))
|
||||
} else {
|
||||
SignedInteger(SignedIntegerRepr::Big(Box::new(v.into_owned())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SignedInteger> for Cow<'a, BigInt> {
|
||||
fn from(v: &'a SignedInteger) -> Self {
|
||||
match v.repr() {
|
||||
SignedIntegerRepr::I128(i) => Cow::Owned(BigInt::from(*i)),
|
||||
SignedIntegerRepr::U128(u) => Cow::Owned(BigInt::from(*u)),
|
||||
SignedIntegerRepr::Big(n) => Cow::Borrowed(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--
|
||||
|
||||
impl From<isize> for SignedInteger {
|
||||
fn from(v: isize) -> Self {
|
||||
SignedInteger(SignedIntegerRepr::I128(v as i128))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for SignedInteger {
|
||||
fn from(v: usize) -> Self {
|
||||
SignedInteger(SignedIntegerRepr::U128(v as u128))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&SignedInteger> for isize {
|
||||
type Error = ();
|
||||
fn try_from(v: &SignedInteger) -> Result<Self, Self::Error> {
|
||||
i128::try_from(v)?.try_into().map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&SignedInteger> for usize {
|
||||
type Error = ();
|
||||
fn try_from(v: &SignedInteger) -> Result<Self, Self::Error> {
|
||||
u128::try_from(v)?.try_into().map_err(|_| ())
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
#![doc(hidden)]
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub enum Suspendable<T> {
|
||||
Active(T),
|
||||
Suspended,
|
||||
}
|
||||
|
||||
impl<T> Suspendable<T> {
|
||||
pub fn new(t: T) -> Self {
|
||||
Suspendable::Active(t)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn suspend(&mut self) -> Self {
|
||||
match self {
|
||||
Suspendable::Active(_) => std::mem::replace(self, Suspendable::Suspended),
|
||||
Suspendable::Suspended => panic!("Attempt to suspend suspended Suspendable"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn resume(&mut self, other: Self) {
|
||||
match self {
|
||||
Suspendable::Suspended => match other {
|
||||
Suspendable::Active(_) => *self = other,
|
||||
Suspendable::Suspended => panic!("Attempt to resume from suspended Suspendable"),
|
||||
},
|
||||
Suspendable::Active(_) => panic!("Attempt to resume non-suspended Suspendable"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn take(self) -> T {
|
||||
match self {
|
||||
Suspendable::Active(t) => t,
|
||||
Suspendable::Suspended => panic!("Attempt to take from suspended Suspendable"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Suspendable<T> {
|
||||
type Target = T;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Suspendable::Suspended => panic!("Suspended Suspendable at deref"),
|
||||
Suspendable::Active(t) => t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Suspendable<T> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Suspendable::Suspended => panic!("Empty Suspendable at deref_mut"),
|
||||
Suspendable::Active(t) => t,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
//! Implements the Preserves [human-oriented text
|
||||
//! syntax](https://preserves.dev/preserves-text.html).
|
||||
//!
|
||||
//! The main entry points for reading are functions [iovalue_from_str],
|
||||
//! [annotated_iovalue_from_str], [from_str], and [annotated_from_str].
|
||||
//!
|
||||
//! The main entry points for writing are [TextWriter::encode_iovalue] and
|
||||
//! [TextWriter::encode].
|
||||
//!
|
||||
//! # Summary of Text Syntax
|
||||
#![doc = include_str!("../../../doc/cheatsheet-text-plaintext.md")]
|
||||
|
||||
pub mod reader;
|
||||
pub mod writer;
|
||||
|
||||
pub use reader::TextReader;
|
||||
pub use reader::ToplevelWhitespaceMode;
|
||||
pub use writer::TextWriter;
|
||||
|
||||
use crate::value::reader::BytesBinarySource;
|
||||
|
||||
use std::io;
|
||||
|
||||
use super::{DomainParse, IOValue, IOValueDomainCodec, NestedValue, Reader, ViaCodec};
|
||||
|
||||
/// Reads a value from the given string using the text syntax, discarding annotations.
|
||||
pub fn from_str<N: NestedValue, Dec: DomainParse<N::Embedded>>(
|
||||
s: &str,
|
||||
decode_embedded: Dec,
|
||||
) -> io::Result<N> {
|
||||
TextReader::new(&mut BytesBinarySource::new(s.as_bytes()), decode_embedded).demand_next(false)
|
||||
}
|
||||
|
||||
/// Reads an [IOValue] from the given string using the text syntax, discarding annotations.
|
||||
pub fn iovalue_from_str(s: &str) -> io::Result<IOValue> {
|
||||
from_str(s, ViaCodec::new(IOValueDomainCodec))
|
||||
}
|
||||
|
||||
/// As [from_str], but includes annotations.
|
||||
pub fn annotated_from_str<N: NestedValue, Dec: DomainParse<N::Embedded>>(
|
||||
s: &str,
|
||||
decode_embedded: Dec,
|
||||
) -> io::Result<N> {
|
||||
TextReader::new(&mut BytesBinarySource::new(s.as_bytes()), decode_embedded).demand_next(true)
|
||||
}
|
||||
|
||||
/// As [iovalue_from_str], but includes annotations.
|
||||
pub fn annotated_iovalue_from_str(s: &str) -> io::Result<IOValue> {
|
||||
annotated_from_str(s, ViaCodec::new(IOValueDomainCodec))
|
||||
}
|
|
@ -1,715 +0,0 @@
|
|||
//! Implementation of [Reader] for the text syntax.
|
||||
|
||||
use crate::error::io_syntax_error;
|
||||
use crate::error::is_eof_io_error;
|
||||
use crate::error::syntax_error;
|
||||
use crate::error::Error;
|
||||
use crate::error::ExpectedKind;
|
||||
use crate::error::Received;
|
||||
|
||||
use crate::hex;
|
||||
|
||||
use crate::value::boundary as B;
|
||||
use crate::value::reader::BinarySource;
|
||||
use crate::value::reader::ReaderResult;
|
||||
use crate::value::repr::Annotations;
|
||||
use crate::value::CompoundClass;
|
||||
use crate::value::DomainParse;
|
||||
use crate::value::IOValue;
|
||||
use crate::value::IOValueDomainCodec;
|
||||
use crate::value::Map;
|
||||
use crate::value::NestedValue;
|
||||
use crate::value::Reader;
|
||||
use crate::value::Record;
|
||||
use crate::value::Set;
|
||||
use crate::value::Token;
|
||||
use crate::value::Value;
|
||||
use crate::value::ViaCodec;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use num::bigint::BigInt;
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// The text syntax Preserves reader.
|
||||
pub struct TextReader<'de, 'src, N: NestedValue, Dec: DomainParse<N::Embedded>, S: BinarySource<'de>> {
|
||||
/// Underlying source of (utf8) bytes.
|
||||
pub source: &'src mut S,
|
||||
/// Decoder for producing Rust values embedded in the text.
|
||||
pub dec: Dec,
|
||||
/// Treatment of whitespace before a toplevel term.
|
||||
pub toplevel_whitespace_mode: ToplevelWhitespaceMode,
|
||||
phantom: PhantomData<&'de N>,
|
||||
}
|
||||
|
||||
/// [TextReader] chooses `Document` mode to treat whitespace preceding end-of-file as a "no
|
||||
/// more values" non-error situation, or `Value` mode to treat it as an "expected more input"
|
||||
/// situation.
|
||||
///
|
||||
/// The Preserves syntax for `Value` treats any input at all, even whitespace, as an indicator
|
||||
/// that a term is to follow. However, when using a TextReader to parse a *series* of `Value`s
|
||||
/// in a `Document`, whitespace followed by EOF is to be treated as the permitted optional
|
||||
/// whitespace at the end of a `Document.
|
||||
pub enum ToplevelWhitespaceMode {
|
||||
Document,
|
||||
Value,
|
||||
}
|
||||
|
||||
fn decode_utf8(bs: Vec<u8>) -> io::Result<String> {
|
||||
Ok(String::from_utf8(bs).map_err(|_| io_syntax_error("Invalid UTF-8"))?)
|
||||
}
|
||||
|
||||
fn append_codepoint(bs: &mut Vec<u8>, n: u32) -> io::Result<()> {
|
||||
let c = char::from_u32(n).ok_or_else(|| io_syntax_error("Bad code point"))?;
|
||||
let mut buf = [0; 4];
|
||||
let _ = c.encode_utf8(&mut buf);
|
||||
bs.extend(&buf[0..c.len_utf8()]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'de, 'src, N: NestedValue, Dec: DomainParse<N::Embedded>, S: BinarySource<'de>>
|
||||
TextReader<'de, 'src, N, Dec, S>
|
||||
{
|
||||
/// Construct a new reader from a byte (utf8) source and embedded-value decoder.
|
||||
pub fn new(source: &'src mut S, dec: Dec) -> Self {
|
||||
TextReader {
|
||||
source,
|
||||
dec,
|
||||
toplevel_whitespace_mode: ToplevelWhitespaceMode::Document,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toplevel_whitespace_mode(mut self, new_mode: ToplevelWhitespaceMode) -> Self {
|
||||
self.toplevel_whitespace_mode = new_mode;
|
||||
self
|
||||
}
|
||||
|
||||
fn peek(&mut self) -> io::Result<u8> {
|
||||
self.source.peek()
|
||||
}
|
||||
|
||||
fn skip(&mut self) -> io::Result<()> {
|
||||
self.source.skip()
|
||||
}
|
||||
|
||||
fn next_byte(&mut self) -> io::Result<u8> {
|
||||
let b = self.source.peek()?;
|
||||
self.source.skip()?;
|
||||
Ok(b)
|
||||
}
|
||||
|
||||
fn skip_whitespace(&mut self) {
|
||||
self.skip_whitespace_and_maybe_commas(false)
|
||||
}
|
||||
|
||||
fn skip_whitespace_and_maybe_commas(&mut self, skip_commas: bool) {
|
||||
// Deliberately swallows errors.
|
||||
while let Ok(c) = self.peek() {
|
||||
match c {
|
||||
b' ' | b'\t' | b'\r' | b'\n' => {
|
||||
let _ = self.skip();
|
||||
()
|
||||
}
|
||||
b',' if skip_commas => {
|
||||
let _ = self.skip();
|
||||
()
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is a duplicate of fn expected in PackedReader.
|
||||
fn expected(&mut self, k: ExpectedKind) -> Error {
|
||||
match Reader::<N>::demand_next(self, true) {
|
||||
Ok(v) => Error::Expected(k, Received::ReceivedOtherValue(format!("{:?}", v))),
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_annotations(&mut self, vs: &mut Vec<N>) -> ReaderResult<()> {
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
match self.peek()? {
|
||||
b'#' => {
|
||||
let m = self.source.mark()?;
|
||||
self.skip()?;
|
||||
match self.next_byte()? {
|
||||
b' ' | b'\t' => vs.push(N::new(self.comment_line()?)),
|
||||
b'\n' | b'\r' => vs.push(N::new("")),
|
||||
_ => {
|
||||
self.source.restore(&m)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
b'@' => {
|
||||
self.skip()?;
|
||||
vs.push(self.demand_next(true)?)
|
||||
}
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prepend_annotations_to_next(&mut self, mut annotations: Vec<N>) -> ReaderResult<N> {
|
||||
let (existing_annotations, v) = Reader::<N>::demand_next(self, true)?.pieces();
|
||||
annotations.extend_from_slice(existing_annotations.slice());
|
||||
Ok(N::wrap(Annotations::new(Some(annotations)), v))
|
||||
}
|
||||
|
||||
fn skip_annotations(&mut self) -> ReaderResult<()> {
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
match self.peek()? {
|
||||
b'#' => {
|
||||
let m = self.source.mark()?;
|
||||
self.skip()?;
|
||||
match self.next_byte()? {
|
||||
b' ' | b'\t' => { self.comment_line()?; () }
|
||||
b'\n' | b'\r' => (),
|
||||
_ => {
|
||||
self.source.restore(&m)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
b'@' => {
|
||||
self.skip()?;
|
||||
self.skip_value()?;
|
||||
}
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the next [IOValue] in the input stream.
|
||||
pub fn next_iovalue(&mut self, read_annotations: bool) -> io::Result<IOValue> {
|
||||
let mut r = TextReader::new(self.source, ViaCodec::new(IOValueDomainCodec));
|
||||
let v = r.demand_next(read_annotations)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn comment_line(&mut self) -> io::Result<String> {
|
||||
let mut bs = Vec::new();
|
||||
loop {
|
||||
let b = self.peek()?;
|
||||
self.skip()?;
|
||||
match b {
|
||||
b'\r' | b'\n' => return Ok(decode_utf8(bs)?),
|
||||
_ => bs.push(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_hex_float(&mut self) -> io::Result<N> {
|
||||
if self.next_byte()? != b'"' {
|
||||
return Err(io_syntax_error(
|
||||
"Missing open-double-quote in hex-encoded floating-point number",
|
||||
));
|
||||
}
|
||||
let bs = self.read_hex_binary()?;
|
||||
if bs.len() != 8 {
|
||||
return Err(io_syntax_error(
|
||||
"Incorrect number of bytes in hex-encoded floating-point number",
|
||||
));
|
||||
}
|
||||
Ok(Value::from(f64::from_bits(u64::from_be_bytes(bs.try_into().unwrap()))).wrap())
|
||||
}
|
||||
|
||||
fn read_stringlike<X, H, R>(
|
||||
&mut self,
|
||||
mut seed: R,
|
||||
xform_item: X,
|
||||
terminator: u8,
|
||||
hexescape: u8,
|
||||
hexescaper: H,
|
||||
) -> io::Result<R>
|
||||
where
|
||||
X: Fn(&mut R, u8) -> io::Result<()>,
|
||||
H: Fn(&mut R, &mut Self) -> io::Result<()>,
|
||||
{
|
||||
loop {
|
||||
match self.next_byte()? {
|
||||
c if c == terminator => return Ok(seed),
|
||||
b'\\' => match self.next_byte()? {
|
||||
c if c == hexescape => hexescaper(&mut seed, self)?,
|
||||
c if c == terminator || c == b'\\' || c == b'/' => xform_item(&mut seed, c)?,
|
||||
b'b' => xform_item(&mut seed, b'\x08')?,
|
||||
b'f' => xform_item(&mut seed, b'\x0c')?,
|
||||
b'n' => xform_item(&mut seed, b'\x0a')?,
|
||||
b'r' => xform_item(&mut seed, b'\x0d')?,
|
||||
b't' => xform_item(&mut seed, b'\x09')?,
|
||||
_ => return Err(io_syntax_error("Invalid escape code")),
|
||||
},
|
||||
c => xform_item(&mut seed, c)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hexnum(&mut self, count: usize) -> io::Result<u32> {
|
||||
let mut v: u32 = 0;
|
||||
for _ in 0..count {
|
||||
let c = self.next_byte()?;
|
||||
match (c as char).to_digit(16) {
|
||||
Some(d) => v = v << 4 | d,
|
||||
None => return Err(io_syntax_error("Bad hex escape")),
|
||||
}
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn read_string(&mut self, delimiter: u8) -> io::Result<String> {
|
||||
decode_utf8(self.read_stringlike(
|
||||
Vec::new(),
|
||||
|bs, c| Ok(bs.push(c)),
|
||||
delimiter,
|
||||
b'u',
|
||||
|bs, r| {
|
||||
let n1 = r.hexnum(4)?;
|
||||
if (0xd800..=0xdbff).contains(&n1) {
|
||||
let mut ok = true;
|
||||
ok = ok && r.next_byte()? == b'\\';
|
||||
ok = ok && r.next_byte()? == b'u';
|
||||
if !ok {
|
||||
Err(io_syntax_error("Missing second half of surrogate pair"))
|
||||
} else {
|
||||
let n2 = r.hexnum(4)?;
|
||||
if (0xdc00..=0xdfff).contains(&n2) {
|
||||
let n = ((n1 - 0xd800) << 10) + (n2 - 0xdc00) + 0x10000;
|
||||
append_codepoint(bs, n)
|
||||
} else {
|
||||
Err(io_syntax_error("Bad second half of surrogate pair"))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
append_codepoint(bs, n1)
|
||||
}
|
||||
},
|
||||
)?)
|
||||
}
|
||||
|
||||
fn read_literal_binary(&mut self) -> io::Result<N> {
|
||||
Ok(N::new(
|
||||
&self.read_stringlike(
|
||||
Vec::new(),
|
||||
|bs, b| Ok(bs.push(b)),
|
||||
b'"',
|
||||
b'x',
|
||||
|bs, r| Ok(bs.push(r.hexnum(2)? as u8)),
|
||||
)?[..],
|
||||
))
|
||||
}
|
||||
|
||||
fn read_hex_binary(&mut self) -> io::Result<Vec<u8>> {
|
||||
let mut s = String::new();
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
let c1 = self.next_byte()? as char;
|
||||
if c1 == '"' {
|
||||
return Ok(hex::HexParser::Strict.decode(&s).unwrap());
|
||||
}
|
||||
let c2 = self.next_byte()? as char;
|
||||
if !(c1.is_digit(16) && c2.is_digit(16)) {
|
||||
return Err(io_syntax_error("Invalid hex binary"));
|
||||
}
|
||||
s.push(c1);
|
||||
s.push(c2);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_base64_binary(&mut self) -> io::Result<N> {
|
||||
let mut bs = Vec::new();
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
let mut c = self.next_byte()?;
|
||||
if c == b']' {
|
||||
let bs = base64::decode_config(&decode_utf8(bs)?, base64::STANDARD_NO_PAD)
|
||||
.map_err(|_| io_syntax_error("Invalid base64 character"))?;
|
||||
return Ok(N::new(&bs[..]));
|
||||
}
|
||||
if c == b'-' {
|
||||
c = b'+';
|
||||
}
|
||||
if c == b'_' {
|
||||
c = b'/';
|
||||
}
|
||||
if c == b'=' {
|
||||
continue;
|
||||
}
|
||||
bs.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
fn upto(&mut self, delimiter: u8, read_annotations: bool, skip_commas: bool) -> io::Result<Vec<N>> {
|
||||
let mut vs = Vec::new();
|
||||
loop {
|
||||
self.skip_whitespace_and_maybe_commas(skip_commas);
|
||||
if self.peek()? == delimiter {
|
||||
self.skip()?;
|
||||
return Ok(vs);
|
||||
}
|
||||
vs.push(Reader::<N>::demand_next(self, read_annotations)?);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_set(&mut self, read_annotations: bool) -> io::Result<N> {
|
||||
let items = self.upto(b'}', read_annotations, true)?;
|
||||
let mut s = Set::<N>::new();
|
||||
for i in items {
|
||||
if s.contains(&i) {
|
||||
return Err(io_syntax_error("Duplicate set element"));
|
||||
}
|
||||
s.insert(i);
|
||||
}
|
||||
Ok(N::new(s))
|
||||
}
|
||||
|
||||
fn read_dictionary(&mut self, read_annotations: bool) -> io::Result<N> {
|
||||
let mut d = Map::new();
|
||||
loop {
|
||||
self.skip_whitespace_and_maybe_commas(true);
|
||||
if self.peek()? == b'}' {
|
||||
self.skip()?;
|
||||
return Ok(N::new(d));
|
||||
}
|
||||
let k = Reader::<N>::demand_next(self, read_annotations)?;
|
||||
self.skip_whitespace();
|
||||
if self.next_byte()? != b':' {
|
||||
return Err(io_syntax_error("Missing expected key/value separator"));
|
||||
}
|
||||
if d.contains_key(&k) {
|
||||
return Err(io_syntax_error("Duplicate key"));
|
||||
}
|
||||
let v = Reader::<N>::demand_next(self, read_annotations)?;
|
||||
d.insert(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
fn require_delimiter(&mut self, msg: &'static str) -> io::Result<()> {
|
||||
if self.delimiter_follows()? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io_syntax_error(msg))
|
||||
}
|
||||
}
|
||||
|
||||
fn delimiter_follows(&mut self) -> io::Result<bool> {
|
||||
let c = match self.peek() {
|
||||
Err(e) if is_eof_io_error(&e) => return Ok(true),
|
||||
Err(e) => return Err(e)?,
|
||||
Ok(c) if (c as char).is_whitespace() => return Ok(true),
|
||||
Ok(c) => c,
|
||||
};
|
||||
Ok(match c {
|
||||
b'(' | b')' | b'{' | b'}' | b'[' | b']' | b'<' | b'>' | b'"' | b';' | b','
|
||||
| b'@' | b'#' | b':' | b'|' | b' ' => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_raw_symbol_or_number(&mut self, mut bs: Vec<u8>) -> io::Result<N> {
|
||||
lazy_static! {
|
||||
static ref NUMBER_RE: regex::Regex =
|
||||
regex::Regex::new(r"^([-+]?\d+)((\.\d+([eE][-+]?\d+)?)|([eE][-+]?\d+))?$")
|
||||
.unwrap();
|
||||
}
|
||||
while !self.delimiter_follows()? {
|
||||
bs.push(self.next_byte()?);
|
||||
}
|
||||
let s = decode_utf8(bs)?;
|
||||
match NUMBER_RE.captures(&s) {
|
||||
None => Ok(N::symbol(&s)),
|
||||
Some(m) => match m.get(2) {
|
||||
None => Ok(N::new(s.parse::<BigInt>().map_err(|_| {
|
||||
io_syntax_error(&format!("Invalid signed-integer number: {:?}", s))})?)),
|
||||
Some(_) => Ok(N::new(s.parse::<f64>().map_err(|_| {
|
||||
io_syntax_error(&format!("Invalid double-precision number: {:?}", s))})?)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'src, N: NestedValue, Dec: DomainParse<N::Embedded>, S: BinarySource<'de>> Reader<'de, N>
|
||||
for TextReader<'de, 'src, N, Dec, S>
|
||||
{
|
||||
fn next(&mut self, read_annotations: bool) -> io::Result<Option<N>> {
|
||||
'restart: loop {
|
||||
match self.toplevel_whitespace_mode {
|
||||
ToplevelWhitespaceMode::Document => self.skip_whitespace(),
|
||||
ToplevelWhitespaceMode::Value => (),
|
||||
}
|
||||
match self.peek() {
|
||||
Err(e) if is_eof_io_error(&e) => return Ok(None),
|
||||
_ => (),
|
||||
}
|
||||
match self.toplevel_whitespace_mode {
|
||||
ToplevelWhitespaceMode::Document => (),
|
||||
ToplevelWhitespaceMode::Value => self.skip_whitespace(),
|
||||
}
|
||||
return Ok(Some(match self.peek()? {
|
||||
b'"' => {
|
||||
self.skip()?;
|
||||
N::new(self.read_string(b'"')?)
|
||||
}
|
||||
b'|' => {
|
||||
self.skip()?;
|
||||
N::symbol(&self.read_string(b'|')?)
|
||||
}
|
||||
b';' => {
|
||||
return Err(io_syntax_error(
|
||||
"Semicolon is reserved syntax"
|
||||
));
|
||||
}
|
||||
b'@' => {
|
||||
if read_annotations {
|
||||
let mut annotations = Vec::new();
|
||||
self.gather_annotations(&mut annotations)?;
|
||||
self.prepend_annotations_to_next(annotations)?
|
||||
} else {
|
||||
self.skip_annotations()?;
|
||||
self.demand_next(read_annotations)?
|
||||
}
|
||||
}
|
||||
b':' => {
|
||||
return Err(io_syntax_error(
|
||||
"Unexpected key/value separator between items",
|
||||
));
|
||||
}
|
||||
b'#' => {
|
||||
self.skip()?;
|
||||
match self.next_byte()? {
|
||||
b' ' | b'\t' => {
|
||||
if read_annotations {
|
||||
let mut annotations = vec![N::new(self.comment_line()?)];
|
||||
self.gather_annotations(&mut annotations)?;
|
||||
self.prepend_annotations_to_next(annotations)?
|
||||
} else {
|
||||
self.comment_line()?;
|
||||
continue 'restart;
|
||||
}
|
||||
}
|
||||
b'\n' | b'\r' => {
|
||||
if read_annotations {
|
||||
let mut annotations = vec![N::new("")];
|
||||
self.gather_annotations(&mut annotations)?;
|
||||
self.prepend_annotations_to_next(annotations)?
|
||||
} else {
|
||||
continue 'restart;
|
||||
}
|
||||
}
|
||||
b'f' => { self.require_delimiter("Delimiter must follow #f")?; N::new(false) }
|
||||
b't' => { self.require_delimiter("Delimiter must follow #t")?; N::new(true) }
|
||||
b'{' => self.read_set(read_annotations)?,
|
||||
b'"' => self.read_literal_binary()?,
|
||||
b'x' => match self.next_byte()? {
|
||||
b'"' => N::new(&self.read_hex_binary()?[..]),
|
||||
b'd' => self.read_hex_float()?,
|
||||
_ => return Err(io_syntax_error("Invalid #x syntax")),
|
||||
},
|
||||
b'[' => self.read_base64_binary()?,
|
||||
b'!' => {
|
||||
let v = self.next_iovalue(read_annotations)?;
|
||||
Value::Embedded(self.dec.parse_embedded(&v)?).wrap()
|
||||
}
|
||||
other => {
|
||||
return Err(io_syntax_error(&format!("Invalid # syntax: {:?}", other)))
|
||||
}
|
||||
}
|
||||
}
|
||||
b'<' => {
|
||||
self.skip()?;
|
||||
let vs = self.upto(b'>', read_annotations, false)?;
|
||||
if vs.is_empty() {
|
||||
return Err(io_syntax_error("Missing record label"));
|
||||
}
|
||||
Value::Record(Record(vs)).wrap()
|
||||
}
|
||||
b'[' => {
|
||||
self.skip()?;
|
||||
N::new(self.upto(b']', read_annotations, true)?)
|
||||
}
|
||||
b'{' => {
|
||||
self.skip()?;
|
||||
self.read_dictionary(read_annotations)?
|
||||
}
|
||||
b'>' => return Err(io_syntax_error("Unexpected >")),
|
||||
b']' => return Err(io_syntax_error("Unexpected ]")),
|
||||
b'}' => return Err(io_syntax_error("Unexpected }")),
|
||||
b',' => return Err(io_syntax_error("Unexpected ,")),
|
||||
other => {
|
||||
self.skip()?;
|
||||
self.read_raw_symbol_or_number(vec![other])?
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn open_record(&mut self, arity: Option<usize>) -> ReaderResult<B::Type> {
|
||||
self.skip_annotations()?;
|
||||
if self.peek()? != b'<' {
|
||||
return Err(self.expected(ExpectedKind::Record(arity)));
|
||||
}
|
||||
self.skip()?;
|
||||
let mut b = B::Type::default();
|
||||
Reader::<N>::ensure_more_expected(self, &mut b, &B::Item::RecordLabel)?;
|
||||
Ok(b)
|
||||
}
|
||||
|
||||
fn open_sequence_or_set(&mut self) -> ReaderResult<B::Item> {
|
||||
self.skip_annotations()?;
|
||||
let mark = Reader::<N>::mark(self)?;
|
||||
match self.next_byte()? {
|
||||
b'#' => match self.next_byte()? {
|
||||
b'{' => return Ok(B::Item::SetValue),
|
||||
_ => (),
|
||||
},
|
||||
b'[' => return Ok(B::Item::SequenceValue),
|
||||
_ => (),
|
||||
}
|
||||
Reader::<N>::restore(self, &mark)?;
|
||||
Err(self.expected(ExpectedKind::SequenceOrSet))
|
||||
}
|
||||
|
||||
fn open_sequence(&mut self) -> ReaderResult<()> {
|
||||
self.skip_annotations()?;
|
||||
if self.peek()? != b'[' {
|
||||
return Err(self.expected(ExpectedKind::Sequence));
|
||||
}
|
||||
self.skip()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_set(&mut self) -> ReaderResult<()> {
|
||||
self.skip_annotations()?;
|
||||
let mark = Reader::<N>::mark(self)?;
|
||||
match self.next_byte()? {
|
||||
b'#' => match self.next_byte()? {
|
||||
b'{' => return Ok(()),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
Reader::<N>::restore(self, &mark)?;
|
||||
Err(self.expected(ExpectedKind::Set))
|
||||
}
|
||||
|
||||
fn open_dictionary(&mut self) -> ReaderResult<()> {
|
||||
self.skip_annotations()?;
|
||||
if self.peek()? != b'{' {
|
||||
return Err(self.expected(ExpectedKind::Dictionary));
|
||||
}
|
||||
self.skip()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn boundary(&mut self, b: &B::Type) -> ReaderResult<()> {
|
||||
match b {
|
||||
B::Type {
|
||||
closing: Some(B::Item::DictionaryKey),
|
||||
opening: Some(B::Item::DictionaryValue),
|
||||
} => {
|
||||
self.skip_whitespace();
|
||||
if self.next_byte()? != b':' {
|
||||
return Err(syntax_error("Missing expected key/value separator"));
|
||||
}
|
||||
}
|
||||
B::Type { closing: Some(B::Item::DictionaryValue), opening: None } |
|
||||
B::Type { closing: None, opening: Some(B::Item::DictionaryKey) } |
|
||||
B::Type { closing: Some(B::Item::SetValue), opening: _ } |
|
||||
B::Type { closing: _, opening: Some(B::Item::SetValue) } |
|
||||
B::Type { closing: Some(B::Item::SequenceValue), opening: _ } |
|
||||
B::Type { closing: _, opening: Some(B::Item::SequenceValue) } |
|
||||
B::Type {
|
||||
closing: Some(B::Item::DictionaryValue),
|
||||
opening: Some(B::Item::DictionaryKey),
|
||||
} => {
|
||||
self.skip_whitespace_and_maybe_commas(true);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_compound(&mut self, b: &mut B::Type, i: &B::Item) -> ReaderResult<bool> {
|
||||
self.skip_whitespace();
|
||||
match self.peek()? {
|
||||
b'>' | b']' | b'}' => {
|
||||
self.skip()?;
|
||||
Ok(true)
|
||||
}
|
||||
_ => {
|
||||
b.shift(Some(i.clone()));
|
||||
Reader::<N>::boundary(self, b)?;
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_embedded(&mut self) -> ReaderResult<()> {
|
||||
self.skip_annotations()?;
|
||||
let mark = Reader::<N>::mark(self)?;
|
||||
match self.next_byte()? {
|
||||
b'#' => match self.next_byte()? {
|
||||
b'!' => return Ok(()),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
Reader::<N>::restore(self, &mark)?;
|
||||
Err(self.expected(ExpectedKind::Embedded))
|
||||
}
|
||||
|
||||
fn close_embedded(&mut self) -> ReaderResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type Mark = S::Mark;
|
||||
|
||||
fn mark(&mut self) -> io::Result<Self::Mark> {
|
||||
self.source.mark()
|
||||
}
|
||||
|
||||
fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
|
||||
self.source.restore(mark)
|
||||
}
|
||||
|
||||
fn next_token(&mut self, read_embedded_annotations: bool) -> io::Result<Token<N>> {
|
||||
self.skip_annotations()?;
|
||||
let mark = Reader::<N>::mark(self)?;
|
||||
Ok(match self.next_byte()? {
|
||||
b'<' => Token::Compound(CompoundClass::Record),
|
||||
b'[' => Token::Compound(CompoundClass::Sequence),
|
||||
b'{' => Token::Compound(CompoundClass::Dictionary),
|
||||
b'>' => Token::End,
|
||||
b']' => Token::End,
|
||||
b'}' => Token::End,
|
||||
b'#' => match self.next_byte()? {
|
||||
b'!' => {
|
||||
let v = self.next_iovalue(read_embedded_annotations)?;
|
||||
Token::Embedded(self.dec.parse_embedded(&v)?)
|
||||
}
|
||||
b'{' => Token::Compound(CompoundClass::Set),
|
||||
_ => {
|
||||
Reader::<N>::restore(self, &mark)?;
|
||||
Token::Atom(self.demand_next(false)?)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Reader::<N>::restore(self, &mark)?;
|
||||
Token::Atom(self.demand_next(false)?)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn next_annotations_and_token(&mut self) -> io::Result<(Vec<N>, Token<N>)> {
|
||||
let mut annotations = Vec::new();
|
||||
self.gather_annotations(&mut annotations)?;
|
||||
Ok((annotations, self.next_token(true)?))
|
||||
}
|
||||
}
|
|
@ -1,379 +0,0 @@
|
|||
//! Implementation of [Writer] for the text syntax.
|
||||
|
||||
use crate::hex::HexFormatter;
|
||||
use crate::value::suspendable::Suspendable;
|
||||
use crate::value::writer::CompoundWriter;
|
||||
use crate::value::DomainEncode;
|
||||
use crate::value::IOValue;
|
||||
use crate::value::IOValueDomainCodec;
|
||||
use crate::value::NestedValue;
|
||||
use crate::value::Writer;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use num::bigint::BigInt;
|
||||
|
||||
use std::io;
|
||||
|
||||
use super::super::boundary as B;
|
||||
|
||||
/// Specifies a comma style for printing using [TextWriter].
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CommaStyle {
|
||||
/// No commas will be printed. (Preserves text syntax treats commas as whitespace (!).)
|
||||
None,
|
||||
/// Commas will be used to separate subterms.
|
||||
Separating,
|
||||
/// Commas will be used to terminate subterms.
|
||||
Terminating,
|
||||
}
|
||||
|
||||
/// The (optionally pretty-printing) text syntax Preserves writer.
|
||||
pub struct TextWriter<W: io::Write> {
|
||||
w: Suspendable<W>,
|
||||
/// Selects a comma style to use when printing.
|
||||
pub comma_style: CommaStyle,
|
||||
/// Specifies indentation to use when pretty-printing; 0 disables pretty-printing.
|
||||
pub indentation: usize,
|
||||
/// An aid to use of printed terms in shell scripts: set `true` to escape spaces embedded
|
||||
/// in strings and symbols.
|
||||
pub escape_spaces: bool,
|
||||
indent: String,
|
||||
}
|
||||
|
||||
impl std::default::Default for CommaStyle {
|
||||
fn default() -> Self {
|
||||
CommaStyle::Separating
|
||||
}
|
||||
}
|
||||
|
||||
impl TextWriter<&mut Vec<u8>> {
|
||||
/// Writes `v` to `f` using text syntax. Selects indentation mode based on
|
||||
/// [`f.alternate()`][std::fmt::Formatter::alternate].
|
||||
pub fn fmt_value<N: NestedValue, Enc: DomainEncode<N::Embedded>>(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
enc: &mut Enc,
|
||||
v: &crate::value::Value<N>,
|
||||
) -> io::Result<()> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
let mut w = TextWriter::new(&mut buf);
|
||||
if f.alternate() {
|
||||
w.indentation = 4
|
||||
}
|
||||
w.write_value(enc, v)?;
|
||||
f.write_str(std::str::from_utf8(&buf).expect("valid UTF-8 from TextWriter"))
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "could not append to Formatter"))
|
||||
}
|
||||
|
||||
/// Encode `v` to a [String].
|
||||
pub fn encode<N: NestedValue, Enc: DomainEncode<N::Embedded>>(
|
||||
enc: &mut Enc,
|
||||
v: &N,
|
||||
) -> io::Result<String> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
TextWriter::new(&mut buf).write(enc, v)?;
|
||||
Ok(String::from_utf8(buf).expect("valid UTF-8 from TextWriter"))
|
||||
}
|
||||
|
||||
/// Encode `v` to a [String].
|
||||
pub fn encode_iovalue(v: &IOValue) -> io::Result<String> {
|
||||
Self::encode(&mut IOValueDomainCodec, v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> TextWriter<W> {
|
||||
/// Construct a writer from the given byte sink `w`.
|
||||
pub fn new(w: W) -> Self {
|
||||
TextWriter {
|
||||
w: Suspendable::new(w),
|
||||
comma_style: CommaStyle::default(),
|
||||
indentation: 0,
|
||||
escape_spaces: false,
|
||||
indent: "\n".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update selected comma-printing style.
|
||||
pub fn set_comma_style(mut self, v: CommaStyle) -> Self {
|
||||
self.comma_style = v;
|
||||
self
|
||||
}
|
||||
|
||||
/// Update selected space-escaping style.
|
||||
pub fn set_escape_spaces(mut self, v: bool) -> Self {
|
||||
self.escape_spaces = v;
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn suspend(&mut self) -> Self {
|
||||
TextWriter {
|
||||
w: self.w.suspend(),
|
||||
indent: self.indent.clone(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn resume(&mut self, other: Self) {
|
||||
self.w.resume(other.w)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn write_stringlike_char_fallback<F>(&mut self, c: char, f: F) -> io::Result<()>
|
||||
where
|
||||
F: FnOnce(&mut W, char) -> io::Result<()>,
|
||||
{
|
||||
match c {
|
||||
'\\' => write!(self.w, "\\\\"),
|
||||
'\x08' => write!(self.w, "\\b"),
|
||||
'\x0c' => write!(self.w, "\\f"),
|
||||
'\x0a' => write!(self.w, "\\n"),
|
||||
'\x0d' => write!(self.w, "\\r"),
|
||||
'\x09' => write!(self.w, "\\t"),
|
||||
_ => f(&mut self.w, c),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn write_stringlike_char(&mut self, c: char) -> io::Result<()> {
|
||||
self.write_stringlike_char_fallback(c, |w, c| write!(w, "{}", c))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn add_indent(&mut self) {
|
||||
for _ in 0..self.indentation {
|
||||
self.indent.push(' ')
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn del_indent(&mut self) {
|
||||
if self.indentation > 0 {
|
||||
self.indent.truncate(self.indent.len() - self.indentation)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn indent(&mut self) -> io::Result<()> {
|
||||
if self.indentation > 0 {
|
||||
write!(self.w, "{}", &self.indent)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn indent_sp(&mut self) -> io::Result<()> {
|
||||
if self.indentation > 0 {
|
||||
write!(self.w, "{}", &self.indent)
|
||||
} else {
|
||||
write!(self.w, " ")
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow the underlying byte sink.
|
||||
pub fn borrow_write(&mut self) -> &mut W {
|
||||
&mut self.w
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> CompoundWriter for TextWriter<W> {
|
||||
#[inline]
|
||||
fn boundary(&mut self, b: &B::Type) -> io::Result<()> {
|
||||
match (b.closing.as_ref(), b.opening.as_ref()) {
|
||||
(None, Some(B::Item::RecordLabel))
|
||||
| (Some(B::Item::RecordLabel), None)
|
||||
| (Some(B::Item::RecordField), None) => return Ok(()),
|
||||
(_, Some(B::Item::RecordField)) => return write!(self.w, " "),
|
||||
|
||||
(Some(B::Item::DictionaryKey), Some(B::Item::DictionaryValue)) => {
|
||||
return write!(self.w, ": ")
|
||||
}
|
||||
|
||||
(None, Some(B::Item::Annotation)) => return write!(self.w, "@"),
|
||||
(Some(_), Some(B::Item::Annotation)) => return write!(self.w, " @"),
|
||||
(Some(B::Item::Annotation), Some(B::Item::AnnotatedValue)) => {
|
||||
return write!(self.w, " ")
|
||||
}
|
||||
(Some(B::Item::AnnotatedValue), None) => return Ok(()),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match (b.closing.as_ref(), b.opening.as_ref()) {
|
||||
(None, None) => (),
|
||||
(None, Some(_)) => {
|
||||
self.add_indent();
|
||||
self.indent()?
|
||||
}
|
||||
(Some(_), Some(_)) => {
|
||||
match self.comma_style {
|
||||
CommaStyle::Separating | CommaStyle::Terminating => write!(self.w, ",")?,
|
||||
CommaStyle::None => (),
|
||||
}
|
||||
self.indent_sp()?
|
||||
}
|
||||
(Some(_), None) => {
|
||||
match self.comma_style {
|
||||
CommaStyle::Terminating => write!(self.w, ",")?,
|
||||
CommaStyle::Separating | CommaStyle::None => (),
|
||||
}
|
||||
self.del_indent();
|
||||
self.indent()?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! simple_writer_method {
|
||||
($n:ident, $argty:ty) => {
|
||||
fn $n(&mut self, v: $argty) -> io::Result<()> {
|
||||
write!(self.w, "{}", v)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<W: io::Write> Writer for TextWriter<W> {
|
||||
type AnnWriter = Self;
|
||||
type RecWriter = Self;
|
||||
type SeqWriter = Self;
|
||||
type SetWriter = Self;
|
||||
type DictWriter = Self;
|
||||
type EmbeddedWriter = Self;
|
||||
|
||||
fn start_annotations(&mut self) -> io::Result<Self::AnnWriter> {
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_annotations(&mut self, ann: Self::AnnWriter) -> io::Result<()> {
|
||||
self.resume(ann);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_bool(&mut self, v: bool) -> io::Result<()> {
|
||||
write!(self.w, "{}", if v { "#t" } else { "#f" })
|
||||
}
|
||||
|
||||
fn write_f64(&mut self, v: f64) -> io::Result<()> {
|
||||
if v.is_nan() || v.is_infinite() {
|
||||
write!(
|
||||
self.w,
|
||||
"#xd\"{}\"",
|
||||
HexFormatter::Packed.encode(&u64::to_be_bytes(f64::to_bits(v)))
|
||||
)
|
||||
} else {
|
||||
dtoa::write(&mut *self.w, v)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
simple_writer_method!(write_i8, i8);
|
||||
simple_writer_method!(write_u8, u8);
|
||||
simple_writer_method!(write_i16, i16);
|
||||
simple_writer_method!(write_u16, u16);
|
||||
simple_writer_method!(write_i32, i32);
|
||||
simple_writer_method!(write_u32, u32);
|
||||
simple_writer_method!(write_i64, i64);
|
||||
simple_writer_method!(write_u64, u64);
|
||||
simple_writer_method!(write_i128, i128);
|
||||
simple_writer_method!(write_u128, u128);
|
||||
simple_writer_method!(write_int, &BigInt);
|
||||
|
||||
fn write_string(&mut self, v: &str) -> io::Result<()> {
|
||||
write!(self.w, "\"")?;
|
||||
for c in v.chars() {
|
||||
match c {
|
||||
'"' => write!(self.w, "\\\"")?,
|
||||
' ' if self.escape_spaces => write!(self.w, "\\u0020")?,
|
||||
_ => self.write_stringlike_char(c)?,
|
||||
}
|
||||
}
|
||||
write!(self.w, "\"")
|
||||
}
|
||||
|
||||
fn write_bytes(&mut self, v: &[u8]) -> io::Result<()> {
|
||||
write!(
|
||||
self.w,
|
||||
"#[{}]",
|
||||
base64::encode_config(v, base64::URL_SAFE_NO_PAD)
|
||||
)
|
||||
}
|
||||
|
||||
fn write_symbol(&mut self, v: &str) -> io::Result<()> {
|
||||
lazy_static! {
|
||||
// FIXME: This regular expression is conservatively correct, but Anglo-chauvinistic.
|
||||
static ref RE: regex::Regex =
|
||||
regex::Regex::new("^[-a-zA-Z0-9~!$%^&*?_=+/.]+$").unwrap();
|
||||
}
|
||||
if RE.is_match(v) {
|
||||
write!(self.w, "{}", v)
|
||||
} else {
|
||||
write!(self.w, "|")?;
|
||||
for c in v.chars() {
|
||||
match c {
|
||||
'|' => write!(self.w, "\\|")?,
|
||||
' ' if self.escape_spaces => write!(self.w, "\\u0020")?,
|
||||
_ => self.write_stringlike_char(c)?,
|
||||
}
|
||||
}
|
||||
write!(self.w, "|")
|
||||
}
|
||||
}
|
||||
|
||||
fn start_record(&mut self, _field_count: Option<usize>) -> io::Result<Self::RecWriter> {
|
||||
write!(self.w, "<")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_record(&mut self, rec: Self::RecWriter) -> io::Result<()> {
|
||||
self.resume(rec);
|
||||
write!(self.w, ">")
|
||||
}
|
||||
|
||||
fn start_sequence(&mut self, _item_count: Option<usize>) -> io::Result<Self::SeqWriter> {
|
||||
write!(self.w, "[")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_sequence(&mut self, seq: Self::SeqWriter) -> io::Result<()> {
|
||||
self.resume(seq);
|
||||
write!(self.w, "]")
|
||||
}
|
||||
|
||||
fn start_set(&mut self, _item_count: Option<usize>) -> io::Result<Self::SetWriter> {
|
||||
write!(self.w, "#{{")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_set(&mut self, set: Self::SetWriter) -> io::Result<()> {
|
||||
self.resume(set);
|
||||
write!(self.w, "}}")
|
||||
}
|
||||
|
||||
fn start_dictionary(&mut self, _entry_count: Option<usize>) -> io::Result<Self::DictWriter> {
|
||||
write!(self.w, "{{")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_dictionary(&mut self, dict: Self::DictWriter) -> io::Result<()> {
|
||||
self.resume(dict);
|
||||
write!(self.w, "}}")
|
||||
}
|
||||
|
||||
fn start_embedded(&mut self) -> io::Result<Self::EmbeddedWriter> {
|
||||
write!(self.w, "#!")?;
|
||||
Ok(self.suspend())
|
||||
}
|
||||
|
||||
fn end_embedded(&mut self, ptr: Self::EmbeddedWriter) -> io::Result<()> {
|
||||
self.resume(ptr);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.w.flush()
|
||||
}
|
||||
}
|
|
@ -1,232 +0,0 @@
|
|||
//! Generic [Writer] trait for unparsing Preserves [Value]s, implemented by code that provides
|
||||
//! each specific transfer syntax.
|
||||
|
||||
use super::boundary as B;
|
||||
use super::repr::{Double, NestedValue, Value};
|
||||
use super::signed_integer::SignedIntegerRepr;
|
||||
use super::DomainEncode;
|
||||
use num::bigint::BigInt;
|
||||
use std::io;
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Utility trait for tracking unparser state during production of compound `Value`s.
|
||||
pub trait CompoundWriter: Writer {
|
||||
fn boundary(&mut self, b: &B::Type) -> io::Result<()>;
|
||||
}
|
||||
|
||||
/// Generic unparser for Preserves.
|
||||
pub trait Writer: Sized {
|
||||
// Hiding these from the documentation for the moment because I don't want to have to
|
||||
// document the whole Boundary thing.
|
||||
#[doc(hidden)]
|
||||
type AnnWriter: CompoundWriter;
|
||||
#[doc(hidden)]
|
||||
type RecWriter: CompoundWriter;
|
||||
#[doc(hidden)]
|
||||
type SeqWriter: CompoundWriter;
|
||||
#[doc(hidden)]
|
||||
type SetWriter: CompoundWriter;
|
||||
#[doc(hidden)]
|
||||
type DictWriter: CompoundWriter;
|
||||
#[doc(hidden)]
|
||||
type EmbeddedWriter: Writer;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn start_annotations(&mut self) -> io::Result<Self::AnnWriter>;
|
||||
#[doc(hidden)]
|
||||
fn end_annotations(&mut self, ann: Self::AnnWriter) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn write_bool(&mut self, v: bool) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn write_f64(&mut self, v: f64) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn write_i8(&mut self, v: i8) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_u8(&mut self, v: u8) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_i16(&mut self, v: i16) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_u16(&mut self, v: u16) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_i32(&mut self, v: i32) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_u32(&mut self, v: u32) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_i64(&mut self, v: i64) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_u64(&mut self, v: u64) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_i128(&mut self, v: i128) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_u128(&mut self, v: u128) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_int(&mut self, v: &BigInt) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn write_string(&mut self, v: &str) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_bytes(&mut self, v: &[u8]) -> io::Result<()>;
|
||||
#[doc(hidden)]
|
||||
fn write_symbol(&mut self, v: &str) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn start_record(&mut self, field_count: Option<usize>) -> io::Result<Self::RecWriter>;
|
||||
#[doc(hidden)]
|
||||
fn end_record(&mut self, rec: Self::RecWriter) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn start_sequence(&mut self, item_count: Option<usize>) -> io::Result<Self::SeqWriter>;
|
||||
#[doc(hidden)]
|
||||
fn end_sequence(&mut self, seq: Self::SeqWriter) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn start_set(&mut self, item_count: Option<usize>) -> io::Result<Self::SetWriter>;
|
||||
#[doc(hidden)]
|
||||
fn end_set(&mut self, set: Self::SetWriter) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn start_dictionary(&mut self, entry_count: Option<usize>) -> io::Result<Self::DictWriter>;
|
||||
#[doc(hidden)]
|
||||
fn end_dictionary(&mut self, dict: Self::DictWriter) -> io::Result<()>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn start_embedded(&mut self) -> io::Result<Self::EmbeddedWriter>;
|
||||
#[doc(hidden)]
|
||||
fn end_embedded(&mut self, ptr: Self::EmbeddedWriter) -> io::Result<()>;
|
||||
|
||||
/// Flushes any buffered output.
|
||||
fn flush(&mut self) -> io::Result<()>;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/// Writes [NestedValue] `v` to the output of this [Writer].
|
||||
fn write<N: NestedValue, Enc: DomainEncode<N::Embedded>>(
|
||||
&mut self,
|
||||
enc: &mut Enc,
|
||||
v: &N,
|
||||
) -> io::Result<()> {
|
||||
match v.annotations().maybe_slice() {
|
||||
None => {
|
||||
self.write_value(enc, v.value())?;
|
||||
}
|
||||
Some(anns) => {
|
||||
let mut a = self.start_annotations()?;
|
||||
let mut b = B::Type::default();
|
||||
for ann in anns {
|
||||
b.shift(Some(B::Item::Annotation));
|
||||
a.boundary(&b)?;
|
||||
a.write(enc, ann)?;
|
||||
}
|
||||
b.shift(Some(B::Item::AnnotatedValue));
|
||||
a.boundary(&b)?;
|
||||
a.write_value(enc, v.value())?;
|
||||
b.shift(None);
|
||||
a.boundary(&b)?;
|
||||
self.end_annotations(a)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes [Value] `v` to the output of this [Writer].
|
||||
fn write_value<N: NestedValue, Enc: DomainEncode<N::Embedded>>(
|
||||
&mut self,
|
||||
enc: &mut Enc,
|
||||
v: &Value<N>,
|
||||
) -> io::Result<()> {
|
||||
match v {
|
||||
Value::Boolean(b) => self.write_bool(*b),
|
||||
Value::Double(Double(d)) => self.write_f64(*d),
|
||||
Value::SignedInteger(n) => match n.repr() {
|
||||
SignedIntegerRepr::I128(i) => self.write_i128(*i),
|
||||
SignedIntegerRepr::U128(u) => self.write_u128(*u),
|
||||
SignedIntegerRepr::Big(n) => self.write_int(n),
|
||||
},
|
||||
Value::String(s) => self.write_string(s),
|
||||
Value::ByteString(bs) => self.write_bytes(bs),
|
||||
Value::Symbol(s) => self.write_symbol(s),
|
||||
Value::Record(r) => {
|
||||
let mut c = self.start_record(Some(r.arity()))?;
|
||||
let mut b = B::start(B::Item::RecordLabel);
|
||||
c.boundary(&b)?;
|
||||
c.write(enc, r.label())?;
|
||||
for f in r.fields() {
|
||||
b.shift(Some(B::Item::RecordField));
|
||||
c.boundary(&b)?;
|
||||
c.write(enc, f)?;
|
||||
}
|
||||
b.shift(None);
|
||||
c.boundary(&b)?;
|
||||
self.end_record(c)
|
||||
}
|
||||
Value::Sequence(vs) => {
|
||||
let mut c = self.start_sequence(Some(vs.len()))?;
|
||||
let mut b = B::Type::default();
|
||||
for v in vs {
|
||||
b.shift(Some(B::Item::SequenceValue));
|
||||
c.boundary(&b)?;
|
||||
c.write(enc, v)?;
|
||||
}
|
||||
b.shift(None);
|
||||
c.boundary(&b)?;
|
||||
self.end_sequence(c)
|
||||
}
|
||||
Value::Set(vs) => {
|
||||
let mut c = self.start_set(Some(vs.len()))?;
|
||||
let mut b = B::Type::default();
|
||||
for v in vs {
|
||||
b.shift(Some(B::Item::SetValue));
|
||||
c.boundary(&b)?;
|
||||
c.write(enc, v)?;
|
||||
}
|
||||
b.shift(None);
|
||||
c.boundary(&b)?;
|
||||
self.end_set(c)
|
||||
}
|
||||
Value::Dictionary(vs) => {
|
||||
let mut c = self.start_dictionary(Some(vs.len()))?;
|
||||
let mut b = B::Type::default();
|
||||
for (k, v) in vs {
|
||||
b.shift(Some(B::Item::DictionaryKey));
|
||||
c.boundary(&b)?;
|
||||
c.write(enc, k)?;
|
||||
b.shift(Some(B::Item::DictionaryValue));
|
||||
c.boundary(&b)?;
|
||||
c.write(enc, v)?;
|
||||
}
|
||||
b.shift(None);
|
||||
c.boundary(&b)?;
|
||||
self.end_dictionary(c)
|
||||
}
|
||||
Value::Embedded(d) => {
|
||||
let mut c = self.start_embedded()?;
|
||||
enc.encode_embedded(&mut c, d)?;
|
||||
self.end_embedded(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a [varint](https://protobuf.dev/programming-guides/encoding/#varints) to `w`.
|
||||
/// Returns the number of bytes written.
|
||||
///
|
||||
/// ```text
|
||||
/// varint(n) = [n] if n < 128
|
||||
/// [(n & 127) | 128] ++ varint(n >> 7) if n ≥ 128
|
||||
/// ```
|
||||
pub fn varint<W: io::Write>(w: &mut W, mut v: u64) -> io::Result<usize> {
|
||||
let mut byte_count = 0;
|
||||
loop {
|
||||
byte_count += 1;
|
||||
if v < 128 {
|
||||
w.write_all(&[v as u8])?;
|
||||
return Ok(byte_count);
|
||||
} else {
|
||||
w.write_all(&[((v & 0x7f) + 128) as u8])?;
|
||||
v >>= 7;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
use preserves::symbol::Symbol;
|
||||
use preserves::value::{IOValue, Map};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TestCases {
|
||||
pub tests: Map<Symbol, TestCase>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum TestCase {
|
||||
Test(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
|
||||
NondeterministicTest(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
|
||||
ParseError(String),
|
||||
ParseShort(String),
|
||||
ParseEOF(String),
|
||||
DecodeError(#[serde(with = "serde_bytes")] Vec<u8>),
|
||||
DecodeShort(#[serde(with = "serde_bytes")] Vec<u8>),
|
||||
DecodeEOF(#[serde(with = "serde_bytes")] Vec<u8>),
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
use preserves::error::{is_eof_io_error, is_syntax_io_error};
|
||||
use preserves::symbol::Symbol;
|
||||
use preserves::value::de::from_value as deserialize_from_value;
|
||||
use preserves::value::BinarySource;
|
||||
use preserves::value::BytesBinarySource;
|
||||
use preserves::value::IOBinarySource;
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::PackedWriter;
|
||||
use preserves::value::Reader;
|
||||
use preserves::value::ToplevelWhitespaceMode;
|
||||
use std::io;
|
||||
use std::iter::Iterator;
|
||||
|
||||
mod samples;
|
||||
use samples::*;
|
||||
|
||||
fn decode_all(bytes: &'_ [u8]) -> io::Result<Vec<IOValue>> {
|
||||
BytesBinarySource::new(bytes)
|
||||
.packed_iovalues()
|
||||
.configured(true)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_all(text: &str) -> io::Result<Vec<IOValue>> {
|
||||
BytesBinarySource::new(text.as_bytes())
|
||||
.text_iovalues()
|
||||
.configured(true)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare_text_with_packed() -> io::Result<()> {
|
||||
use io::prelude::*;
|
||||
let from_text = {
|
||||
let mut fh = std::fs::File::open("../../../tests/samples.pr").unwrap();
|
||||
let mut contents = String::new();
|
||||
fh.read_to_string(&mut contents)?;
|
||||
BytesBinarySource::new(contents.as_bytes())
|
||||
.text_iovalues()
|
||||
.demand_next(true)?
|
||||
};
|
||||
let from_packed = {
|
||||
let mut fh = std::fs::File::open("../../../tests/samples.bin").unwrap();
|
||||
IOBinarySource::new(&mut fh)
|
||||
.packed_iovalues()
|
||||
.demand_next(true)?
|
||||
};
|
||||
assert_eq!(from_text, from_packed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare_deserialize_text_with_packed() -> io::Result<()> {
|
||||
use io::prelude::*;
|
||||
let from_text = {
|
||||
let mut fh = std::fs::File::open("../../../tests/samples.pr").unwrap();
|
||||
let mut contents = String::new();
|
||||
fh.read_to_string(&mut contents)?;
|
||||
let tests: TestCases = preserves::de::from_text(&contents)?;
|
||||
tests
|
||||
};
|
||||
let from_packed = {
|
||||
let mut fh = std::fs::File::open("../../../tests/samples.bin").unwrap();
|
||||
let tests: TestCases = preserves::de::from_read(&mut fh)?;
|
||||
tests
|
||||
};
|
||||
assert_eq!(from_text, from_packed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_write_read_text() -> io::Result<()> {
|
||||
use io::prelude::*;
|
||||
let from_text = {
|
||||
let mut fh = std::fs::File::open("../../../tests/samples.pr").unwrap();
|
||||
let mut contents = String::new();
|
||||
fh.read_to_string(&mut contents)?;
|
||||
preserves::value::text::annotated_iovalue_from_str(&contents)?
|
||||
};
|
||||
let roundtripped = {
|
||||
let mut bs = Vec::new();
|
||||
let mut w = preserves::value::TextWriter::new(&mut bs);
|
||||
preserves::ser::to_writer(&mut w, &from_text)?;
|
||||
let s = String::from_utf8(bs).unwrap();
|
||||
preserves::value::text::annotated_iovalue_from_str(&s)?
|
||||
};
|
||||
let roundtripped_indented = {
|
||||
let mut bs = Vec::new();
|
||||
let mut w = preserves::value::TextWriter::new(&mut bs);
|
||||
w.indentation = 4;
|
||||
preserves::ser::to_writer(&mut w, &from_text)?;
|
||||
let s = String::from_utf8(bs).unwrap();
|
||||
preserves::value::text::annotated_iovalue_from_str(&s)?
|
||||
};
|
||||
assert_eq!(from_text, roundtripped);
|
||||
assert_eq!(from_text, roundtripped_indented);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_serialize_deserialize_text() -> io::Result<()> {
|
||||
use io::prelude::*;
|
||||
let from_text = {
|
||||
let mut fh = std::fs::File::open("../../../tests/samples.pr").unwrap();
|
||||
let mut contents = String::new();
|
||||
fh.read_to_string(&mut contents)?;
|
||||
let tests: TestCases = preserves::de::from_text(&contents)?;
|
||||
tests
|
||||
};
|
||||
let roundtripped = {
|
||||
let mut bs = Vec::new();
|
||||
let mut w = preserves::value::TextWriter::new(&mut bs);
|
||||
preserves::ser::to_writer(&mut w, &from_text)?;
|
||||
let s = String::from_utf8(bs).unwrap();
|
||||
preserves::de::from_text(&s)?
|
||||
};
|
||||
assert_eq!(from_text, roundtripped);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run() -> io::Result<()> {
|
||||
let mut fh = std::fs::File::open("../../../tests/samples.bin").unwrap();
|
||||
let mut src = IOBinarySource::new(&mut fh);
|
||||
let mut d = src.packed_iovalues().configured(true);
|
||||
let tests: TestCases = deserialize_from_value(&d.next().unwrap().unwrap()).unwrap();
|
||||
|
||||
for (Symbol(name), case) in tests.tests {
|
||||
println!("{:?} ==> {:?}", name, case);
|
||||
match case {
|
||||
TestCase::Test(bin, val) => {
|
||||
assert_eq!(
|
||||
&decode_all(&PackedWriter::encode_iovalue(&val)?[..])?,
|
||||
&[val.clone()]
|
||||
);
|
||||
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
|
||||
assert_eq!(&PackedWriter::encode_iovalue(&val)?, &bin);
|
||||
}
|
||||
TestCase::NondeterministicTest(bin, val) => {
|
||||
assert_eq!(&PackedWriter::encode_iovalue(&val)?, &bin);
|
||||
assert_eq!(
|
||||
&decode_all(&PackedWriter::encode_iovalue(&val)?[..])?,
|
||||
&[val.clone()]
|
||||
);
|
||||
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
|
||||
}
|
||||
TestCase::ParseError(text) => {
|
||||
match parse_all(&text) {
|
||||
Ok(_) => panic!("Unexpected success"),
|
||||
Err(e) => {
|
||||
if is_syntax_io_error(&e) {
|
||||
// all is OK
|
||||
} else {
|
||||
panic!("Unexpected error {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TestCase::ParseShort(text) => {
|
||||
assert!(if let Err(e) = BytesBinarySource::new(text.as_bytes())
|
||||
.text_iovalues()
|
||||
.toplevel_whitespace_mode(ToplevelWhitespaceMode::Value)
|
||||
.configured(true)
|
||||
.next()
|
||||
.unwrap()
|
||||
{
|
||||
is_eof_io_error(&e)
|
||||
} else {
|
||||
false
|
||||
})
|
||||
}
|
||||
TestCase::ParseEOF(text) => {
|
||||
assert!(BytesBinarySource::new(text.as_bytes())
|
||||
.text_iovalues()
|
||||
.toplevel_whitespace_mode(ToplevelWhitespaceMode::Value)
|
||||
.configured(true)
|
||||
.next()
|
||||
.is_none());
|
||||
}
|
||||
TestCase::DecodeError(bin) => {
|
||||
match decode_all(&bin[..]) {
|
||||
Ok(_) => panic!("Unexpected success"),
|
||||
Err(e) => {
|
||||
if is_syntax_io_error(&e) {
|
||||
// all is OK
|
||||
} else {
|
||||
panic!("Unexpected error {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TestCase::DecodeShort(bin) => {
|
||||
assert!(if let Err(e) = BytesBinarySource::new(&bin)
|
||||
.packed_iovalues()
|
||||
.configured(true)
|
||||
.next()
|
||||
.unwrap()
|
||||
{
|
||||
is_eof_io_error(&e)
|
||||
} else {
|
||||
false
|
||||
})
|
||||
}
|
||||
TestCase::DecodeEOF(bin) => {
|
||||
assert!(BytesBinarySource::new(&bin)
|
||||
.packed_iovalues()
|
||||
.configured(true)
|
||||
.next()
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
consolidate-commits=true
|
||||
consolidate-pushes=true
|
||||
tag-prefix="rust-{{crate_name}}"
|
||||
tag-name="{{prefix}}@{{version}}"
|
||||
dev-version-ext="dev.0"
|
|
@ -31,7 +31,7 @@ object-oriented languages like Java, [Python][python-impl] and
|
|||
[nim-impl]: https://git.syndicate-lang.org/ehmry/preserves-nim
|
||||
[python-impl]: https://gitlab.com/preserves/preserves/-/blob/main/implementations/python/preserves/schema.py
|
||||
[racket-impl]: https://gitlab.com/preserves/preserves/-/tree/main/implementations/racket/preserves/preserves-schema
|
||||
[rust-impl]: https://gitlab.com/preserves/preserves/-/tree/main/implementations/rust/preserves-schema
|
||||
[rust-impl]: https://gitlab.com/preserves/preserves-rs/-/tree/main/preserves-schema
|
||||
[smalltalk-impl]: https://squeaksource.com/Preserves.html
|
||||
[ts-impl]: https://gitlab.com/preserves/preserves/-/tree/main/implementations/javascript/packages/schema
|
||||
|
||||
|
|
Loading…
Reference in New Issue