diff --git a/src/handler.rs b/src/handler.rs index cd1e309..373f20c 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -197,11 +197,11 @@ async fn lawsuit_command_handler( UserOption::get_optional(options.get(5)).wrap_err("accused_layer")?; let mut lawsuit = Lawsuit { - plaintiff: plaintiff.0.id.to_string(), - accused: accused.0.id.to_string(), - judge: judge.0.id.to_string(), - plaintiff_layer: plaintiff_layer.map(|l| l.0.id.to_string()), - accused_layer: accused_layer.map(|l| l.0.id.to_string()), + plaintiff: plaintiff.id.into(), + accused: accused.id.into(), + judge: judge.id.into(), + plaintiff_layer: plaintiff_layer.map(|user| user.id.into()), + accused_layer: accused_layer.map(|user| user.id.into()), reason: reason.to_owned(), state: LawsuitState::Initial, court_room: None, @@ -228,7 +228,7 @@ async fn lawsuit_command_handler( Some(category) => { let id = category.id; mongo_client - .set_court_category(&guild_id.to_string(), &id.to_string()) + .set_court_category(guild_id.into(), id.into()) .await?; } None => return Ok(Response::Simple("Das ist keine Kategorie!".to_owned())), @@ -277,13 +277,13 @@ struct UserOption; #[nougat::gat] impl GetOption for UserOption { - type Get<'a> = (&'a User, &'a Option); + type Get<'a> = &'a User; fn extract( command: &ApplicationCommandInteractionDataOptionValue, ) -> crate::Result> { - if let ApplicationCommandInteractionDataOptionValue::User(user, member) = command { - Ok((user, member)) + if let ApplicationCommandInteractionDataOptionValue::User(user, _) = command { + Ok(user) } else { Err(eyre!("Expected user!")) } diff --git a/src/lawsuit.rs b/src/lawsuit.rs index c7a143e..dfb443b 100644 --- a/src/lawsuit.rs +++ b/src/lawsuit.rs @@ -1,14 +1,16 @@ -use std::str::FromStr; - use color_eyre::Result; use serde::{Deserialize, Serialize}; use serenity::{ http::Http, - model::{channel::PermissionOverwriteType, id::ChannelId, prelude::*, Permissions}, + model::{channel::PermissionOverwriteType, prelude::*, Permissions}, }; use tracing::info; -use crate::{handler::Response, model::CourtRoom, Mongo, WrapErr}; +use crate::{ + handler::Response, + model::{CourtRoom, SnowflakeId}, + Mongo, WrapErr, +}; #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum LawsuitState { @@ -19,14 +21,14 @@ pub enum LawsuitState { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Lawsuit { - pub plaintiff: String, - pub accused: String, - pub plaintiff_layer: Option, - pub accused_layer: Option, - pub judge: String, + pub plaintiff: SnowflakeId, + pub accused: SnowflakeId, + pub plaintiff_layer: Option, + pub accused_layer: Option, + pub judge: SnowflakeId, pub reason: String, pub state: LawsuitState, - pub court_room: Option, + pub court_room: Option, } impl Lawsuit { @@ -37,7 +39,7 @@ impl Lawsuit { mongo_client: &Mongo, ) -> Result { let state = mongo_client - .find_or_insert_state(&guild_id.to_string()) + .find_or_insert_state(guild_id.into()) .await?; let free_room = state @@ -55,7 +57,7 @@ impl Lawsuit { http, guild_id, state.court_rooms.len(), - ChannelId::from_str(category).wrap_err("invalid channel_id stored")?, + *category, mongo_client, ) .await @@ -83,8 +85,6 @@ impl Lawsuit { return Ok(response); } - // TODO: now give the people their roles - Ok(Response::Simple(format!( "ha eine ufgmacht im channel <#{}>", room.channel_id @@ -104,7 +104,7 @@ impl Lawsuit { .channels(http) .await .wrap_err("fetch channels")?; - let channel = channels.get(&ChannelId::from_str(&room.channel_id).expect("oh god")); + let channel = channels.get(&room.channel_id.into()); match channel { Some(channel) => { @@ -155,7 +155,7 @@ async fn create_room( http: &Http, guild_id: GuildId, room_len: usize, - category_id: ChannelId, + category_id: SnowflakeId, mongo_client: &Mongo, ) -> Result> { let room_number = room_len + 1; @@ -184,7 +184,7 @@ async fn create_room( let channel_id = match channels.values().find(|c| c.name() == room_name) { Some(channel) => { - if channel.parent_id != Some(category_id) { + if channel.parent_id != Some(category_id.into()) { return Ok(Err(Response::Simple(format!( "de channel {room_name} isch i de falsche kategorie, man eh" )))); @@ -210,13 +210,13 @@ async fn create_room( }; let room = CourtRoom { - channel_id: channel_id.to_string(), + channel_id: channel_id.into(), ongoing_lawsuit: false, - role_id: role_id.to_string(), + role_id: role_id.into(), }; mongo_client - .add_court_room(&guild_id.to_string(), &room) + .add_court_room(guild_id.into(), &room) .await .wrap_err("add court room to database")?; diff --git a/src/model.rs b/src/model.rs index d39f50f..649f595 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,28 +1,104 @@ +use std::{ + fmt::{Display, Formatter}, + num::ParseIntError, + str::FromStr, +}; + use color_eyre::Result; use mongodb::{ bson, - bson::doc, + bson::{doc, Bson}, options::{ClientOptions, Credential}, Client, Collection, Database, }; use serde::{Deserialize, Serialize}; +use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; use tracing::info; use crate::{lawsuit::Lawsuit, WrapErr}; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(transparent)] +pub struct SnowflakeId(#[serde(with = "serde_string")] pub u64); + +impl FromStr for SnowflakeId { + type Err = ParseIntError; + + fn from_str(s: &str) -> std::result::Result { + s.parse().map(Self) + } +} + +impl Display for SnowflakeId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +mod serde_string { + use std::{fmt::Display, str::FromStr}; + + use serde::{de, Deserialize, Deserializer, Serializer}; + + pub fn serialize(value: &T, serializer: S) -> Result + where + T: Display, + S: Serializer, + { + serializer.collect_str(value) + } + + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: FromStr, + T::Err: Display, + D: Deserializer<'de>, + { + String::deserialize(deserializer)? + .parse() + .map_err(de::Error::custom) + } +} + +impl From for Bson { + fn from(id: SnowflakeId) -> Self { + Bson::String(id.to_string()) + } +} + +macro_rules! from_snowflake { + ($($ty:ty),*) => { + $( + impl From for $ty { + fn from(id: SnowflakeId) -> Self { + Self(id.0) + } + } + + impl From<$ty> for SnowflakeId { + fn from(id: $ty) -> Self { + Self(id.0) + } + } + )* + }; +} + +from_snowflake!(GuildId, RoleId, ChannelId, UserId); + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct State { - pub guild_id: String, + pub guild_id: SnowflakeId, pub lawsuits: Vec, - pub court_category: Option, + pub court_category: Option, pub court_rooms: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CourtRoom { - pub channel_id: String, + pub channel_id: SnowflakeId, pub ongoing_lawsuit: bool, - pub role_id: String, + pub role_id: SnowflakeId, } pub struct Mongo { @@ -53,7 +129,7 @@ impl Mongo { Ok(Self { db }) } - pub async fn find_or_insert_state(&self, guild_id: &str) -> Result { + pub async fn find_or_insert_state(&self, guild_id: SnowflakeId) -> Result { let coll = self.state_coll(); let state = coll .find_one(doc! {"guild_id": &guild_id }, None) @@ -71,7 +147,7 @@ impl Mongo { Ok(state) } - pub async fn new_state(&self, guild_id: String) -> Result { + pub async fn new_state(&self, guild_id: SnowflakeId) -> Result { let state = State { guild_id, lawsuits: vec![], @@ -86,7 +162,11 @@ impl Mongo { Ok(state) } - pub async fn set_court_category(&self, guild_id: &str, category: &str) -> Result<()> { + pub async fn set_court_category( + &self, + guild_id: SnowflakeId, + category: SnowflakeId, + ) -> Result<()> { let _ = self.find_or_insert_state(guild_id).await?; let coll = self.state_coll(); coll.update_one( @@ -99,7 +179,7 @@ impl Mongo { Ok(()) } - pub async fn add_court_room(&self, guild_id: &str, room: &CourtRoom) -> Result<()> { + pub async fn add_court_room(&self, guild_id: SnowflakeId, room: &CourtRoom) -> Result<()> { let _ = self.find_or_insert_state(guild_id).await?; let coll = self.state_coll(); coll.update_one(