use std::marker::PhantomData;
use serde_json::{json, Value};
pub struct NoChannel;
pub struct HasChannel;
pub struct CreateMessage<S = NoChannel> {
channel_id: Option<String>,
content: Option<String>,
tts: bool,
embeds: Vec<Value>,
components: Vec<Value>,
flags: u64,
_state: PhantomData<S>,
}
impl CreateMessage<NoChannel> {
pub fn new() -> Self {
Self {
channel_id: None,
content: None,
tts: false,
embeds: Vec::new(),
components: Vec::new(),
flags: 0,
_state: PhantomData,
}
}
pub fn channel(self, channel_id: impl Into<String>) -> CreateMessage<HasChannel> {
CreateMessage {
channel_id: Some(channel_id.into()),
content: self.content,
tts: self.tts,
embeds: self.embeds,
components: self.components,
flags: self.flags,
_state: PhantomData,
}
}
}
impl Default for CreateMessage<NoChannel> {
fn default() -> Self {
Self::new()
}
}
impl<S> CreateMessage<S> {
pub fn content(mut self, content: impl Into<String>) -> Self {
self.content = Some(content.into());
self
}
pub fn tts(mut self, tts: bool) -> Self {
self.tts = tts;
self
}
pub fn embed(mut self, embed: Value) -> Self {
self.embeds.push(embed);
self
}
pub fn component(mut self, component: Value) -> Self {
self.components.push(component);
self
}
pub fn flags(mut self, flags: u64) -> Self {
self.flags = flags;
self
}
}
impl CreateMessage<HasChannel> {
pub fn build(self) -> Value {
json!({
"channel_id": self.channel_id,
"content": self.content,
"tts": self.tts,
"embeds": self.embeds,
"components": self.components,
"flags": self.flags,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_requires_channel_at_compile_time() {
let payload = CreateMessage::new().channel("111222333").content("hello").tts(false).build();
assert_eq!(payload["channel_id"], "111222333");
assert_eq!(payload["content"], "hello");
assert_eq!(payload["tts"], false);
}
#[test]
fn optional_fields_before_channel() {
let payload = CreateMessage::new().content("pre-channel content").tts(true).channel("999").build();
assert_eq!(payload["channel_id"], "999");
assert_eq!(payload["content"], "pre-channel content");
assert_eq!(payload["tts"], true);
}
#[test]
fn no_channel_type_has_no_build_method() {
let _: CreateMessage<NoChannel> = CreateMessage::new();
}
}