Skip to main content

wechat_oa_sdk/models/
menu.rs

1use serde::{Deserialize, Serialize};
2
3/// Menu button types.
4#[derive(Debug, Clone, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum MenuButtonType {
7    Click,
8    View,
9    #[serde(rename = "scancode_push")]
10    ScancodePush,
11    #[serde(rename = "scancode_waitmsg")]
12    ScancodeWaitmsg,
13    #[serde(rename = "pic_sysphoto")]
14    PicSysphoto,
15    #[serde(rename = "pic_photo_or_album")]
16    PicPhotoOrAlbum,
17    #[serde(rename = "pic_weixin")]
18    PicWeixin,
19    #[serde(rename = "location_select")]
20    LocationSelect,
21    #[serde(rename = "media_id")]
22    MediaId,
23    #[serde(rename = "article_id")]
24    ArticleId,
25    #[serde(rename = "article_view_limited")]
26    ArticleViewLimited,
27    #[serde(rename = "miniprogram")]
28    MiniProgram,
29}
30
31/// A menu button (can have sub-buttons).
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct MenuButton {
34    /// Button name (max 16 bytes for main, 60 bytes for sub)
35    pub name: String,
36    /// Button type (required for leaf buttons)
37    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
38    pub button_type: Option<String>,
39    /// Key for click type buttons
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub key: Option<String>,
42    /// URL for view type buttons
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub url: Option<String>,
45    /// Media ID for media_id type buttons
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub media_id: Option<String>,
48    /// Article ID for article_id type buttons
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub article_id: Option<String>,
51    /// Mini program app ID
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub appid: Option<String>,
54    /// Mini program page path
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub pagepath: Option<String>,
57    /// Sub-buttons (max 5)
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub sub_button: Option<Vec<MenuButton>>,
60}
61
62impl MenuButton {
63    /// Create a click type button.
64    pub fn click(name: impl Into<String>, key: impl Into<String>) -> Self {
65        Self {
66            name: name.into(),
67            button_type: Some("click".to_string()),
68            key: Some(key.into()),
69            url: None,
70            media_id: None,
71            article_id: None,
72            appid: None,
73            pagepath: None,
74            sub_button: None,
75        }
76    }
77
78    /// Create a view (URL) type button.
79    pub fn view(name: impl Into<String>, url: impl Into<String>) -> Self {
80        Self {
81            name: name.into(),
82            button_type: Some("view".to_string()),
83            key: None,
84            url: Some(url.into()),
85            media_id: None,
86            article_id: None,
87            appid: None,
88            pagepath: None,
89            sub_button: None,
90        }
91    }
92
93    /// Create a parent button with sub-buttons.
94    pub fn parent(name: impl Into<String>, sub_buttons: Vec<MenuButton>) -> Self {
95        Self {
96            name: name.into(),
97            button_type: None,
98            key: None,
99            url: None,
100            media_id: None,
101            article_id: None,
102            appid: None,
103            pagepath: None,
104            sub_button: Some(sub_buttons),
105        }
106    }
107
108    /// Create a mini program button.
109    pub fn miniprogram(
110        name: impl Into<String>,
111        url: impl Into<String>,
112        appid: impl Into<String>,
113        pagepath: impl Into<String>,
114    ) -> Self {
115        Self {
116            name: name.into(),
117            button_type: Some("miniprogram".to_string()),
118            key: None,
119            url: Some(url.into()),
120            media_id: None,
121            article_id: None,
122            appid: Some(appid.into()),
123            pagepath: Some(pagepath.into()),
124            sub_button: None,
125        }
126    }
127}
128
129/// Menu structure for creating menus.
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct Menu {
132    pub button: Vec<MenuButton>,
133}
134
135/// Response from getting current menu.
136#[derive(Debug, Clone, Deserialize)]
137pub struct GetMenuResponse {
138    pub menu: Option<Menu>,
139    pub conditionalmenu: Option<Vec<ConditionalMenu>>,
140}
141
142/// Conditional menu with match rules.
143#[derive(Debug, Clone, Deserialize, Serialize)]
144pub struct ConditionalMenu {
145    pub button: Vec<MenuButton>,
146    pub matchrule: MatchRule,
147    pub menuid: Option<i64>,
148}
149
150/// Match rules for conditional menus.
151#[derive(Debug, Clone, Deserialize, Serialize, Default)]
152pub struct MatchRule {
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub tag_id: Option<String>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub client_platform_type: Option<String>,
157}