Skip to content

Commit

Permalink
refactor: use macro for u8 enums
Browse files Browse the repository at this point in the history
  • Loading branch information
timokoesters committed Dec 10, 2024
1 parent 5efa8db commit 6c5fd1b
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 138 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

111 changes: 21 additions & 90 deletions src/content_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use serde_bytes::ByteBuf;
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
use std::collections::HashMap;

use crate::serde_enum_as_u8;

#[derive(Serialize_tuple, Deserialize_tuple, PartialEq, Eq, Debug, Clone)]
pub struct MimiContent {
replaces: Option<ByteBuf>,
Expand Down Expand Up @@ -256,63 +258,22 @@ impl<'de> Deserialize<'de> for NestedPart {
}
}

#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum Disposition {
Unspecified,
Render,
Reaction,
Profile,
Inline,
Icon,
Attachment,
Session,
Preview,
Unspecified = 0,
Render = 1,
Reaction = 2,
Profile = 3,
Inline = 4,
Icon = 5,
Attachment = 6,
Session = 7,
Preview = 8,
Custom(u8),
}

impl Serialize for Disposition {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Disposition::Unspecified => 0,
Disposition::Render => 1,
Disposition::Reaction => 2,
Disposition::Profile => 3,
Disposition::Inline => 4,
Disposition::Icon => 5,
Disposition::Attachment => 6,
Disposition::Session => 7,
Disposition::Preview => 8,
Disposition::Custom(u) => *u,
}
.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for Disposition {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let u8 = u8::deserialize(deserializer)?;
let disposition = match u8 {
0 => Disposition::Unspecified,
1 => Disposition::Render,
2 => Disposition::Reaction,
3 => Disposition::Profile,
4 => Disposition::Inline,
5 => Disposition::Icon,
6 => Disposition::Attachment,
7 => Disposition::Session,
8 => Disposition::Preview,
u => Disposition::Custom(u),
};

Ok(disposition)
}
}
serde_enum_as_u8!(Disposition);

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NestedPartContent {
Expand Down Expand Up @@ -345,46 +306,16 @@ pub struct AeadInfo {
aad: ByteBuf,
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PartSemantics {
ChooseOne,
SingleUnit,
ProcessAll,
}

impl Serialize for PartSemantics {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
PartSemantics::ChooseOne => 0_u8,
PartSemantics::SingleUnit => 1,
PartSemantics::ProcessAll => 2,
}
.serialize(serializer)
}
ChooseOne = 0,
SingleUnit = 1,
ProcessAll = 2,
Custom(u8),
}

impl<'de> Deserialize<'de> for PartSemantics {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let u8 = u8::deserialize(deserializer)?;
Ok(match u8 {
0 => PartSemantics::ChooseOne,
1 => PartSemantics::SingleUnit,
2 => PartSemantics::ProcessAll,
u => {
return Err(de::Error::invalid_value(
Unexpected::Unsigned(u64::from(u)),
&"0, 1 or 2",
))
}
})
}
}
serde_enum_as_u8!(PartSemantics);

#[cfg(test)]
mod tests {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

pub mod content_container;
mod macros;
pub mod message_status;
48 changes: 48 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#[macro_export]
macro_rules! serde_enum_as_u8 {
($enum_name:ident) => {
// https://doc.rust-lang.org/reference/items/enumerations.html?search=#pointer-casting
impl $enum_name {
fn discriminant(&self) -> u8 {
// This is safe if the enum only contains primitive types
let pointer = self as *const Self as *const u8;
unsafe { *pointer }
}
}

impl Serialize for $enum_name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Custom(custom) => *custom,
known => known.discriminant(),
}
.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for $enum_name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let value = u8::deserialize(deserializer)?;

// This assumes that Custom is the last variant of the enum
let variant = if value < Self::Custom(0).discriminant() {
// The value corresponds to the discriminant of the enum
let result = unsafe { *(&value as *const u8 as *const Self) };
assert_eq!(result.discriminant(), value);

result
} else {
Self::Custom(value)
};

Ok(variant)
}
}
};
}
59 changes: 12 additions & 47 deletions src/message_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use serde::{
use serde_bytes::ByteBuf;
use serde_tuple::{Deserialize_tuple, Serialize_tuple};

use crate::serde_enum_as_u8;

#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Eq)]
pub struct MessageStatusReport {
timestamp: Timestamp,
Expand Down Expand Up @@ -68,57 +70,20 @@ pub struct PerMessageStatus {
status: MessageStatus,
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum MessageStatus {
Unread,
Delivered,
Read,
Expired,
Deleted,
Hidden,
Error,
Unread = 0,
Delivered = 1,
Read = 2,
Expired = 3,
Deleted = 4,
Hidden = 5,
Error = 6,
Custom(u8),
}

impl Serialize for MessageStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
MessageStatus::Unread => 0,
MessageStatus::Delivered => 1,
MessageStatus::Read => 2,
MessageStatus::Expired => 3,
MessageStatus::Deleted => 4,
MessageStatus::Hidden => 5,
MessageStatus::Error => 6,
MessageStatus::Custom(u) => *u,
}
.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for MessageStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let u8 = u8::deserialize(deserializer)?;
let disposition = match u8 {
0 => MessageStatus::Unread,
1 => MessageStatus::Delivered,
2 => MessageStatus::Read,
3 => MessageStatus::Expired,
4 => MessageStatus::Deleted,
5 => MessageStatus::Hidden,
6 => MessageStatus::Error,
u => MessageStatus::Custom(u),
};

Ok(disposition)
}
}
serde_enum_as_u8!(MessageStatus);

#[cfg(test)]
mod tests {
Expand Down

0 comments on commit 6c5fd1b

Please sign in to comment.