stoat-rs 0.2.5

Stoat API Wrapper
Documentation
use async_trait::async_trait;
use bytes::Bytes;
use stoat_models::v0::{
    DataEditUser, FlagResponse, MutualResponse, User, UserProfile, UserVoiceState,
};

use crate::{FileExt, GlobalCache, HttpClient, Identifiable, Result, builders::SendMessageBuilder};

#[async_trait]
pub trait UserExt: Identifiable {
    fn mention(&self) -> String;
    fn name(&self) -> &str;

    fn voice(&self, cache: impl AsRef<GlobalCache>) -> Vec<(String, UserVoiceState)>;

    async fn send(&self, http: impl AsRef<HttpClient> + Send) -> Result<SendMessageBuilder>;
    async fn edit(
        &mut self,
        http: impl AsRef<HttpClient> + Send,
        data: &DataEditUser,
    ) -> Result<()>;
    async fn fetch_profile(&self, http: impl AsRef<HttpClient> + Send) -> Result<UserProfile>;
    async fn fetch_flags(&self, http: impl AsRef<HttpClient> + Send) -> Result<FlagResponse>;
    async fn fetch_mutuals(&self, http: impl AsRef<HttpClient> + Send) -> Result<MutualResponse>;
    async fn fetch_default_avatar(&self, http: impl AsRef<HttpClient> + Send) -> Result<Bytes>;

    fn avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String;
    fn default_avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String;
}

#[async_trait]
impl UserExt for User {
    fn mention(&self) -> String {
        format!("<@{}>", &self.id)
    }

    fn name(&self) -> &str {
        self.display_name.as_deref().unwrap_or(&self.username)
    }

    fn voice(&self, cache: impl AsRef<GlobalCache>) -> Vec<(String, UserVoiceState)> {
        let mut states = Vec::new();

        cache.as_ref().servers.iter_sync(|_, server| {
            for channel in &server.channels {
                if let Some(channel_voice_state) = cache.as_ref().voice_states.get_sync(channel) {
                    if let Some(user_voice_state) = channel_voice_state
                        .participants
                        .iter()
                        .find(|s| &s.id == &self.id)
                    {
                        states.push((channel.clone(), user_voice_state.clone()));
                    }
                }
            }

            true
        });

        states
    }

    async fn send(&self, http: impl AsRef<HttpClient> + Send) -> Result<SendMessageBuilder> {
        let dm_channel = http.as_ref().open_dm(&self.id).await?;

        Ok(SendMessageBuilder::new(
            http.as_ref().clone(),
            dm_channel.id().to_string(),
        ))
    }

    async fn edit(
        &mut self,
        http: impl AsRef<HttpClient> + Send,
        data: &DataEditUser,
    ) -> Result<()> {
        let user = http.as_ref().edit_user(&self.id, data).await?;

        *self = user;

        Ok(())
    }

    async fn fetch_profile(&self, http: impl AsRef<HttpClient> + Send) -> Result<UserProfile> {
        http.as_ref().fetch_user_profile(&self.id).await
    }

    async fn fetch_flags(&self, http: impl AsRef<HttpClient> + Send) -> Result<FlagResponse> {
        http.as_ref().fetch_user_flags(&self.id).await
    }

    async fn fetch_mutuals(&self, http: impl AsRef<HttpClient> + Send) -> Result<MutualResponse> {
        http.as_ref().fetch_user_mutuals(&self.id).await
    }

    async fn fetch_default_avatar(&self, http: impl AsRef<HttpClient> + Send) -> Result<Bytes> {
        http.as_ref().fetch_default_avatar(&self.id).await
    }

    fn avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String {
        self.avatar
            .as_ref()
            .map(|file| file.url(http.as_ref(), false))
            .unwrap_or_else(|| self.default_avatar_url(http.as_ref()))
    }

    fn default_avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String {
        format!("{}/users/{}/default_avatar", &http.as_ref().base, &self.id)
    }
}

impl Identifiable for User {
    fn id(&self) -> &str {
        &self.id
    }
}