From 774e47d94e63f0ae749e53e4540fc07ee78e6247 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 6 Feb 2019 17:30:22 +0100 Subject: [PATCH] v0.1.0 --- .travis.yml | 16 ++++- CHANGELOG.md | 22 +++++++ Cargo.toml | 1 + ci/install.sh | 4 +- ci/script.sh | 18 +++++- src/de/enum_.rs | 3 +- src/de/map.rs | 5 +- src/de/mod.rs | 157 ++++++++++++++++++++++++++------------------- src/de/seq.rs | 5 +- src/lib.rs | 19 +++--- src/ser/mod.rs | 53 +++++++-------- src/ser/seq.rs | 12 ++-- src/ser/struct_.rs | 10 +-- 13 files changed, 200 insertions(+), 125 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.travis.yml b/.travis.yml index 71d535521..51a2e623f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,23 @@ language: rust +rust: stable matrix: include: + # MSRV - env: TARGET=x86_64-unknown-linux-gnu - rust: nightly + rust: 1.31.0 + if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) + + - env: TARGET=x86_64-unknown-linux-gnu + if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) + + # no-std sanity check - env: TARGET=thumbv7m-none-eabi - rust: nightly + if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) + + # no-std sanity check + - env: TARGET=rustfmt + if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) before_install: set -e diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..635c46252 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] + +## [v0.1.0] - 2019-02-06 + +### Changed + +- [breaking-change] The `heapless` dependency has been bumped to v0.4.0 + +- This crate now compiles on stable + +## v0.0.1 + +Initial release + +[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.5.8...HEAD diff --git a/Cargo.toml b/Cargo.toml index cc4375a44..40a248f3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [package] authors = ["Jorge Aparicio "] +edition = "2018" name = "serde-json-core" version = "0.1.0" diff --git a/ci/install.sh b/ci/install.sh index 555c1f96f..e88d3ac61 100644 --- a/ci/install.sh +++ b/ci/install.sh @@ -1,8 +1,10 @@ set -euxo pipefail main() { - if [ $TARGET = thumbv7m-none-eabi ]; then + if [ $TARGET != rustfmt ]; then rustup target add $TARGET + else + rustup component add rustfmt fi } diff --git a/ci/script.sh b/ci/script.sh index ee709182d..f3117384a 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,15 +1,29 @@ set -euxo pipefail main() { + if [ $TARGET = rustfmt ]; then + cargo fmt -- --check + return + fi + cargo check --target $TARGET if [ $TARGET = x86_64-unknown-linux-gnu ]; then cargo test --target $TARGET - cargo check --target $TARGET --features std + return fi } -if [ $TRAVIS_BRANCH != master ]; then +# fake Travis variables to be able to run this on a local machine +if [ -z ${TRAVIS_BRANCH-} ]; then + TRAVIS_BRANCH=auto +fi + +if [ -z ${TARGET-} ]; then + TARGET=$(rustc -Vv | grep host | cut -d ' ' -f2) +fi + +if [ $TRAVIS_BRANCH != master ] || [ $TRAVIS_PULL_REQUEST != false ]; then main fi diff --git a/src/de/enum_.rs b/src/de/enum_.rs index 81ba943eb..dcc983854 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -1,5 +1,6 @@ use serde::de; -use de::{Deserializer, Error, Result}; + +use crate::de::{Deserializer, Error, Result}; pub(crate) struct UnitVariantAccess<'a, 'b> where diff --git a/src/de/map.rs b/src/de/map.rs index a98b178be..fc0e77b39 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -1,6 +1,6 @@ use serde::de::{self, Visitor}; -use de::{Deserializer, Error}; +use crate::de::{Deserializer, Error}; pub struct MapAccess<'a, 'b> where @@ -23,7 +23,8 @@ impl<'a, 'de> de::MapAccess<'de> for MapAccess<'a, 'de> { where K: de::DeserializeSeed<'de>, { - let peek = match self.de + let peek = match self + .de .parse_whitespace() .ok_or(Error::EofWhileParsingObject)? { diff --git a/src/de/mod.rs b/src/de/mod.rs index 8ea3c3277..4e268bcdf 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -13,7 +13,7 @@ mod map; mod seq; /// Deserialization result -pub type Result = ::core::result::Result; +pub type Result = core::result::Result; /// This type represents all possible errors that can occur when deserializing JSON data #[derive(Debug, PartialEq)] @@ -80,7 +80,7 @@ pub(crate) struct Deserializer<'b> { } impl<'a> Deserializer<'a> { - fn new(slice: &'a [u8]) -> Deserializer { + fn new(slice: &'a [u8]) -> Deserializer<'_> { Deserializer { slice, index: 0 } } @@ -113,7 +113,10 @@ impl<'a> Deserializer<'a> { } fn end_map(&mut self) -> Result<()> { - match self.parse_whitespace().ok_or(Error::EofWhileParsingObject)? { + match self + .parse_whitespace() + .ok_or(Error::EofWhileParsingObject)? + { b'}' => { self.eat_char(); Ok(()) @@ -144,7 +147,10 @@ impl<'a> Deserializer<'a> { } fn parse_object_colon(&mut self) -> Result<()> { - match self.parse_whitespace().ok_or(Error::EofWhileParsingObject)? { + match self + .parse_whitespace() + .ok_or(Error::EofWhileParsingObject)? + { b':' => { self.eat_char(); Ok(()) @@ -193,7 +199,9 @@ impl<'a> Deserializer<'a> { // Flash, when targeting non 64-bit architectures macro_rules! deserialize_unsigned { ($self:ident, $visitor:ident, $uxx:ident, $visit_uxx:ident) => {{ - let peek = $self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?; + let peek = $self + .parse_whitespace() + .ok_or(Error::EofWhileParsingValue)?; match peek { b'-' => Err(Error::InvalidNumber), @@ -201,13 +209,13 @@ macro_rules! deserialize_unsigned { $self.eat_char(); $visitor.$visit_uxx(0) } - b'1'...b'9' => { + b'1'..=b'9' => { $self.eat_char(); let mut number = (peek - b'0') as $uxx; loop { match $self.peek() { - Some(c @ b'0'...b'9') => { + Some(c @ b'0'..=b'9') => { $self.eat_char(); number = number .checked_mul(10) @@ -226,7 +234,10 @@ macro_rules! deserialize_unsigned { macro_rules! deserialize_signed { ($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{ - let signed = match $self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + let signed = match $self + .parse_whitespace() + .ok_or(Error::EofWhileParsingValue)? + { b'-' => { $self.eat_char(); true @@ -239,13 +250,13 @@ macro_rules! deserialize_signed { $self.eat_char(); $visitor.$visit_ixx(0) } - c @ b'1'...b'9' => { + c @ b'1'..=b'9' => { $self.eat_char(); let mut number = (c - b'0') as $ixx * if signed { -1 } else { 1 }; loop { match $self.peek() { - Some(c @ b'0'...b'9') => { + Some(c @ b'0'..=b'9') => { $self.eat_char(); number = number .checked_mul(10) @@ -548,32 +559,44 @@ impl de::Error for Error { } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match self { - Error::EofWhileParsingList => "EOF while parsing a list.", - Error::EofWhileParsingObject => "EOF while parsing an object.", - Error::EofWhileParsingString => "EOF while parsing a string.", - Error::EofWhileParsingValue => "EOF while parsing a JSON value.", - Error::ExpectedColon => "Expected this character to be a `':'`.", - Error::ExpectedListCommaOrEnd => "Expected this character to be either a `','` or\ - a \ - `']'`.", - Error::ExpectedObjectCommaOrEnd => "Expected this character to be either a `','` \ - or a \ - `'}'`.", - Error::ExpectedSomeIdent => "Expected to parse either a `true`, `false`, or a \ - `null`.", - Error::ExpectedSomeValue => "Expected this character to start a JSON value.", - Error::InvalidNumber => "Invalid number.", - Error::InvalidType => "Invalid type", - Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", - Error::KeyMustBeAString => "Object key is not a string.", - Error::TrailingCharacters => "JSON has non-whitespace trailing characters after \ - the \ - value.", - Error::TrailingComma => "JSON has a comma after the last value in an array or map.", - _ => "Invalid JSON" - }) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Error::EofWhileParsingList => "EOF while parsing a list.", + Error::EofWhileParsingObject => "EOF while parsing an object.", + Error::EofWhileParsingString => "EOF while parsing a string.", + Error::EofWhileParsingValue => "EOF while parsing a JSON value.", + Error::ExpectedColon => "Expected this character to be a `':'`.", + Error::ExpectedListCommaOrEnd => { + "Expected this character to be either a `','` or\ + a \ + `']'`." + } + Error::ExpectedObjectCommaOrEnd => { + "Expected this character to be either a `','` \ + or a \ + `'}'`." + } + Error::ExpectedSomeIdent => { + "Expected to parse either a `true`, `false`, or a \ + `null`." + } + Error::ExpectedSomeValue => "Expected this character to start a JSON value.", + Error::InvalidNumber => "Invalid number.", + Error::InvalidType => "Invalid type", + Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", + Error::KeyMustBeAString => "Object key is not a string.", + Error::TrailingCharacters => { + "JSON has non-whitespace trailing characters after \ + the \ + value." + } + Error::TrailingComma => "JSON has a comma after the last value in an array or map.", + _ => "Invalid JSON", + } + ) } } @@ -599,6 +622,8 @@ where #[cfg(test)] mod tests { + use serde_derive::Deserialize; + #[derive(Debug, Deserialize, PartialEq)] enum Type { #[serde(rename = "boolean")] @@ -611,38 +636,38 @@ mod tests { #[test] fn array() { - assert_eq!(super::from_str::<[i32; 0]>("[]"), Ok([])); - assert_eq!(super::from_str("[0, 1, 2]"), Ok([0, 1, 2])); + assert_eq!(crate::from_str::<[i32; 0]>("[]"), Ok([])); + assert_eq!(crate::from_str("[0, 1, 2]"), Ok([0, 1, 2])); // errors - assert!(super::from_str::<[i32; 2]>("[0, 1,]").is_err()); + assert!(crate::from_str::<[i32; 2]>("[0, 1,]").is_err()); } #[test] fn bool() { - assert_eq!(super::from_str("true"), Ok(true)); - assert_eq!(super::from_str(" true"), Ok(true)); - assert_eq!(super::from_str("true "), Ok(true)); + assert_eq!(crate::from_str("true"), Ok(true)); + assert_eq!(crate::from_str(" true"), Ok(true)); + assert_eq!(crate::from_str("true "), Ok(true)); - assert_eq!(super::from_str("false"), Ok(false)); - assert_eq!(super::from_str(" false"), Ok(false)); - assert_eq!(super::from_str("false "), Ok(false)); + assert_eq!(crate::from_str("false"), Ok(false)); + assert_eq!(crate::from_str(" false"), Ok(false)); + assert_eq!(crate::from_str("false "), Ok(false)); // errors - assert!(super::from_str::("true false").is_err()); - assert!(super::from_str::("tru").is_err()); + assert!(crate::from_str::("true false").is_err()); + assert!(crate::from_str::("tru").is_err()); } #[test] fn enum_clike() { - assert_eq!(super::from_str(r#" "boolean" "#), Ok(Type::Boolean)); - assert_eq!(super::from_str(r#" "number" "#), Ok(Type::Number)); - assert_eq!(super::from_str(r#" "thing" "#), Ok(Type::Thing)); + assert_eq!(crate::from_str(r#" "boolean" "#), Ok(Type::Boolean)); + assert_eq!(crate::from_str(r#" "number" "#), Ok(Type::Number)); + assert_eq!(crate::from_str(r#" "thing" "#), Ok(Type::Thing)); } #[test] fn str() { - assert_eq!(super::from_str(r#" "hello" "#), Ok("hello")); + assert_eq!(crate::from_str(r#" "hello" "#), Ok("hello")); } #[test] @@ -652,9 +677,9 @@ mod tests { led: bool, } - assert_eq!(super::from_str(r#"{ "led": true }"#), Ok(Led { led: true })); + assert_eq!(crate::from_str(r#"{ "led": true }"#), Ok(Led { led: true })); assert_eq!( - super::from_str(r#"{ "led": false }"#), + crate::from_str(r#"{ "led": false }"#), Ok(Led { led: false }) ); } @@ -667,23 +692,23 @@ mod tests { } assert_eq!( - super::from_str(r#"{ "temperature": -17 }"#), + crate::from_str(r#"{ "temperature": -17 }"#), Ok(Temperature { temperature: -17 }) ); assert_eq!( - super::from_str(r#"{ "temperature": -0 }"#), + crate::from_str(r#"{ "temperature": -0 }"#), Ok(Temperature { temperature: -0 }) ); assert_eq!( - super::from_str(r#"{ "temperature": 0 }"#), + crate::from_str(r#"{ "temperature": 0 }"#), Ok(Temperature { temperature: 0 }) ); // out of range - assert!(super::from_str::(r#"{ "temperature": 128 }"#).is_err()); - assert!(super::from_str::(r#"{ "temperature": -129 }"#).is_err()); + assert!(crate::from_str::(r#"{ "temperature": 128 }"#).is_err()); + assert!(crate::from_str::(r#"{ "temperature": -129 }"#).is_err()); } #[test] @@ -695,18 +720,18 @@ mod tests { } assert_eq!( - super::from_str(r#"{ "description": "An ambient temperature sensor" }"#), + crate::from_str(r#"{ "description": "An ambient temperature sensor" }"#), Ok(Property { description: Some("An ambient temperature sensor"), }) ); assert_eq!( - super::from_str(r#"{ "description": null }"#), + crate::from_str(r#"{ "description": null }"#), Ok(Property { description: None }) ); - assert_eq!(super::from_str(r#"{}"#), Ok(Property { description: None })); + assert_eq!(crate::from_str(r#"{}"#), Ok(Property { description: None })); } #[test] @@ -717,18 +742,18 @@ mod tests { } assert_eq!( - super::from_str(r#"{ "temperature": 20 }"#), + crate::from_str(r#"{ "temperature": 20 }"#), Ok(Temperature { temperature: 20 }) ); assert_eq!( - super::from_str(r#"{ "temperature": 0 }"#), + crate::from_str(r#"{ "temperature": 0 }"#), Ok(Temperature { temperature: 0 }) ); // out of range - assert!(super::from_str::(r#"{ "temperature": 256 }"#).is_err()); - assert!(super::from_str::(r#"{ "temperature": -1 }"#).is_err()); + assert!(crate::from_str::(r#"{ "temperature": 256 }"#).is_err()); + assert!(crate::from_str::(r#"{ "temperature": -1 }"#).is_err()); } // See https://iot.mozilla.org/wot/#thing-resource @@ -764,7 +789,7 @@ mod tests { } assert_eq!( - super::from_str::( + crate::from_str::>( r#" { "type": "thing", diff --git a/src/de/seq.rs b/src/de/seq.rs index b20728156..b809dd165 100644 --- a/src/de/seq.rs +++ b/src/de/seq.rs @@ -1,6 +1,6 @@ use serde::de; -use de::{Deserializer, Error, Result}; +use crate::de::{Deserializer, Error, Result}; pub(crate) struct SeqAccess<'a, 'b> where @@ -23,7 +23,8 @@ impl<'a, 'de> de::SeqAccess<'de> for SeqAccess<'a, 'de> { where T: de::DeserializeSeed<'de>, { - let peek = match self.de + let peek = match self + .de .parse_whitespace() .ok_or(Error::EofWhileParsingList)? { diff --git a/src/lib.rs b/src/lib.rs index e639e8769..41df0c9c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,18 +47,17 @@ //! - Anything that involves dynamic memory allocation //! - Like the dynamic [`Value`](https://docs.rs/serde_json/1.0.11/serde_json/enum.Value.html) //! type +//! +//! # MSRV +//! +//! This crate is guaranteed to compile on stable Rust 1.31.0 and up. It *might* compile with older +//! versions but that may change in any new patch release. + #![deny(missing_docs)] +#![deny(rust_2018_compatibility)] +#![deny(rust_2018_idioms)] #![deny(warnings)] -#![feature(unsize)] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate core; -extern crate heapless; -extern crate serde; -#[cfg(test)] -#[macro_use] -extern crate serde_derive; +#![no_std] pub mod de; pub mod ser; diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 030fe9052..f7d06a462 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -25,14 +25,13 @@ pub enum Error { } impl From<()> for Error { - fn from(_:()) -> Error { + fn from(_: ()) -> Error { Error::BufferFull } } - impl From for Error { - fn from(_:u8) -> Error { + fn from(_: u8) -> Error { Error::BufferFull } } @@ -44,9 +43,8 @@ impl ::std::error::Error for Error { } } - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Buffer is full") } } @@ -421,20 +419,20 @@ impl ser::SerializeStructVariant for Unreachable { #[cfg(test)] mod tests { + use serde_derive::Serialize; + use heapless::consts::U128; + type N = U128; #[test] fn array() { - assert_eq!( - &*super::to_string::(&[0, 1, 2]).unwrap(), - "[0,1,2]" - ); + assert_eq!(&*crate::to_string::(&[0, 1, 2]).unwrap(), "[0,1,2]"); } #[test] fn bool() { - assert_eq!(&*super::to_string::(&true).unwrap(), "true"); + assert_eq!(&*crate::to_string::(&true).unwrap(), "true"); } #[test] @@ -448,22 +446,19 @@ mod tests { } assert_eq!( - &*super::to_string::(&Type::Boolean).unwrap(), + &*crate::to_string::(&Type::Boolean).unwrap(), r#""boolean""# ); assert_eq!( - &*super::to_string::(&Type::Number).unwrap(), + &*crate::to_string::(&Type::Number).unwrap(), r#""number""# ); } #[test] fn str() { - assert_eq!( - &*super::to_string::("hello").unwrap(), - r#""hello""# - ); + assert_eq!(&*crate::to_string::("hello").unwrap(), r#""hello""#); } #[test] @@ -474,7 +469,7 @@ mod tests { } assert_eq!( - &*super::to_string::(&Led { led: true }).unwrap(), + &*crate::to_string::(&Led { led: true }).unwrap(), r#"{"led":true}"# ); } @@ -487,22 +482,22 @@ mod tests { } assert_eq!( - &*super::to_string::(&Temperature { temperature: 127 }).unwrap(), + &*crate::to_string::(&Temperature { temperature: 127 }).unwrap(), r#"{"temperature":127}"# ); assert_eq!( - &*super::to_string::(&Temperature { temperature: 20 }).unwrap(), + &*crate::to_string::(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); assert_eq!( - &*super::to_string::(&Temperature { temperature: -17 }).unwrap(), + &*crate::to_string::(&Temperature { temperature: -17 }).unwrap(), r#"{"temperature":-17}"# ); assert_eq!( - &*super::to_string::(&Temperature { temperature: -128 }).unwrap(), + &*crate::to_string::(&Temperature { temperature: -128 }).unwrap(), r#"{"temperature":-128}"# ); } @@ -515,15 +510,16 @@ mod tests { } assert_eq!( - super::to_string::(&Property { + crate::to_string::(&Property { description: Some("An ambient temperature sensor"), - }).unwrap(), + }) + .unwrap(), r#"{"description":"An ambient temperature sensor"}"# ); // XXX Ideally this should produce "{}" assert_eq!( - super::to_string::(&Property { description: None }).unwrap(), + crate::to_string::(&Property { description: None }).unwrap(), r#"{"description":null}"# ); } @@ -536,7 +532,7 @@ mod tests { } assert_eq!( - &*super::to_string::(&Temperature { temperature: 20 }).unwrap(), + &*crate::to_string::(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); } @@ -546,10 +542,7 @@ mod tests { #[derive(Serialize)] struct Empty {} - assert_eq!( - &*super::to_string::(&Empty {}).unwrap(), - r#"{}"# - ); + assert_eq!(&*crate::to_string::(&Empty {}).unwrap(), r#"{}"#); #[derive(Serialize)] struct Tuple { @@ -558,7 +551,7 @@ mod tests { } assert_eq!( - &*super::to_string::(&Tuple { a: true, b: false }).unwrap(), + &*crate::to_string::(&Tuple { a: true, b: false }).unwrap(), r#"{"a":true,"b":false}"# ); } diff --git a/src/ser/seq.rs b/src/ser/seq.rs index 553a02170..d260edddf 100644 --- a/src/ser/seq.rs +++ b/src/ser/seq.rs @@ -1,10 +1,12 @@ use serde::ser; -use ser::{Error, Result, Serializer}; +use heapless::ArrayLength; + +use crate::ser::{Error, Result, Serializer}; pub struct SerializeSeq<'a, B> where - B: heapless::ArrayLength + 'a, + B: ArrayLength, { de: &'a mut Serializer, first: bool, @@ -12,7 +14,7 @@ where impl<'a, B> SerializeSeq<'a, B> where - B: heapless::ArrayLength, + B: ArrayLength, { pub(crate) fn new(de: &'a mut Serializer) -> Self { SerializeSeq { de, first: true } @@ -21,7 +23,7 @@ where impl<'a, B> ser::SerializeSeq for SerializeSeq<'a, B> where - B: heapless::ArrayLength, + B: ArrayLength, { type Ok = (); type Error = Error; @@ -47,7 +49,7 @@ where impl<'a, B> ser::SerializeTuple for SerializeSeq<'a, B> where - B: heapless::ArrayLength, + B: ArrayLength, { type Ok = (); type Error = Error; diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index a6a51ad71..2ff9421ad 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -1,10 +1,12 @@ use serde::ser; -use ser::{Error, Result, Serializer}; +use heapless::ArrayLength; + +use crate::ser::{Error, Result, Serializer}; pub struct SerializeStruct<'a, B> where - B: heapless::ArrayLength + 'a, + B: ArrayLength, { de: &'a mut Serializer, first: bool, @@ -12,7 +14,7 @@ where impl<'a, B> SerializeStruct<'a, B> where - B: heapless::ArrayLength, + B: ArrayLength, { pub(crate) fn new(de: &'a mut Serializer) -> Self { SerializeStruct { de, first: true } @@ -21,7 +23,7 @@ where impl<'a, B> ser::SerializeStruct for SerializeStruct<'a, B> where - B: heapless::ArrayLength, + B: ArrayLength, { type Ok = (); type Error = Error;