1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! 实现 mirai 提供的 API 接口,如拉取群列表等
//!
pub mod friend_list;
pub mod group_list;
pub mod member_list;
pub mod message_from_id;
pub mod recall;
pub mod send_friend_message;
pub mod send_group_message;

use serde::Serialize;

use crate::Result;

/// 所有发往 mirai 的请求都实现这个 trait
pub trait Api: ApiRequest {
    /// 请求返回的类型
    type Response: serde::de::DeserializeOwned;

    fn process_response(value: serde_json::Value) -> Result<Self::Response>;
}

/// 对应将请求序列化成 ws packet 的行为
pub trait ApiRequest: Send {
    fn command(&self) -> &'static str;

    fn sub_command(&self) -> Option<&'static str>;

    fn encode(&self, sync_id: i64) -> String;
}

#[derive(Serialize)]
struct ApiRequestData<T: Serialize> {
    #[serde(rename = "syncId")]
    sync_id: i64,

    command: &'static str,

    #[serde(rename = "subCommand")]
    sub_command: Option<&'static str>,

    content: T,
}

#[macro_export]
macro_rules! api {
    (
        command = $cmd:literal,
        $req:path,
        $rsp:path
    ) => {
        $crate::api!(
            command = $cmd,
            subcommand = None,
            field = "data",
            $req,
            $rsp
        );
    };
    (
        command = $cmd:literal,
        subcommand = $sub_cmd:expr,
        field = $field:tt,
        $req:path,
        $rsp:path
    ) => {
        impl $crate::api::ApiRequest for $req {
            fn command(&self) -> &'static str {
                $cmd
            }
            fn sub_command(&self) -> Option<&'static str> {
                $sub_cmd
            }
            fn encode(&self, sync_id: i64) -> String {
                let request = $crate::api::ApiRequestData {
                    command: self.command(),
                    sub_command: self.sub_command(),
                    sync_id,
                    content: &self,
                };
                serde_json::to_string(&request).unwrap()
            }
        }
        // 定义返回的类型
        crate::api!(@def_resp field = $field);

        impl $crate::api::Api for $req {
            type Response = $rsp;
            fn process_response(value: serde_json::Value) -> $crate::Result<Self::Response> {
                debug!("process value {:?} as response", value);
                let resp: ApiResponseData::<$rsp> = serde_json::from_value(value)?;
                if resp.code != 0 {
                    return Err($crate::Error::Request {
                        code: resp.code,
                        msg: resp.msg,
                    });
                }
                Ok(resp.data)
            }
        }
    };
    (@def_resp field = "data") => {
        #[derive(Deserialize)]
        struct ApiResponseData<T> {
            code: i32,
            msg: String,
            data: T,
        }
    };
    (@def_resp field = "flatten") => {
        #[derive(Deserialize)]
        struct ApiResponseData<T> {
            code: i32,
            msg: String,
            #[serde(flatten)]
            data: T,
        }
    };
    (@def_resp field = "default") => {
        #[derive(Deserialize)]
        struct ApiResponseData<T> {
            code: i32,
            msg: String,
            #[serde(default)]
            data: T,
        }
    }
}