disma 0.16.4

Discord server management has never been easier!
Documentation
use crate::{
    channel::{AwaitingChannel, Channel, ExistingChannel},
    core::events::{Change, ChangeEntity, ChangeEvent, ChangeEventListener},
    guild::{ExistingGuild, GuildCommander},
};

use super::Command;

pub struct AddChannel {
    channel: AwaitingChannel,
}

impl AddChannel {
    pub fn new(channel: AwaitingChannel) -> Self {
        Self { channel }
    }

    fn describe(&self) -> Change {
        Change::Create(
            ChangeEntity::Channel,
            self.channel.unique_name().to_string(),
        )
    }
}

impl Command for AddChannel {
    fn execute(
        &self,
        commander: &dyn GuildCommander,
        event_listener: &dyn ChangeEventListener,
        existing_guild: &mut ExistingGuild,
    ) {
        let result = commander.add_channel(
            &self.channel,
            existing_guild.roles(),
            existing_guild.categories(),
        );

        let event = match result {
            Ok(channel) => {
                existing_guild.add_or_replace_channel(channel);
                ChangeEvent::Success(self.describe())
            }
            Err(message) => ChangeEvent::Error(self.describe(), message),
        };

        event_listener.handle(event);
    }
}

pub struct UpdateChannel {
    existing_channel: ExistingChannel,
    awaiting_channel: AwaitingChannel,
}

impl UpdateChannel {
    pub fn new(existing_channel: ExistingChannel, awaiting_channel: AwaitingChannel) -> Self {
        Self {
            existing_channel,
            awaiting_channel,
        }
    }

    fn describe(&self) -> Change {
        Change::Update(
            ChangeEntity::Channel,
            self.existing_channel.unique_name().to_string(),
        )
    }
}

impl Command for UpdateChannel {
    fn execute(
        &self,
        commander: &dyn GuildCommander,
        event_listener: &dyn ChangeEventListener,
        existing_guild: &mut ExistingGuild,
    ) {
        let result = commander.update_channel(
            &self.existing_channel.id,
            &self.awaiting_channel,
            existing_guild.roles(),
            existing_guild.categories(),
        );

        let event = match result {
            Ok(channel) => {
                existing_guild.add_or_replace_channel(channel);
                ChangeEvent::Success(self.describe())
            }
            Err(message) => ChangeEvent::Error(self.describe(), message),
        };

        event_listener.handle(event);
    }
}

pub struct DeleteChannel {
    channel: ExistingChannel,
}

impl DeleteChannel {
    pub fn new(channel: ExistingChannel) -> Self {
        Self { channel }
    }

    fn describe(&self) -> Change {
        Change::Delete(
            ChangeEntity::Channel,
            self.channel.unique_name().to_string(),
        )
    }
}

impl Command for DeleteChannel {
    fn execute(
        &self,
        commander: &dyn GuildCommander,
        event_listener: &dyn ChangeEventListener,
        existing_guild: &mut ExistingGuild,
    ) {
        let result = commander.delete_channel(&self.channel.id);

        let event = match result {
            Ok(()) => {
                existing_guild.remove_channel(self.channel.clone());
                ChangeEvent::Success(self.describe())
            }
            Err(message) => ChangeEvent::Error(self.describe(), message),
        };

        event_listener.handle(event);
    }
}

#[cfg(test)]
mod tests {
    use mock_it::{any, eq};

    use crate::{
        channel::Channel,
        core::{
            commands::Command,
            events::{Change, ChangeEntity, ChangeEvent, ChangeEventListenerMock},
        },
        guild::{ExistingGuild, GuildCommanderMock},
        tests::fixtures::{
            commands::{AddChannelFixture, DeleteChannelFixture, UpdateChannelFixture},
            existing::{ExistingChannelFixture, ExistingGuildFixture},
        },
    };

    const AN_ERROR_MESSAGE: &str = "Unexpected error";
    const A_CHANNEL_NAME: &str = "channel abc";

    fn setup() -> (GuildCommanderMock, ChangeEventListenerMock, ExistingGuild) {
        let commander = GuildCommanderMock::new();
        let event_listener = ChangeEventListenerMock::new();
        let existing_guild = ExistingGuildFixture::new().build();

        event_listener.when_handle(any()).will_return_default();

        return (commander, event_listener, existing_guild);
    }

    #[test]
    fn when_adding_channel_should_add_channel_with_commander() {
        let (commander, event_listener, mut existing_guild) = setup();
        commander
            .when_add_channel(any(), any(), any())
            .will_return(Ok(ExistingChannelFixture::new().build()));

        let add_command = AddChannelFixture::new().build();
        add_command.execute(&commander, &event_listener, &mut existing_guild);

        commander.expect_add_channel(
            eq(&add_command.channel),
            eq(existing_guild.roles()),
            eq(existing_guild.categories()),
        );
    }

    #[test]
    fn given_failing_commander_when_adding_channel_should_notify_of_error() {
        let (commander, event_listener, mut existing_guild) = setup();
        commander
            .when_add_channel(any(), any(), any())
            .will_return(Err(AN_ERROR_MESSAGE.to_string()));

        let add_command = AddChannelFixture::new().build();
        add_command.execute(&commander, &event_listener, &mut existing_guild);

        event_listener.expect_handle(eq(ChangeEvent::Error(
            Change::Create(
                ChangeEntity::Channel,
                add_command.channel.unique_name().to_string(),
            ),
            AN_ERROR_MESSAGE.to_string(),
        )));
    }

