ic_query/sns/report/model/reports/proposals/
row.rs1use serde::{Deserialize as SerdeDeserialize, Deserializer, Serialize};
8use std::borrow::Cow;
9
10#[must_use]
12pub fn sns_proposal_action_label(id: u64) -> Cow<'static, str> {
13 match id {
14 0 => Cow::Borrowed("unspecified"),
15 1 => Cow::Borrowed("motion"),
16 2 => Cow::Borrowed("manage_nervous_system_parameters"),
17 3 => Cow::Borrowed("upgrade_sns_controlled_canister"),
18 4 => Cow::Borrowed("add_generic_nervous_system_function"),
19 5 => Cow::Borrowed("remove_generic_nervous_system_function"),
20 6 => Cow::Borrowed("execute_generic_nervous_system_function"),
21 7 => Cow::Borrowed("upgrade_sns_to_next_version"),
22 8 => Cow::Borrowed("manage_sns_metadata"),
23 9 => Cow::Borrowed("transfer_sns_treasury_funds"),
24 10 => Cow::Borrowed("register_dapp_canisters"),
25 11 => Cow::Borrowed("deregister_dapp_canisters"),
26 12 => Cow::Borrowed("mint_sns_tokens"),
27 13 => Cow::Borrowed("manage_ledger_parameters"),
28 14 => Cow::Borrowed("manage_dapp_canister_settings"),
29 15 => Cow::Borrowed("advance_sns_target_version"),
30 16 => Cow::Borrowed("set_topics_for_custom_proposals"),
31 17 => Cow::Borrowed("register_extension"),
32 18 => Cow::Borrowed("execute_extension_operation"),
33 19 => Cow::Borrowed("upgrade_extension"),
34 id if id >= 1_000 => Cow::Owned(format!("generic:{id}")),
35 id => Cow::Owned(format!("unknown:{id}")),
36 }
37}
38
39#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, SerdeDeserialize, Serialize)]
46#[serde(rename_all = "snake_case")]
47pub enum SnsProposalDecisionState {
48 Open,
50 Decided,
52 Executed,
54 Failed,
56}
57
58impl SnsProposalDecisionState {
59 #[must_use]
61 pub const fn as_str(self) -> &'static str {
62 match self {
63 Self::Open => "open",
64 Self::Decided => "decided",
65 Self::Executed => "executed",
66 Self::Failed => "failed",
67 }
68 }
69}
70
71#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
78#[serde(deny_unknown_fields)]
79pub struct SnsProposalRow {
80 pub proposal_id: u64,
81 pub action_id: u64,
82 pub title: String,
83 pub summary: String,
84 pub url: Option<String>,
85 pub decision_state: SnsProposalDecisionState,
86 #[serde(deserialize_with = "deserialize_required_option")]
87 pub status: Option<i32>,
88 #[serde(deserialize_with = "deserialize_required_option")]
89 pub topic: Option<String>,
90 pub reject_cost_e8s: u64,
91 pub proposal_creation_timestamp_seconds: u64,
92 pub decided_timestamp_seconds: Option<u64>,
93 pub executed_timestamp_seconds: Option<u64>,
94 pub failed_timestamp_seconds: Option<u64>,
95 pub failure_reason: Option<SnsProposalFailureReason>,
96 pub reward_event_round: u64,
97 pub reward_event_end_timestamp_seconds: Option<u64>,
98 pub is_eligible_for_rewards: bool,
99 pub latest_tally: Option<SnsProposalTally>,
100 pub ballot_count: usize,
101 pub ballots: Vec<SnsProposalBallotRow>,
102 pub payload_text_rendering: Option<String>,
103 pub proposer_neuron_id: Option<String>,
104}
105
106fn deserialize_required_option<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
107where
108 D: Deserializer<'de>,
109 T: SerdeDeserialize<'de>,
110{
111 Option::<T>::deserialize(deserializer)
112}
113
114#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
121#[serde(deny_unknown_fields)]
122pub struct SnsProposalBallotRow {
123 pub neuron_id: String,
124 pub vote: i32,
125 pub cast_timestamp_seconds: u64,
126 pub voting_power: u64,
127}
128
129#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
136pub struct SnsProposalFailureReason {
137 pub error_type: i32,
138 pub error_message: String,
139}
140
141#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
148pub struct SnsProposalTally {
149 pub timestamp_seconds: u64,
150 pub yes: u64,
151 pub no: u64,
152 pub total: u64,
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn proposal_decision_state_labels_round_trip() {
161 for (state, label) in [
162 (SnsProposalDecisionState::Open, "open"),
163 (SnsProposalDecisionState::Decided, "decided"),
164 (SnsProposalDecisionState::Executed, "executed"),
165 (SnsProposalDecisionState::Failed, "failed"),
166 ] {
167 assert_eq!(
168 serde_json::to_string(&state).unwrap(),
169 format!("\"{label}\"")
170 );
171 assert_eq!(
172 serde_json::from_str::<SnsProposalDecisionState>(&format!("\"{label}\"")).unwrap(),
173 state
174 );
175 }
176 assert!(serde_json::from_str::<SnsProposalDecisionState>("\"unknown\"").is_err());
177 }
178
179 #[test]
180 fn proposal_action_labels_cover_native_generic_and_unknown_ids() {
181 for (id, label) in [
182 (0, "unspecified"),
183 (1, "motion"),
184 (2, "manage_nervous_system_parameters"),
185 (3, "upgrade_sns_controlled_canister"),
186 (4, "add_generic_nervous_system_function"),
187 (5, "remove_generic_nervous_system_function"),
188 (6, "execute_generic_nervous_system_function"),
189 (7, "upgrade_sns_to_next_version"),
190 (8, "manage_sns_metadata"),
191 (9, "transfer_sns_treasury_funds"),
192 (10, "register_dapp_canisters"),
193 (11, "deregister_dapp_canisters"),
194 (12, "mint_sns_tokens"),
195 (13, "manage_ledger_parameters"),
196 (14, "manage_dapp_canister_settings"),
197 (15, "advance_sns_target_version"),
198 (16, "set_topics_for_custom_proposals"),
199 (17, "register_extension"),
200 (18, "execute_extension_operation"),
201 (19, "upgrade_extension"),
202 (20, "unknown:20"),
203 (1_000, "generic:1000"),
204 (u64::MAX, "generic:18446744073709551615"),
205 ] {
206 assert_eq!(sns_proposal_action_label(id), label);
207 }
208 }
209}