use crate::{
guild::Permissions,
id::{RoleId, UserId},
};
use serde::{de::Deserializer, ser::SerializeStruct, Deserialize, Serialize, Serializer};
use serde_repr::{Deserialize_repr, Serialize_repr};
pub(crate) mod integer {
use serde::de::{Deserializer, Error as DeError, Visitor};
use std::{
fmt::{Formatter, Result as FmtResult},
marker::PhantomData,
};
struct IdVisitor(PhantomData<u64>);
impl<'de> Visitor<'de> for IdVisitor {
type Value = u64;
fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("string or integer snowflake")
}
fn visit_u64<E: DeError>(self, value: u64) -> Result<Self::Value, E> {
Ok(value)
}
fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
value.parse().map_err(DeError::custom)
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
deserializer.deserialize_any(IdVisitor(PhantomData))
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PermissionOverwrite {
pub allow: Permissions,
pub deny: Permissions,
pub kind: PermissionOverwriteType,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum PermissionOverwriteType {
Member(UserId),
Role(RoleId),
}
#[derive(Deserialize)]
struct PermissionOverwriteData {
allow: Permissions,
deny: Permissions,
#[serde(deserialize_with = "integer::deserialize")]
id: u64,
#[serde(rename = "type")]
kind: PermissionOverwriteTargetType,
}
#[derive(Clone, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
pub enum PermissionOverwriteTargetType {
Member = 1,
Role = 0,
}
impl<'de> Deserialize<'de> for PermissionOverwrite {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let data = PermissionOverwriteData::deserialize(deserializer)?;
let span = tracing::trace_span!("deserializing permission overwrite");
let _span_enter = span.enter();
let kind = match data.kind {
PermissionOverwriteTargetType::Member => {
let id = UserId(data.id);
tracing::trace!(id = %id.0, kind = ?data.kind);
PermissionOverwriteType::Member(id)
}
PermissionOverwriteTargetType::Role => {
let id = RoleId(data.id);
tracing::trace!(id = %id.0, kind = ?data.kind);
PermissionOverwriteType::Role(id)
}
};
Ok(Self {
allow: data.allow,
deny: data.deny,
kind,
})
}
}
impl Serialize for PermissionOverwrite {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut state = serializer.serialize_struct("PermissionOverwriteData", 4)?;
state.serialize_field("allow", &self.allow)?;
state.serialize_field("deny", &self.deny)?;
match &self.kind {
PermissionOverwriteType::Member(id) => {
state.serialize_field("id", &id.0.to_string())?;
state.serialize_field("type", &(PermissionOverwriteTargetType::Member as u8))?;
}
PermissionOverwriteType::Role(id) => {
state.serialize_field("id", &id.0.to_string())?;
state.serialize_field("type", &(PermissionOverwriteTargetType::Role as u8))?;
}
}
state.end()
}
}
#[cfg(test)]
mod tests {
use super::{
PermissionOverwrite, PermissionOverwriteTargetType, PermissionOverwriteType, Permissions,
};
use crate::id::UserId;
use serde_test::Token;
#[test]
fn test_overwrite() {
let overwrite = PermissionOverwrite {
allow: Permissions::CREATE_INVITE,
deny: Permissions::KICK_MEMBERS,
kind: PermissionOverwriteType::Member(UserId(12_345_678)),
};
let input = r#"{
"allow": "1",
"deny": "2",
"id": "12345678",
"type": 1
}"#;
assert_eq!(
serde_json::from_str::<PermissionOverwrite>(input).unwrap(),
overwrite
);
assert_eq!(serde_json::to_string_pretty(&overwrite).unwrap(), input);
}
#[test]
fn test_blank_overwrite() {
let raw = r#"{
"allow": "1",
"deny": "2",
"id": 0,
"type": 1
}"#;
let value = PermissionOverwrite {
allow: Permissions::CREATE_INVITE,
deny: Permissions::KICK_MEMBERS,
kind: PermissionOverwriteType::Member(UserId(0)),
};
let deserialized = serde_json::from_str::<PermissionOverwrite>(raw).unwrap();
assert_eq!(deserialized, value);
serde_test::assert_tokens(
&value,
&[
Token::Struct {
name: "PermissionOverwriteData",
len: 4,
},
Token::Str("allow"),
Token::Str("1"),
Token::Str("deny"),
Token::Str("2"),
Token::Str("id"),
Token::Str("0"),
Token::Str("type"),
Token::U8(1),
Token::StructEnd,
],
);
}
#[test]
fn test_overwrite_type_name() {
serde_test::assert_tokens(&PermissionOverwriteTargetType::Member, &[Token::U8(1)]);
serde_test::assert_tokens(&PermissionOverwriteTargetType::Role, &[Token::U8(0)]);
}
}