Again lock 'SystemTimeDecodeError' behind the 'std' feature; Support custom error types when deriving 'Encode' and 'Decode';
This commit is contained in:
parent
869629fa01
commit
9520130eb3
16 changed files with 151 additions and 38 deletions
|
@ -3,6 +3,11 @@
|
|||
This is the changelog of [Oct](https://crates.io/crates/oct/).
|
||||
See `README.md` for more information.
|
||||
|
||||
## 0.21.0
|
||||
|
||||
* Again lock `SystemTimeDecodeError` behind the `std` feature
|
||||
* Support custom error types when deriving `Encode` and `Decode`
|
||||
|
||||
## 0.20.2
|
||||
|
||||
* Clean up procedural macros
|
||||
|
|
|
@ -9,7 +9,7 @@ members = ["oct", "oct-benchmarks", "oct-macros"]
|
|||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.20.2"
|
||||
version = "0.21.0"
|
||||
authors = ["Gabriel Bjørnager Jensen"]
|
||||
readme = "README.md"
|
||||
repository = "https://mandelbrot.dk/bjoernager/oct/"
|
||||
|
|
|
@ -32,7 +32,7 @@ readme.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
oct = { path = "../oct", version = "0.20", features = ["proc-macro"]}
|
||||
oct = { path = "../oct", version = "0.21", features = ["proc-macro"]}
|
||||
|
||||
bincode = "2.0"
|
||||
rand = "0.9"
|
||||
|
|
|
@ -11,10 +11,10 @@ use crate::{Discriminants, Repr};
|
|||
use core::iter;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DataEnum, Fields};
|
||||
use syn::{DataEnum, Fields, Type};
|
||||
|
||||
#[must_use]
|
||||
pub fn decode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
||||
pub fn decode_enum(data: DataEnum, repr: Repr, error: Type) -> TokenStream {
|
||||
let discriminants: Vec<_> = Discriminants::new(&data.variants).collect();
|
||||
|
||||
let values = data
|
||||
|
@ -26,7 +26,7 @@ pub fn decode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
|||
let commands = iter::repeat_n(
|
||||
quote! {
|
||||
::oct::decode::Decode::decode(stream)
|
||||
.map_err(::core::convert::Into::<::oct::error::GenericDecodeError>::into)
|
||||
.map_err(::core::convert::Into::<#error>::into)
|
||||
.map_err(::oct::error::EnumDecodeError::BadField)?
|
||||
},
|
||||
variant.fields.len(),
|
||||
|
@ -49,7 +49,7 @@ pub fn decode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
|||
});
|
||||
|
||||
quote! {
|
||||
type Error = ::oct::error::EnumDecodeError<#repr, <#repr as ::oct::decode::Decode>::Error, ::oct::error::GenericDecodeError>;
|
||||
type Error = ::oct::error::EnumDecodeError<#repr, <#repr as ::oct::decode::Decode>::Error, #error>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut ::oct::decode::Input) -> ::core::result::Result<Self, Self::Error> {
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DataStruct, Fields};
|
||||
use syn::{DataStruct, Fields, Type};
|
||||
|
||||
#[must_use]
|
||||
pub fn decode_struct(data: DataStruct) -> TokenStream {
|
||||
pub fn decode_struct(data: DataStruct, error: Type) -> TokenStream {
|
||||
let commands = data
|
||||
.fields
|
||||
.iter()
|
||||
|
@ -23,7 +23,7 @@ pub fn decode_struct(data: DataStruct) -> TokenStream {
|
|||
quote! {
|
||||
#slot {
|
||||
::oct::decode::Decode::decode(input)
|
||||
.map_err(::core::convert::Into::<::oct::error::GenericDecodeError>::into)?
|
||||
.map_err(::core::convert::Into::<#error>::into)?
|
||||
},
|
||||
}
|
||||
});
|
||||
|
@ -35,7 +35,7 @@ pub fn decode_struct(data: DataStruct) -> TokenStream {
|
|||
};
|
||||
|
||||
quote! {
|
||||
type Error = ::oct::error::GenericDecodeError;
|
||||
type Error = #error;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut ::oct::decode::Input) -> ::core::result::Result<Self, Self::Error> {
|
||||
|
|
|
@ -10,10 +10,16 @@ use crate::{Discriminants, Repr};
|
|||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{DataEnum, Fields, Ident, LitInt};
|
||||
use syn::{
|
||||
DataEnum,
|
||||
Fields,
|
||||
Ident,
|
||||
LitInt,
|
||||
Type,
|
||||
};
|
||||
|
||||
#[must_use]
|
||||
pub fn encode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
||||
pub fn encode_enum(data: DataEnum, repr: Repr, error: Type) -> TokenStream {
|
||||
let discriminants: Vec<LitInt> = Discriminants::new(&data.variants).collect();
|
||||
|
||||
let captures: Vec<Vec<Ident>> = data
|
||||
|
@ -53,7 +59,7 @@ pub fn encode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
|||
});
|
||||
|
||||
quote! {
|
||||
type Error = ::oct::error::EnumEncodeError<<#repr as ::oct::encode::Encode>::Error, ::oct::error::GenericEncodeError>;
|
||||
type Error = ::oct::error::EnumEncodeError<<#repr as ::oct::encode::Encode>::Error, #error>;
|
||||
|
||||
#[allow(unreachable_patterns)]
|
||||
#[inline]
|
||||
|
@ -66,7 +72,7 @@ pub fn encode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
|||
|
||||
#(
|
||||
::oct::encode::Encode::encode(#captures, stream)
|
||||
.map_err(::core::convert::Into::<::oct::error::GenericEncodeError>::into)
|
||||
.map_err(::core::convert::Into::<#error>::into)
|
||||
.map_err(::oct::error::EnumEncodeError::BadField)?;
|
||||
)*
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DataStruct, Index};
|
||||
use syn::{DataStruct, Index, Type};
|
||||
|
||||
#[must_use]
|
||||
pub fn encode_struct(data: DataStruct) -> TokenStream {
|
||||
pub fn encode_struct(data: DataStruct, error: Type) -> TokenStream {
|
||||
let commands = data
|
||||
.fields
|
||||
.iter()
|
||||
|
@ -28,12 +28,12 @@ pub fn encode_struct(data: DataStruct) -> TokenStream {
|
|||
|
||||
quote! {
|
||||
::oct::encode::Encode::encode(&self.#name, stream)
|
||||
.map_err(::core::convert::Into::<::oct::error::GenericEncodeError>::into)?;
|
||||
.map_err(::core::convert::Into::<#error>::into)?;
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
type Error = ::oct::error::GenericEncodeError;
|
||||
type Error = #error;
|
||||
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut ::oct::encode::Output) -> ::core::result::Result<(), Self::Error> {
|
||||
|
|
|
@ -15,7 +15,14 @@ extern crate self as oct_macros;
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Data, DeriveInput};
|
||||
use syn::{
|
||||
parse_macro_input,
|
||||
parse2,
|
||||
Data,
|
||||
DeriveInput,
|
||||
Type,
|
||||
};
|
||||
use syn::parse::Parse;
|
||||
|
||||
mod derive_impl;
|
||||
|
||||
|
@ -25,19 +32,33 @@ mod repr;
|
|||
use discriminants::Discriminants;
|
||||
use repr::Repr;
|
||||
|
||||
#[proc_macro_derive(Decode)]
|
||||
#[proc_macro_derive(Decode, attributes(oct))]
|
||||
pub fn derive_decode(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let self_name = input.ident;
|
||||
|
||||
let mut error: Type = parse2(quote! { ::oct::error::GenericDecodeError }).unwrap();
|
||||
|
||||
for attr in &input.attrs {
|
||||
if attr.meta.path().is_ident("oct") {
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("decode_error") {
|
||||
error = Parse::parse(meta.value()?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let body = match input.data {
|
||||
Data::Struct(data) => derive_impl::decode_struct(data),
|
||||
Data::Struct(data) => derive_impl::decode_struct(data, error),
|
||||
|
||||
Data::Enum(data) => {
|
||||
let repr = Repr::get(&input.attrs).unwrap_or_default();
|
||||
|
||||
derive_impl::decode_enum(data, repr)
|
||||
derive_impl::decode_enum(data, repr, error)
|
||||
}
|
||||
|
||||
Data::Union(_) => panic!("untagged union `{self_name}` cannot derive `oct::decode::Decode`"),
|
||||
|
@ -46,6 +67,7 @@ pub fn derive_decode(input: TokenStream) -> TokenStream {
|
|||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let output = quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::oct::decode::Decode for #self_name #ty_generics
|
||||
#where_clause
|
||||
{
|
||||
|
@ -56,19 +78,33 @@ pub fn derive_decode(input: TokenStream) -> TokenStream {
|
|||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Encode)]
|
||||
#[proc_macro_derive(Encode, attributes(oct))]
|
||||
pub fn derive_encode(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let self_name = input.ident;
|
||||
|
||||
let mut error: Type = parse2(quote! { ::oct::error::GenericEncodeError }).unwrap();
|
||||
|
||||
for attr in &input.attrs {
|
||||
if attr.meta.path().is_ident("oct") {
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("encode_error") {
|
||||
error = Parse::parse(meta.value()?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let body = match input.data {
|
||||
Data::Struct(data) => derive_impl::encode_struct(data),
|
||||
Data::Struct(data) => derive_impl::encode_struct(data, error),
|
||||
|
||||
Data::Enum(data) => {
|
||||
let repr = Repr::get(&input.attrs).unwrap_or_default();
|
||||
|
||||
derive_impl::encode_enum(data, repr)
|
||||
derive_impl::encode_enum(data, repr, error)
|
||||
}
|
||||
|
||||
Data::Union(_) => panic!("untagged union `{self_name}` cannot derive `oct::encode::Encode`"),
|
||||
|
@ -77,6 +113,7 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
|
|||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let output = quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::oct::encode::Encode for #self_name #ty_generics
|
||||
#where_clause
|
||||
{
|
||||
|
@ -108,6 +145,7 @@ pub fn derive_sized_encode(input: TokenStream) -> TokenStream {
|
|||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let output = quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::oct::encode::SizedEncode for #self_name #ty_generics
|
||||
#where_clause
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ categories.workspace = true
|
|||
all-features = true
|
||||
|
||||
[dependencies]
|
||||
oct-macros = { path = "../oct-macros", version = "0.20", optional = true}
|
||||
oct-macros = { path = "../oct-macros", version = "0.21", optional = true}
|
||||
|
||||
[features]
|
||||
default = ["alloc", "proc-macro", "std"]
|
||||
|
|
|
@ -15,7 +15,6 @@ use crate::error::{
|
|||
CollectionDecodeError,
|
||||
EnumDecodeError,
|
||||
ItemDecodeError,
|
||||
SystemTimeDecodeError,
|
||||
};
|
||||
|
||||
use core::cell::{Cell, RefCell, UnsafeCell};
|
||||
|
@ -62,6 +61,8 @@ use crate::oct::encode::SizedEncode;
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
use {
|
||||
crate::error::SystemTimeDecodeError,
|
||||
|
||||
core::hash::{BuildHasher, Hash},
|
||||
|
||||
std::collections::{HashMap, HashSet},
|
||||
|
|
|
@ -176,6 +176,49 @@ fn test_decode_derive() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_derive_custom_error() {
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct Error;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct Foo(pub u8);
|
||||
|
||||
impl Decode for Foo {
|
||||
type Error = Error;
|
||||
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let Ok(value) = u8::decode(input);
|
||||
|
||||
if value % 0x2 != 0x0 {
|
||||
return Err(Error);
|
||||
}
|
||||
|
||||
let this = Self(value);
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Decode, Eq, PartialEq)]
|
||||
#[oct(decode_error = Error)]
|
||||
struct Bar(Foo);
|
||||
|
||||
test! {
|
||||
Foo {
|
||||
[0x00] => Ok(Foo(0x00)),
|
||||
[0x02] => Ok(Foo(0x02)),
|
||||
[0x80] => Ok(Foo(0x80)),
|
||||
[0x7F] => Err(Error),
|
||||
}
|
||||
|
||||
Bar {
|
||||
[0x00] => Ok(Bar(Foo(0x00))),
|
||||
[0xFE] => Ok(Bar(Foo(0xFE))),
|
||||
[0xFF] => Err(Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_oct_vec_long_len() {
|
||||
let data = [
|
||||
|
|
|
@ -42,7 +42,13 @@ pub use input::Input;
|
|||
|
||||
/// Implements [`Decode`] for the provided type.
|
||||
///
|
||||
/// This macro assumes the same format used by the equivalent [`Encode`](derive@crate::encode::Encode) macro.
|
||||
/// This derive macro assumes the same format used by the equivalent [`Encode`](derive@crate::encode::Encode) derive macro.
|
||||
///
|
||||
/// By default, this macro assumes that all fields implement <code>Decode<[Error]: [Into]<[GenericDecodeError]>></code>.
|
||||
/// If this is **not** the case, then the `#[oct(decode_error = T)` attribute should be used for the desired error `T` on the deriving type.
|
||||
///
|
||||
/// [Error]: Encode::Error
|
||||
/// [GenericDecodeError]: crate::error::GenericDecodeError
|
||||
#[cfg(feature = "proc-macro")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "proc-macro")))]
|
||||
#[doc(inline)]
|
||||
|
|
|
@ -58,13 +58,13 @@ pub use sized_encode::SizedEncode;
|
|||
|
||||
/// Implements [`Encode`] for the provided type.
|
||||
///
|
||||
/// This derive macro assumes that all fields implement <code>Encode<[Error]: [Into]<[GenericEncodeError]>></code>.
|
||||
/// If this is **not** the case, then the trait should be implemented manually instead.
|
||||
/// This derive macro, by default, assumes that all fields implement <code>Encode<[Error]: [Into]<[GenericEncodeError]>></code>.
|
||||
/// If this is **not** the case, then the `#[oct(encode_error = T)` attribute should be used for the desired error `T` on the deriving type.
|
||||
///
|
||||
/// [Error]: Encode::Error
|
||||
/// [GenericEncodeError]: crate::error::GenericEncodeError
|
||||
///
|
||||
/// Do also consider deriving [`SizedEncode`](derive@SizedEncode) -- if possible.
|
||||
/// Do also consider deriving [`SizedEncode`](derive@SizedEncode) -- if appropriate.
|
||||
///
|
||||
/// # Structs
|
||||
///
|
||||
|
|
|
@ -13,7 +13,6 @@ use crate::error::{
|
|||
ItemDecodeError,
|
||||
NonZeroDecodeError,
|
||||
LengthError,
|
||||
SystemTimeDecodeError,
|
||||
Utf8Error,
|
||||
};
|
||||
|
||||
|
@ -21,13 +20,16 @@ use core::convert::Infallible;
|
|||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::error::SystemTimeDecodeError;
|
||||
|
||||
/// A generic decoding error type.
|
||||
///
|
||||
/// The intended use of this type is by [derived](derive@crate::decode::Decode) implementations of [`crate::decode::Decode`].
|
||||
/// Manual implementors are recommended to use a custom or less generic type for the sake of efficiency.
|
||||
#[must_use]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum GenericDecodeError {
|
||||
/// A string contained a non-UTF-8 sequence.
|
||||
BadString(Utf8Error),
|
||||
|
@ -43,6 +45,8 @@ pub enum GenericDecodeError {
|
|||
/// The contained value denotes the raw, numerical value of the discriminant.
|
||||
UnassignedDiscriminant(PrimDiscriminant),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
/// The [`SystemTime`](std::time::SystemTime) type was too narrow.
|
||||
NarrowSystemTime(SystemTimeDecodeError),
|
||||
}
|
||||
|
@ -63,6 +67,8 @@ impl Display for GenericDecodeError {
|
|||
Self::UnassignedDiscriminant(value)
|
||||
=> write!(f, "discriminant value `{value:#X} has not been assigned"),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
Self::NarrowSystemTime(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
}
|
||||
|
@ -79,6 +85,8 @@ impl Error for GenericDecodeError {
|
|||
|
||||
Self::SmallBuffer(ref e) => Some(e),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
Self::NarrowSystemTime(ref e) => Some(e),
|
||||
|
||||
_ => None,
|
||||
|
@ -91,7 +99,7 @@ where
|
|||
L: Into<Self>,
|
||||
I: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
fn from(value: CollectionDecodeError<L, I>) -> Self {
|
||||
use CollectionDecodeError as Error;
|
||||
|
||||
|
@ -109,7 +117,7 @@ where
|
|||
D: Into<Self>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
fn from(value: EnumDecodeError<T, D, F>) -> Self {
|
||||
use EnumDecodeError as Error;
|
||||
|
||||
|
@ -151,6 +159,8 @@ impl From<LengthError> for GenericDecodeError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl From<SystemTimeDecodeError> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: SystemTimeDecodeError) -> Self {
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::error::{
|
|||
use core::cell::BorrowError;
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
/// A generic encoding error type.
|
||||
///
|
||||
|
@ -66,8 +66,6 @@ impl Error for GenericEncodeError {
|
|||
}
|
||||
}
|
||||
|
||||
impl Eq for GenericEncodeError { }
|
||||
|
||||
impl From<BorrowError> for GenericEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: BorrowError) -> Self {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#![cfg(feature = "std")]
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
@ -14,6 +16,7 @@ use core::fmt::{self, Display, Formatter};
|
|||
///
|
||||
/// Note that a UNIX timestamp is here defined as a signed, 64-bit integer denoting a difference of time to 1 january 1970, as measured in Greenwich using seconds.
|
||||
/// This error should therefore not occur on systems that use the same or a more precise counter.
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
pub struct SystemTimeDecodeError {
|
||||
|
@ -21,6 +24,7 @@ pub struct SystemTimeDecodeError {
|
|||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Display for SystemTimeDecodeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
|
@ -28,8 +32,10 @@ impl Display for SystemTimeDecodeError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Error for SystemTimeDecodeError { }
|
||||
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl From<Infallible> for SystemTimeDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
|
|
Loading…
Add table
Reference in a new issue