Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add workflows #1

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
31 changes: 31 additions & 0 deletions .github/workflows/deploy.yml
@@ -0,0 +1,31 @@
# from https://pratikpc.medium.com/publishing-crates-using-github-actions-165ee67780e1

on:
push:
# Pattern matched against refs/tags
tags:
- "*" # Push events to every tag not containing /

name: Publish

jobs:
publish:
name: Publish
runs-on: ubuntu-latest
steps:
- name: Check if token in environment
run: ${{ secrets.CRATES_TOKEN }} == '' && echo "No token in environment, exiting" && exit 1

- name: Checkout sources
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- run: cargo publish --token ${CRATES_TOKEN}
env:
CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}
110 changes: 110 additions & 0 deletions .github/workflows/test.yml
@@ -0,0 +1,110 @@
name: CI

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

env:
RUSTFLAGS: -Dwarnings

jobs:
test:
name: Rust Nightly Tests ${{matrix.os == 'windows' && '(windows)' || ''}}
runs-on: ${{matrix.os}}-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu, windows]
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cargo test
- run: cargo test --features tokio-io --tests -- --skip ui --exact
- run: cargo test --features value --tests -- --skip ui --exact
- run: cargo test --features all --tests -- --skip ui --exact

build:
name: Rust ${{matrix.rust}} ${{matrix.os == 'windows' && '(windows)' || ''}}
runs-on: ${{matrix.os}}-latest
strategy:
fail-fast: false
matrix:
rust: [beta, 1.76.0, 1.65.0]
os: [ubuntu]
include:
- rust: stable
os: ubuntu
target: aarch64-unknown-none
- rust: stable
os: windows
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{matrix.rust}}
targets: ${{matrix.target}}
- run: cargo check
- run: cargo check --features tokio-io
- run: cargo check --features value
- run: cargo check --features all

# this fails right now because of errors in get-size-derive.
# number-general should require 0.1.3
# minimal:
# name: Minimal versions
# runs-on: ubuntu-latest
# timeout-minutes: 45
# steps:
# - uses: actions/checkout@v4
# - uses: dtolnay/rust-toolchain@nightly
# - run: cargo generate-lockfile -Z minimal-versions
# - run: cargo check --locked

clippy:
name: Clippy
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@clippy
- run: cargo clippy --tests # -- -Dclippy::all -Dclippy::pedantic
- run: cargo clippy --all-features --tests # -- -Dclippy::all -Dclippy::pedantic

doc:
name: Documentation
runs-on: ubuntu-latest
timeout-minutes: 45
env:
RUSTDOCFLAGS: -Dwarnings
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/install@cargo-docs-rs
- run: cargo docs-rs

# fuzz testing async code seems hard
# fuzz:
# name: Fuzz
# runs-on: ubuntu-latest
# timeout-minutes: 45
# steps:
# - uses: actions/checkout@v4
# - uses: dtolnay/rust-toolchain@nightly
# - uses: dtolnay/install@cargo-fuzz
# - run: cargo fuzz check

outdated:
name: Outdated
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/install@cargo-outdated
- run: cargo outdated --exit-code 1
8 changes: 4 additions & 4 deletions Cargo.toml
Expand Up @@ -18,18 +18,18 @@ value = ["number-general/stream"]
all = ["tokio-io", "value"]

[dependencies]
async-trait = "0.1"
base64 = "0.21"
async-trait = "0.1.78"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you need this specific patch version?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see this comment and this comment on the specific action.

since we're not running this action, these changes can be reverted, but technically a project won't compile if it requires, for example, async-trait ==v0.1.0. i think i'll revert these changes for now and we can revisit in a future PR.

base64 = "0.22"
bytes = "1.5"
destream = "0.7"
futures = "0.3"
number-general = { version = "0.11", optional = true }
number-general = { version = "0.11.1", optional = true }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

pin-project = "1.1"
tokio = { version = "1.35", features = ["io-util"], optional = true }
uuid = "1.6"

[dev-dependencies]
number-general = { version = "0.11", features=["stream"] }
number-general = { version = "0.11.1", features=["stream"] }
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated these versions as an attempt to get this minimal-versions test working, but ended up dropping it since number-general depends on another of your crates that breaks this check:

cargo generate-lockfile -Z minimal-versions
cargo check --locked

tokio = { version = "1.35", features = ["fs", "macros"] }
tokio-util = { version = "0.7", features = ["io"] }
tokio-test = "0.4"
20 changes: 10 additions & 10 deletions src/constants.rs
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are all a clippy warning, these refs are 'static by default

