mirror of
https://github.com/roc-lang/roc.git
synced 2025-11-17 11:03:15 +00:00
Merge pull request #3748 from rtfeldman/serde
Add serde serializers/deserializers to roc_std
This commit is contained in:
commit
07eed2c4a6
6 changed files with 219 additions and 6 deletions
|
|
@ -64,7 +64,7 @@ build-rust-test:
|
||||||
RUN apt -y install gcc-10 g++-10 && rm /usr/bin/gcc && ln -s /usr/bin/gcc-10 /usr/bin/gcc # gcc-9 maybe causes segfault
|
RUN apt -y install gcc-10 g++-10 && rm /usr/bin/gcc && ln -s /usr/bin/gcc-10 /usr/bin/gcc # gcc-9 maybe causes segfault
|
||||||
RUN gcc --version
|
RUN gcc --version
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --locked --release --features with_sound --workspace --no-run && sccache --show-stats
|
cargo test --locked --release --features with_sound serde --workspace --no-run && sccache --show-stats
|
||||||
|
|
||||||
check-typos:
|
check-typos:
|
||||||
RUN cargo install typos-cli --version 1.0.11 # version set to prevent confusion if the version is updated automatically
|
RUN cargo install typos-cli --version 1.0.11 # version set to prevent confusion if the version is updated automatically
|
||||||
|
|
@ -82,7 +82,7 @@ test-rust:
|
||||||
RUN gcc --version
|
RUN gcc --version
|
||||||
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --locked --release --features with_sound --workspace && sccache --show-stats
|
cargo test --locked --release --features with_sound serde --workspace && sccache --show-stats
|
||||||
# test the dev and wasm backend: they require an explicit feature flag.
|
# test the dev and wasm backend: they require an explicit feature flag.
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --locked --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats
|
cargo test --locked --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats
|
||||||
|
|
@ -99,7 +99,7 @@ test-rust:
|
||||||
# NOTE: disabled until zig 0.9
|
# NOTE: disabled until zig 0.9
|
||||||
# RUN echo "4" | cargo run --locked --release --features="target-x86" -- --target=x86_32 examples/benchmarks/NQueens.roc
|
# RUN echo "4" | cargo run --locked --release --features="target-x86" -- --target=x86_32 examples/benchmarks/NQueens.roc
|
||||||
# RUN --mount=type=cache,target=$SCCACHE_DIR \
|
# RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
# cargo test --locked --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
# cargo test --locked --release --features with_sound serde --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
||||||
# make sure website deployment works (that is, make sure build.sh returns status code 0)
|
# make sure website deployment works (that is, make sure build.sh returns status code 0)
|
||||||
ENV REPL_DEBUG=1
|
ENV REPL_DEBUG=1
|
||||||
RUN bash www/build.sh
|
RUN bash www/build.sh
|
||||||
|
|
@ -126,7 +126,7 @@ build-nightly-release:
|
||||||
COPY --dir .git LICENSE LEGAL_DETAILS ci ./
|
COPY --dir .git LICENSE LEGAL_DETAILS ci ./
|
||||||
# version.txt is used by the CLI: roc --version
|
# version.txt is used by the CLI: roc --version
|
||||||
RUN ./ci/write_version.sh
|
RUN ./ci/write_version.sh
|
||||||
RUN RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release
|
RUN RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound serde --release
|
||||||
RUN ./ci/package_release.sh roc_linux_x86_64.tar.gz
|
RUN ./ci/package_release.sh roc_linux_x86_64.tar.gz
|
||||||
SAVE ARTIFACT ./roc_linux_x86_64.tar.gz AS LOCAL roc_linux_x86_64.tar.gz
|
SAVE ARTIFACT ./roc_linux_x86_64.tar.gz AS LOCAL roc_linux_x86_64.tar.gz
|
||||||
|
|
||||||
|
|
|
||||||
33
crates/roc_std/Cargo.lock
generated
33
crates/roc_std/Cargo.lock
generated
|
|
@ -78,6 +78,12 @@ dependencies = [
|
||||||
"unindent",
|
"unindent",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.119"
|
version = "0.2.119"
|
||||||
|
|
@ -197,7 +203,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
|
@ -205,9 +211,34 @@ dependencies = [
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.143"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,16 @@ version = "0.0.1"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
arrayvec = "0.7.2"
|
arrayvec = "0.7.2"
|
||||||
|
serde = { version = "1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
|
libc = "0.2.106"
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "1.0.0"
|
quickcheck_macros = "1.0.0"
|
||||||
libc = "0.2.106"
|
serde_json = "1.0.83"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = []
|
std = []
|
||||||
|
serde = ["dep:serde"]
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,15 @@ use core::{
|
||||||
|
|
||||||
use crate::{roc_alloc, roc_dealloc, roc_realloc, storage::Storage};
|
use crate::{roc_alloc, roc_dealloc, roc_realloc, storage::Storage};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{
|
||||||
|
de::{Deserializer, Visitor},
|
||||||
|
ser::{SerializeSeq, Serializer},
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct RocList<T> {
|
pub struct RocList<T> {
|
||||||
elements: Option<NonNull<ManuallyDrop<T>>>,
|
elements: Option<NonNull<ManuallyDrop<T>>>,
|
||||||
|
|
@ -586,3 +595,76 @@ impl<T: Clone> FromIterator<T> for RocList<T> {
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<T: Serialize> Serialize for RocList<T> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut seq = serializer.serialize_seq(Some(self.len()))?;
|
||||||
|
for item in self {
|
||||||
|
seq.serialize_element(item)?;
|
||||||
|
}
|
||||||
|
seq.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de, T> Deserialize<'de> for RocList<T>
|
||||||
|
where
|
||||||
|
// TODO: I'm not sure about requiring clone here. Is that fine? Is that
|
||||||
|
// gonna mean lots of extra allocations?
|
||||||
|
T: Deserialize<'de> + core::clone::Clone,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_seq(RocListVisitor::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
struct RocListVisitor<T> {
|
||||||
|
marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<T> RocListVisitor<T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
RocListVisitor {
|
||||||
|
marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de, T> Visitor<'de> for RocListVisitor<T>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de> + core::clone::Clone,
|
||||||
|
{
|
||||||
|
type Value = RocList<T>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(formatter, "a list of strings")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: serde::de::SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut out = match seq.size_hint() {
|
||||||
|
Some(hint) => RocList::with_capacity(hint),
|
||||||
|
None => RocList::empty(),
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(next) = seq.next_element()? {
|
||||||
|
// TODO: it would be ideal to call `out.push` here, but we haven't
|
||||||
|
// implemented that yet! I think this is also why we need Clone.
|
||||||
|
out.extend_from_slice(&[next])
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{
|
||||||
|
de::{Deserializer, Visitor},
|
||||||
|
ser::Serializer,
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
|
||||||
use core::{
|
use core::{
|
||||||
cmp,
|
cmp,
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
|
|
@ -708,3 +715,45 @@ impl Hash for RocStr {
|
||||||
self.as_str().hash(state)
|
self.as_str().hash(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl Serialize for RocStr {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> Deserialize<'de> for RocStr {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
// TODO: using deserialize_string here instead of deserialize_str here
|
||||||
|
// because I think we'd "benefit from taking ownership of buffered data
|
||||||
|
// owned by the Deserializer." is that correct?
|
||||||
|
deserializer.deserialize_string(RocStrVisitor {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
struct RocStrVisitor {}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> Visitor<'de> for RocStrVisitor {
|
||||||
|
type Value = RocStr;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(formatter, "a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Ok(RocStr::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,29 @@ mod test_roc_std {
|
||||||
assert_eq!(roc_str.capacity(), 5000);
|
assert_eq!(roc_str.capacity(), 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn str_short_serde_roundtrip() {
|
||||||
|
let orig = RocStr::from("x");
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&orig).expect("failed to serialize string");
|
||||||
|
let deserialized = serde_json::from_str(&serialized).expect("failed to deserialize string");
|
||||||
|
|
||||||
|
assert_eq!(orig, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn str_long_serde_roundtrip() {
|
||||||
|
// How about a little philosophy to accompany test failures?
|
||||||
|
let orig = RocStr::from("If there's a remedy when trouble strikes, what reason is there for dejection? And if there is no help for it, what use is there in being glum? -- Shantideva, The Way of the Bodhisattva");
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&orig).expect("failed to serialize string");
|
||||||
|
let deserialized = serde_json::from_str(&serialized).expect("failed to deserialize string");
|
||||||
|
|
||||||
|
assert_eq!(orig, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reserve_small_list() {
|
fn reserve_small_list() {
|
||||||
let mut roc_list = RocList::<RocStr>::empty();
|
let mut roc_list = RocList::<RocStr>::empty();
|
||||||
|
|
@ -156,6 +179,31 @@ mod test_roc_std {
|
||||||
assert_eq!(roc_list.capacity(), 5000);
|
assert_eq!(roc_list.capacity(), 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn short_list_roundtrip() {
|
||||||
|
let items: [u8; 4] = [1, 3, 3, 7];
|
||||||
|
let orig = RocList::from_slice(&items);
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&orig).expect("failed to serialize string");
|
||||||
|
let deserialized =
|
||||||
|
serde_json::from_str::<RocList<u8>>(&serialized).expect("failed to deserialize string");
|
||||||
|
|
||||||
|
assert_eq!(orig, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn long_list_roundtrip() {
|
||||||
|
let orig = RocList::from_iter(1..100);
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&orig).expect("failed to serialize string");
|
||||||
|
let deserialized =
|
||||||
|
serde_json::from_str::<RocList<u8>>(&serialized).expect("failed to deserialize string");
|
||||||
|
|
||||||
|
assert_eq!(orig, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_from_iter() {
|
fn list_from_iter() {
|
||||||
let elems: [i64; 5] = [1, 2, 3, 4, 5];
|
let elems: [i64; 5] = [1, 2, 3, 4, 5];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue