walle_core/
action.rs

1//! Action 相关模型定义
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    prelude::{WalleError, WalleResult},
6    structs::Selft,
7    util::{GetSelf, PushToValueMap, ValueMap, ValueMapExt},
8};
9
10/// 标准 Action 模型
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct Action {
13    pub action: String,
14    pub params: ValueMap,
15    #[serde(rename = "self")]
16    pub selft: Option<Selft>,
17}
18
19pub trait ToAction: PushToValueMap {
20    fn ty(&self) -> &'static str;
21    fn selft(&self) -> Option<Selft> {
22        None
23    }
24    fn to_action(self) -> Action
25    where
26        Self: Sized,
27    {
28        Action {
29            action: self.ty().to_string(),
30            selft: self.selft(),
31            params: {
32                let mut map = ValueMap::new();
33                self.push_to(&mut map);
34                map
35            },
36        }
37    }
38}
39
40pub trait TryFromAction: Sized {
41    fn try_from_action_mut(action: &mut Action) -> WalleResult<Self>;
42    fn try_from_action(mut action: Action) -> WalleResult<Self> {
43        Self::try_from_action_mut(&mut action)
44    }
45}
46
47impl GetSelf for Action {
48    fn get_self(&self) -> Selft {
49        self.selft.clone().unwrap_or_default()
50    }
51}
52
53/// 泛型可扩展 Action 模型
54#[derive(Debug, Clone, PartialEq)]
55pub struct BaseAction<T> {
56    pub action: T,
57    pub selft: Option<Selft>,
58    pub extra: ValueMap,
59}
60
61impl<T> From<BaseAction<T>> for Action
62where
63    T: ToAction,
64{
65    fn from(mut action: BaseAction<T>) -> Self {
66        Self {
67            action: action.action.ty().to_string(),
68            selft: action.selft,
69            params: {
70                action.action.push_to(&mut action.extra);
71                action.extra
72            },
73        }
74    }
75}
76
77impl<T> From<(T, Selft)> for Action
78where
79    T: ToAction + Into<ValueMap>,
80{
81    fn from(v: (T, Selft)) -> Self {
82        Self {
83            action: v.0.ty().to_owned(),
84            params: v.0.into(),
85            selft: Some(v.1),
86        }
87    }
88}
89
90impl<T> TryFrom<Action> for BaseAction<T>
91where
92    T: TryFromAction,
93{
94    type Error = WalleError;
95    fn try_from(mut value: Action) -> Result<Self, Self::Error> {
96        Ok(Self {
97            action: T::try_from_action_mut(&mut value)?,
98            selft: value.selft,
99            extra: value.params,
100        })
101    }
102}
103
104use walle_macro::{
105    _PushToValueMap as PushToValueMap, _ToAction as ToAction, _TryFromAction as TryFromAction,
106    _TryFromValue as TryFromValue,
107};
108
109#[derive(Debug, Clone, PartialEq, Eq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
110pub struct GetLatestEvents {
111    pub limit: i64,
112    pub timeout: i64,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
116pub struct DeleteMessage {
117    pub message_id: String,
118}
119
120macro_rules! action {
121    ($name: ident) => {
122        #[derive(Debug, Clone, PartialEq, Eq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
123        pub struct $name;
124    };
125    ($name: ident, $($f: ident: $fty: ty),*) => {
126        #[derive(Debug, Clone, PartialEq, Eq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
127        pub struct $name {
128            $(pub $f: $fty,)*
129        }
130    };
131}
132
133use crate::util::OneBotBytes;
134
135#[derive(Debug, Clone, PartialEq, Eq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
136pub struct GetUserInfo {
137    pub user_id: String,
138}
139// Group
140action!(GetGroupInfo, group_id: String);
141action!(GetGroupList);
142action!(GetGroupMemberInfo, group_id: String, user_id: String);
143action!(GetGroupMemberList, group_id: String);
144action!(SetGroupName, group_id: String, group_name: String);
145action!(LeaveGroup, group_id: String);
146// Guild
147action!(GetGuildInfo, guild_id: String);
148action!(GetGuildList);
149action!(SetGuildName, guild_id: String, guild_name: String);
150action!(GetGuildMemberInfo, guild_id: String, user_id: String);
151action!(GetGuildMemberList, guild_id: String);
152action!(LeaveGuild, guild_id: String);
153// Channel
154action!(GetChannelInfo, guild_id: String, channel_id: String);
155action!(GetChannelList, guild_id: String, joined_only: bool);
156action!(
157    SetChannelName,
158    guild_id: String,
159    channel_id: String,
160    channel_name: String
161);
162action!(
163    GetChannelMemberInfo,
164    guild_id: String,
165    channel_id: String,
166    user_id: String
167);
168action!(GetChannelMemberList, guild_id: String, channel_id: String);
169action!(LeaveChannel, guild_id: String, channel_id: String);
170// message
171#[derive(Debug, Clone, PartialEq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
172pub struct SendMessage {
173    pub detail_type: String,
174    pub user_id: Option<String>,
175    pub group_id: Option<String>,
176    pub guild_id: Option<String>,
177    pub channel_id: Option<String>,
178    pub message: crate::segment::Segments,
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
182pub struct GetFile {
183    pub file_id: String,
184    pub ty: String,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, TryFromValue, TryFromAction, ToAction, PushToValueMap)]
188pub struct UploadFile {
189    pub ty: String,
190    pub name: String,
191    pub url: Option<String>,
192    pub headers: Option<std::collections::HashMap<String, String>>,
193    pub path: Option<String>,
194    pub data: Option<OneBotBytes>,
195    pub sha256: Option<String>,
196}
197
198#[derive(Debug, Clone, PartialEq, Eq)]
199pub enum UploadFileFragmented {
200    Prepare {
201        name: String,
202        total_size: i64,
203    },
204    Transfer {
205        file_id: String,
206        offset: i64,
207        data: OneBotBytes,
208    },
209    Finish {
210        file_id: String,
211        sha256: Option<String>,
212    },
213}
214
215impl TryFromAction for UploadFileFragmented {
216    fn try_from_action_mut(action: &mut Action) -> WalleResult<Self> {
217        if action.action != "upload_file_fragmented" {
218            Err(WalleError::DeclareNotMatch(
219                "upload_file_fragmented",
220                action.action.clone(),
221            ))
222        } else {
223            match action.params.remove_downcast::<String>("stage")?.as_str() {
224                "prepare" => Ok(Self::Prepare {
225                    name: action.params.remove_downcast("name")?,
226                    total_size: action.params.remove_downcast("total_size")?,
227                }),
228                "transfer" => Ok(Self::Transfer {
229                    file_id: action.params.remove_downcast("file_id")?,
230                    offset: action.params.remove_downcast("offset")?,
231                    data: action.params.remove_downcast("data")?,
232                }),
233                "finish" => Ok(Self::Finish {
234                    file_id: action.params.remove_downcast("file_id")?,
235                    sha256: action.params.try_remove_downcast("sha256")?,
236                }),
237                x => Err(WalleError::DeclareNotMatch(
238                    "prepare or transfer or finish",
239                    x.to_string(),
240                )),
241            }
242        }
243    }
244}
245
246impl TryFrom<&mut ValueMap> for UploadFileFragmented {
247    type Error = WalleError;
248    fn try_from(map: &mut ValueMap) -> Result<Self, Self::Error> {
249        match map.remove_downcast::<String>("stage")?.as_str() {
250            "prepare" => Ok(Self::Prepare {
251                name: map.remove_downcast("name")?,
252                total_size: map.remove_downcast("total_size")?,
253            }),
254            "transfer" => Ok(Self::Transfer {
255                file_id: map.remove_downcast("file_id")?,
256                offset: map.remove_downcast("offset")?,
257                data: map.remove_downcast("data")?,
258            }),
259            "finish" => Ok(Self::Finish {
260                file_id: map.remove_downcast("file_id")?,
261                sha256: map.try_remove_downcast("sha256")?,
262            }),
263            x => Err(WalleError::DeclareNotMatch(
264                "prepare or transfer or finish",
265                x.to_string(),
266            )),
267        }
268    }
269}
270
271impl PushToValueMap for UploadFileFragmented {
272    fn push_to(self, map: &mut ValueMap) {
273        match self {
274            UploadFileFragmented::Prepare { name, total_size } => {
275                map.insert("stage".to_string(), "prepare".into());
276                map.insert("name".to_string(), name.into());
277                map.insert("total_size".to_string(), total_size.into());
278            }
279            UploadFileFragmented::Transfer {
280                file_id,
281                offset,
282                data,
283            } => {
284                map.insert("stage".to_string(), "transfer".into());
285                map.insert("file_id".to_string(), file_id.into());
286                map.insert("offset".to_string(), offset.into());
287                map.insert("data".to_string(), data.into());
288            }
289            UploadFileFragmented::Finish { file_id, sha256 } => {
290                map.insert("stage".to_string(), "finish".into());
291                map.insert("file_id".to_string(), file_id.into());
292                map.insert("sha256".to_string(), sha256.into());
293            }
294        }
295    }
296}
297
298impl ToAction for UploadFileFragmented {
299    fn ty(&self) -> &'static str {
300        "upload_file_fragmented"
301    }
302}
303
304impl From<UploadFileFragmented> for Action {
305    fn from(u: UploadFileFragmented) -> Self {
306        u.to_action()
307    }
308}
309
310#[derive(Debug, Clone, PartialEq, Eq)]
311pub enum GetFileFragmented {
312    Prepare {
313        file_id: String,
314    },
315    Transfer {
316        file_id: String,
317        offset: i64,
318        size: i64,
319    },
320}
321
322impl TryFromAction for GetFileFragmented {
323    fn try_from_action_mut(action: &mut Action) -> WalleResult<Self> {
324        if action.action != "get_file_fragmented" {
325            Err(WalleError::DeclareNotMatch(
326                "get_file_fragmented",
327                action.action.clone(),
328            ))
329        } else {
330            match action.params.remove_downcast::<String>("stage")?.as_str() {
331                "prepare" => Ok(Self::Prepare {
332                    file_id: action.params.remove_downcast("file_id")?,
333                }),
334                "transfer" => Ok(Self::Transfer {
335                    file_id: action.params.remove_downcast("file_id")?,
336                    offset: action.params.remove_downcast("offset")?,
337                    size: action.params.remove_downcast("size")?,
338                }),
339                x => Err(WalleError::DeclareNotMatch(
340                    "prepare or transfer or finish",
341                    x.to_string(),
342                )),
343            }
344        }
345    }
346}
347
348impl TryFrom<&mut ValueMap> for GetFileFragmented {
349    type Error = WalleError;
350    fn try_from(map: &mut ValueMap) -> WalleResult<Self> {
351        match map.remove_downcast::<String>("stage")?.as_str() {
352            "prepare" => Ok(Self::Prepare {
353                file_id: map.remove_downcast("file_id")?,
354            }),
355            "transfer" => Ok(Self::Transfer {
356                file_id: map.remove_downcast("file_id")?,
357                offset: map.remove_downcast("offset")?,
358                size: map.remove_downcast("size")?,
359            }),
360            x => Err(WalleError::DeclareNotMatch(
361                "prepare | transfer | finish",
362                x.to_string(),
363            )),
364        }
365    }
366}
367
368impl PushToValueMap for GetFileFragmented {
369    fn push_to(self, map: &mut ValueMap) {
370        match self {
371            Self::Prepare { file_id } => {
372                map.insert("stage".to_string(), "prepare".into());
373                map.insert("file_id".to_string(), file_id.into());
374            }
375            Self::Transfer {
376                file_id,
377                offset,
378                size,
379            } => {
380                map.insert("stage".to_string(), "transfer".into());
381                map.insert("file_id".to_string(), file_id.into());
382                map.insert("offset".to_string(), offset.into());
383                map.insert("size".to_string(), size.into());
384            }
385        }
386    }
387}
388
389impl ToAction for GetFileFragmented {
390    fn ty(&self) -> &'static str {
391        "get_file_fragmented"
392    }
393}
394
395impl From<GetFileFragmented> for Action {
396    fn from(g: GetFileFragmented) -> Self {
397        g.to_action()
398    }
399}
400
401#[test]
402fn action() {
403    use crate::{value_map, WalleResult};
404    let action = Action {
405        action: "upload_file".to_string(),
406        selft: None,
407        params: value_map! {
408            "type": "type",
409            "name": "name",
410            "extra": "test"
411        },
412    };
413    let uf: WalleResult<BaseAction<UploadFile>> = action.try_into();
414    println!("{:?}", uf);
415}