@@ -1,16 +1,16 @@
pub const COLON: &'static [u8] = b":";
pub const COMMA: &'static [u8] = b",";
pub const DECIMAL: &'static [u8] = b".";
pub const E: &'static [u8] = b"e";
pub const ESCAPE: &'static [u8] = b"\\";
pub const COLON: &[u8] = b":";
pub const COMMA: &[u8] = b",";
pub const DECIMAL: &[u8] = b".";
pub const E: &[u8] = b"e";
pub const ESCAPE: &[u8] = b"\\";
pub const FALSE: &[u8] = b"false";
pub const TRUE: &[u8] = b"true";
pub const LIST_BEGIN: &'static [u8] = b"[";
pub const LIST_END: &'static [u8] = b"]";
pub const LIST_BEGIN: &[u8] = b"[";
pub const LIST_END: &[u8] = b"]";
pub const NULL: &[u8] = b"null";
pub const MAP_BEGIN: &'static [u8] = b"{";
pub const MAP_END: &'static [u8] = b"}";
pub const MAP_BEGIN: &[u8] = b"{";
pub const MAP_END: &[u8] = b"}";
pub const NUMERIC: [u8; 15] = [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'-', b'e', b'E', b'.',
];
pub const QUOTE: &'static [u8] = b"\"";
pub const QUOTE: &[u8] = b"\"";
16 changes: 8 additions & 8 deletions src/de.rs
Expand Up @@ -303,7 +303,7 @@ where
impl<S> Decoder<S> {
fn contents(&self, max_len: usize) -> Result<String, Error> {
let len = Ord::min(self.buffer.len(), max_len);
String::from_utf8(self.buffer[..len].to_vec()).map_err(|cause| Error::invalid_utf8(cause))
String::from_utf8(self.buffer[..len].to_vec()).map_err(Error::invalid_utf8)
}
}

Expand Down Expand Up @@ -511,7 +511,7 @@ impl<S: Read> Decoder<S> {

let i = Ord::min(self.buffer.len(), SNIPPET_LEN);
let unknown = String::from_utf8(self.buffer[..i].to_vec()).map_err(Error::invalid_utf8)?;
Err(de::Error::invalid_value(unknown, &"a boolean"))
Err(de::Error::invalid_value(unknown, "a boolean"))
}

async fn parse_number<N: FromStr>(&mut self) -> Result<N, Error>
Expand All @@ -529,7 +529,7 @@ impl<S: Read> Decoder<S> {
self.buffer.drain(..i);
Ok(number)
}
Err(cause) => Err(de::Error::invalid_value(cause, &std::any::type_name::<N>())),
Err(cause) => Err(de::Error::invalid_value(cause, std::any::type_name::<N>())),
}
}

Expand All @@ -553,7 +553,7 @@ impl<S: Read> Decoder<S> {
let as_str =
String::from_utf8(self.buffer[..i].to_vec()).map_err(Error::invalid_utf8)?;

Err(de::Error::invalid_type(as_str, &"null"))
Err(de::Error::invalid_type(as_str, "null"))
}
}
}
Expand All @@ -579,9 +579,9 @@ impl<S: Read> de::Decoder for Decoder<S> {
self.decode_map(visitor).await
} else if self.numeric.contains(&self.buffer[0]) {
self.decode_number(visitor).await
} else if self.buffer.len() >= FALSE.len() && self.buffer.starts_with(FALSE) {
self.decode_bool(visitor).await
} else if self.buffer.len() >= TRUE.len() && self.buffer.starts_with(TRUE) {
} else if (self.buffer.len() >= FALSE.len() && self.buffer.starts_with(FALSE))
|| (self.buffer.len() >= TRUE.len() && self.buffer.starts_with(TRUE))
{
self.decode_bool(visitor).await
Comment on lines +582 to 585
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should maybe make sure a test covers this, but clippy complained that these two condition blocks were the same, so i combined the conditions

} else if self.buffer.len() >= NULL.len() && self.buffer.starts_with(NULL) {
self.decode_option(visitor).await
Expand Down Expand Up @@ -612,7 +612,7 @@ impl<S: Read> de::Decoder for Decoder<S> {

Err(de::Error::invalid_value(
s,
&std::any::type_name::<V::Value>(),
std::any::type_name::<V::Value>(),
))
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/en/mod.rs
Expand Up @@ -205,7 +205,7 @@ impl Encoder {
<T as IntoIterator>::IntoIter: Send + Unpin + 'en,
{
let sequence = chunks
.map(|chunk| futures::stream::iter(chunk.into_iter()))
.map(|chunk| futures::stream::iter(chunk))
.flatten();

en::Encoder::encode_seq_stream(self, sequence)
Expand Down Expand Up @@ -530,7 +530,7 @@ fn escape<T: fmt::Display>(value: T) -> Bytes {
#[inline]
fn encode_fmt<'en, T: fmt::Display>(value: T) -> JSONStream<'en> {
let encoded = escape(value);
Box::pin(futures::stream::once(future::ready(Ok(encoded.into()))))
Box::pin(futures::stream::once(future::ready(Ok(encoded))))
}

#[inline]
Expand Down
11 changes: 4 additions & 7 deletions src/en/stream.rs
Expand Up @@ -45,10 +45,7 @@ impl<'en> Stream for JSONMapEntryStream<'en> {
None => Some(Ok(Bytes::from_static(COLON))),
}
} else if !this.value.is_terminated() {
match ready!(this.value.as_mut().poll_next(cxt)) {
Some(result) => Some(result),
None => None,
}
ready!(this.value.as_mut().poll_next(cxt))
} else {
None
};
Expand Down Expand Up @@ -102,17 +99,17 @@ impl<I: Stream<Item = Result<Bytes, super::Error>>, S: Stream<Item = Result<I, s
break Some(Ok(Bytes::from_static(COMMA)));
} else {
*this.started = true;
break Some(Ok(Bytes::from_static(*this.start)));
break Some(Ok(Bytes::from_static(this.start)));
}
}
Some(Err(cause)) => break Some(Err(en::Error::custom(cause))),
None if !*this.started => {
*this.started = true;
break Some(Ok(Bytes::from_static(*this.start)));
break Some(Ok(Bytes::from_static(this.start)));
}
None if !*this.finished => {
*this.finished = true;
break Some(Ok(Bytes::from_static(*this.end)));
break Some(Ok(Bytes::from_static(this.end)));
}
None => break None,
},
Expand Down
28 changes: 12 additions & 16 deletions src/lib.rs
Expand Up @@ -36,38 +36,29 @@ mod value;
mod tests {
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::iter::FromIterator;
use std::marker::PhantomData;

use async_trait::async_trait;
use bytes::Bytes;
use destream::de::{self, ArrayAccess, FromStream};
use destream::en::{Encoder, IntoStream};
use destream::en::IntoStream;
use futures::future;
use futures::stream::{self, Stream, StreamExt, TryStreamExt};

use super::de::*;
use super::en::*;

struct Error;

impl<'en> IntoStream<'en> for Error {
fn into_stream<E: Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
"an error!".into_stream(encoder)
}
}

Comment on lines -52 to -59
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clippy complained this was unused, is it?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good catch!

async fn test_decode<T: FromStream<Context = ()> + PartialEq + fmt::Debug>(
encoded: &str,
expected: T,
) {
for i in (1..encoded.len()).rev() {
let source = stream::iter(encoded.as_bytes().into_iter().cloned())
let source = stream::iter(encoded.as_bytes().iter().cloned())
.chunks(i)
.map(Bytes::from);

let actual: T = decode((), source).await.unwrap();
assert_eq!(expected, actual)
assert_eq!(expected, actual);
}
}

Expand Down Expand Up @@ -149,11 +140,13 @@ mod tests {
test_decode("1e-6", 1e-6).await;
test_decode("2e2", 2e2_f32).await;
test_decode("-2e-3", -2e-3_f64).await;
#[allow(clippy::approx_constant)]
test_decode("3.14", 3.14_f32).await;
test_decode("-1.414e4", -1.414e4_f64).await;

test_encode_value(2e2_f32, "200").await;
test_encode_value(-2e3, "-2000").await;
#[allow(clippy::approx_constant)]
test_encode_value(3.14_f32, "3.14").await;
test_encode_value(-1.414e4_f64, "-14140").await;

Expand Down Expand Up @@ -224,7 +217,7 @@ mod tests {
&'en self,
encoder: E,
) -> Result<E::Ok, E::Error> {
encoder.encode_array_f64(stream::once(future::ready(self.data.to_vec())))
encoder.encode_array_f64(stream::once(future::ready(self.data.clone())))
}
}

Expand Down Expand Up @@ -416,15 +409,15 @@ mod tests {
}

let encoded = "{\"k1\": [1, 2, 3]}";
let source = stream::iter(encoded.as_bytes().into_iter().cloned())
let source = stream::iter(encoded.as_bytes().iter().copied())
.chunks(5)
.map(Bytes::from);

let actual: TestMap = decode((), source).await.unwrap();
assert_eq!(actual, TestMap);

let encoded = "\t[ 1,2, 3]";
let source = stream::iter(encoded.as_bytes().into_iter().cloned())
let source = stream::iter(encoded.as_bytes().iter().copied())
.chunks(2)
.map(Bytes::from);

Expand Down Expand Up @@ -621,7 +614,10 @@ mod tests {
let mut value = HashMap::new();
value.insert("one".to_string(), Some(Number::from(1)));
value.insert("two".to_string(), None);
value.insert("three".to_string(), Some(Number::from(3.14)));
value.insert(
"three".to_string(),
Some(Number::from(std::f32::consts::PI)),
);

let path = PathBuf::from(".tmp");

Expand Down