    #[test]
    fn given_succeeding_commander_when_adding_channel_should_notify_of_success_and_add_existing_channel(
    ) {
        let (commander, event_listener, mut existing_guild) = setup();
        let created_channel = ExistingChannelFixture::new().build();
        commander
            .when_add_channel(any(), any(), any())
            .will_return(Ok(created_channel.clone()));

        let add_command = AddChannelFixture::new().build();
        add_command.execute(&commander, &event_listener, &mut existing_guild);

        event_listener.expect_handle(eq(ChangeEvent::Success(Change::Create(
            ChangeEntity::Channel,
            add_command.channel.unique_name().to_string(),
        ))));
        assert_eq!(existing_guild.channels().to_list(), vec![&created_channel]);
    }

    #[test]
    fn when_updating_channel_should_update_channel_with_commander() {
        let (commander, event_listener, mut existing_guild) = setup();
        commander
            .when_update_channel(any(), any(), any(), any())
            .will_return(Ok(ExistingChannelFixture::new().build()));

        let update_command = UpdateChannelFixture::new().build();
        update_command.execute(&commander, &event_listener, &mut existing_guild);

        commander.expect_update_channel(
            eq(&update_command.existing_channel.id),
            eq(&update_command.awaiting_channel),
            eq(existing_guild.roles()),
            eq(existing_guild.categories()),
        );
    }

    #[test]
    fn given_failing_commander_when_updating_channel_should_notify_of_error() {
        let (commander, event_listener, mut existing_guild) = setup();
        commander
            .when_update_channel(any(), any(), any(), any())
            .will_return(Err(AN_ERROR_MESSAGE.to_string()));

        let update_command = UpdateChannelFixture::new().build();
        update_command.execute(&commander, &event_listener, &mut existing_guild);

        event_listener.expect_handle(eq(ChangeEvent::Error(
            Change::Update(
                ChangeEntity::Channel,
                update_command.awaiting_channel.unique_name().to_string(),
            ),
            AN_ERROR_MESSAGE.to_string(),
        )));
    }

    #[test]
    fn given_succeeding_commander_when_updating_channel_should_notify_of_success_and_replace_existing_channel(
    ) {
        let (commander, event_listener, mut existing_guild) = setup();
        let existing_channel = ExistingChannelFixture::new()
            .with_name(A_CHANNEL_NAME)
            .build();
        let updated_channel = ExistingChannelFixture::new()
            .with_name(A_CHANNEL_NAME)
            .build();
        existing_guild.add_or_replace_channel(existing_channel);
        commander
            .when_update_channel(any(), any(), any(), any())
            .will_return(Ok(updated_channel.clone()));

        let update_command = UpdateChannelFixture::new().build();
        update_command.execute(&commander, &event_listener, &mut existing_guild);

        event_listener.expect_handle(eq(ChangeEvent::Success(Change::Update(
            ChangeEntity::Channel,
            update_command.awaiting_channel.unique_name().to_string(),
        ))));
        assert_eq!(existing_guild.channels().to_list(), vec![&updated_channel]);
    }

    #[test]
    fn when_deleting_channel_should_delete_channel_with_commander() {
        let (commander, event_listener, mut existing_guild) = setup();
        commander.when_delete_channel(any()).will_return(Ok(()));

        let delete_command = DeleteChannelFixture::new().build();
        delete_command.execute(&commander, &event_listener, &mut existing_guild);

        commander.expect_delete_channel(eq(&delete_command.channel.id));
    }

    #[test]
    fn given_failing_commander_when_deleting_channel_should_notify_of_error() {
        let (commander, event_listener, mut existing_guild) = setup();
        commander
            .when_delete_channel(any())
            .will_return(Err(AN_ERROR_MESSAGE.to_string()));

        let delete_command = DeleteChannelFixture::new().build();
        delete_command.execute(&commander, &event_listener, &mut existing_guild);

        event_listener.expect_handle(eq(ChangeEvent::Error(
            Change::Delete(
                ChangeEntity::Channel,
                delete_command.channel.unique_name().to_string(),
            ),
            AN_ERROR_MESSAGE.to_string(),
        )));
    }

    #[test]
    fn given_succeeding_commander_when_deleting_channel_should_notify_of_success_and_remove_existing_channel(
    ) {
        let (commander, event_listener, mut existing_guild) = setup();
        commander.when_delete_channel(any()).will_return(Ok(()));

        let delete_command = DeleteChannelFixture::new().build();
        existing_guild.add_or_replace_channel(delete_command.channel.clone());
        assert!(!existing_guild.channels().to_list().is_empty());

        delete_command.execute(&commander, &event_listener, &mut existing_guild);

        event_listener.expect_handle(eq(ChangeEvent::Success(Change::Delete(
            ChangeEntity::Channel,
            delete_command.channel.unique_name().to_string(),
        ))));
        assert!(existing_guild.channels().to_list().is_empty());
    }
}