runbot 0.1.5

QQ client framework
Documentation
use std::{fmt::Debug, sync::Arc};

use crate::{
    bot_context::BotContext,
    event::{self, Post},
};
use async_trait::async_trait;

#[derive(Debug)]
pub enum Processor {
    Post(Box<dyn PostProcessor>),
    Message(Box<dyn MessageProcessor>),
    Notice(Box<dyn NoticeProcessor>),
    Request(Box<dyn RequestProcessor>),
    Module(Box<dyn ModuleProcessor>),
}

impl Processor {
    pub fn id(&self) -> &'static str {
        match self {
            Processor::Post(processor) => processor.id(),
            Processor::Message(processor) => processor.id(),
            Processor::Notice(processor) => processor.id(),
            Processor::Request(processor) => processor.id(),
            Processor::Module(processor) => processor.id(),
        }
    }

    pub async fn process(
        &self,
        bot_ctx: Arc<BotContext>,
        post: &event::Post,
    ) -> anyhow::Result<bool> {
        if let (Processor::Message(processor), event::Post::Message(message)) = (self, post) {
            processor.process_message(bot_ctx, message).await
        } else if let (Processor::Notice(processor), event::Post::Notice(notice)) = (self, post) {
            processor.process_notice(bot_ctx, notice).await
        } else if let (Processor::Request(processor), event::Post::Request(request)) = (self, post)
        {
            processor.process_request(bot_ctx, request).await
        } else if let Processor::Post(processor) = self {
            processor.process_post(bot_ctx, post).await
        } else if let Processor::Module(processor) = self {
            processor.process_post(bot_ctx, post).await
        } else {
            Ok(false)
        }
    }
}

#[async_trait]
pub trait PostProcessor: Send + Sync + Debug {
    fn id(&self) -> &'static str;
    async fn process_post(
        &self,
        bot_ctx: Arc<BotContext>,
        post: &event::Post,
    ) -> anyhow::Result<bool>;
}

#[async_trait]
pub trait MessageProcessor: Send + Sync + Debug {
    fn id(&self) -> &'static str;
    async fn process_message(
        &self,
        bot_ctx: Arc<BotContext>,
        message: &event::Message,
    ) -> anyhow::Result<bool>;
}

#[async_trait]
pub trait NoticeProcessor: Send + Sync + Debug {
    fn id(&self) -> &'static str;
    async fn process_notice(
        &self,
        bot_ctx: Arc<BotContext>,
        event: &event::Notice,
    ) -> anyhow::Result<bool>;
}

#[async_trait]
pub trait RequestProcessor: Send + Sync + Debug {
    fn id(&self) -> &'static str;

    async fn process_request(
        &self,
        bot_ctx: Arc<BotContext>,
        request: &event::Request,
    ) -> anyhow::Result<bool>;
}

#[async_trait]
pub trait ModuleProcessor: Send + Sync + Debug {
    fn id(&self) -> &'static str;
    fn name(&self) -> &'static str;
    fn help(&self) -> &'static str;
    async fn process_post(
        &self,
        bot_ctx: Arc<BotContext>,
        post: &event::Post,
    ) -> anyhow::Result<bool>;
    fn processors(&self) -> Arc<Vec<Processor>>;
}

#[derive(Debug)]
pub struct ProcessModule {
    pub id: &'static str,
    pub name: &'static str,
    pub help: &'static str,
    pub processors: Arc<Vec<Processor>>,
}

#[async_trait]
impl ModuleProcessor for ProcessModule {
    fn id(&self) -> &'static str {
        self.id
    }
    fn name(&self) -> &'static str {
        self.name
    }
    fn help(&self) -> &'static str {
        self.help
    }
    async fn process_post(
        &self,
        bot_ctx: Arc<BotContext>,
        post: &event::Post,
    ) -> anyhow::Result<bool> {
        loop_processors(bot_ctx, self.processors.iter(), post).await
    }
    fn processors(&self) -> Arc<Vec<Processor>> {
        self.processors.clone()
    }
}

impl Into<Processor> for Box<dyn PostProcessor> {
    fn into(self) -> Processor {
        Processor::Post(self)
    }
}

impl Into<Processor> for Box<dyn MessageProcessor> {
    fn into(self) -> Processor {
        Processor::Message(self)
    }
}

impl Into<Processor> for Box<dyn NoticeProcessor> {
    fn into(self) -> Processor {
        Processor::Notice(self)
    }
}

impl Into<Processor> for Box<dyn RequestProcessor> {
    fn into(self) -> Processor {
        Processor::Request(self)
    }
}

impl Into<Processor> for Box<dyn ModuleProcessor> {
    fn into(self) -> Processor {
        Processor::Module(self)
    }
}

pub(crate) async fn loop_processors(
    bot_ctx: Arc<BotContext>,
    processor_iter: core::slice::Iter<'_, Processor>,
    post: &Post,
) -> anyhow::Result<bool> {
    for processor in processor_iter {
        let processe_result = processor.process(bot_ctx.clone(), post).await;
        match processe_result {
            Ok(b) => {
                if b {
                    tracing::debug!(
                        "post processed, id : {:?} , post : {:?}",
                        processor.id(),
                        post
                    );
                    return Ok(b);
                }
            }
            Err(err) => {
                tracing::error!(
                    "processor error, id: {:?} , post {:?} error: {:?}",
                    processor.id(),
                    post,
                    err
                );
                return Err(err);
            }
        }
    }
    Ok(false)
}