1use serde::{Deserialize, Serialize};
8
9macro_rules! governance_code_enum {
10 (
11 $(#[$enum_attr:meta])*
12 pub enum $name:ident {
13 $($(#[$variant_attr:meta])* $variant:ident = $code:literal => $label:literal,)*
14 }
15 ) => {
16 $(#[$enum_attr])*
17 pub enum $name {
18 $($(#[$variant_attr])* #[serde(rename = $label)] $variant,)*
19 }
20
21 impl $name {
22 #[must_use]
24 pub const fn from_code(code: i32) -> Self {
25 match code {
26 $($code => Self::$variant,)*
27 _ => Self::Unspecified,
28 }
29 }
30
31 #[must_use]
33 pub const fn code(self) -> i32 {
34 match self { $(Self::$variant => $code,)* }
35 }
36
37 #[must_use]
39 pub const fn as_str(self) -> &'static str {
40 match self { $(Self::$variant => $label,)* }
41 }
42 }
43 };
44}
45
46pub(in crate::nns) const NNS_PROPOSAL_SORT_API_LABEL: &str = "api";
47pub(in crate::nns) const NNS_PROPOSAL_SORT_ID_LABEL: &str = "id";
48pub(in crate::nns) const NNS_PROPOSAL_SORT_STATUS_LABEL: &str = "status";
49pub(in crate::nns) const NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL: &str = "reward-status";
50pub(in crate::nns) const NNS_PROPOSAL_SORT_TOPIC_LABEL: &str = "topic";
51pub(in crate::nns) const NNS_PROPOSAL_SORT_PROPOSER_LABEL: &str = "proposer";
52pub(in crate::nns) const NNS_PROPOSAL_SORT_TITLE_LABEL: &str = "title";
53pub(in crate::nns) const NNS_PROPOSAL_SORT_ACTION_LABEL: &str = "action";
54pub(in crate::nns) const NNS_PROPOSAL_SORT_YES_LABEL: &str = "yes";
55pub(in crate::nns) const NNS_PROPOSAL_SORT_NO_LABEL: &str = "no";
56pub(in crate::nns) const NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL: &str = "total-votes";
57pub(in crate::nns) const NNS_PROPOSAL_SORT_TALLY_TIME_LABEL: &str = "tally-time";
58pub(in crate::nns) const NNS_PROPOSAL_SORT_VOTING_POWER_LABEL: &str = "voting-power";
59pub(in crate::nns) const NNS_PROPOSAL_SORT_BALLOTS_LABEL: &str = "ballots";
60pub(in crate::nns) const NNS_PROPOSAL_SORT_REJECT_COST_LABEL: &str = "reject-cost";
61pub(in crate::nns) const NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL: &str = "reward-round";
62pub(in crate::nns) const NNS_PROPOSAL_SORT_PROPOSED_LABEL: &str = "proposed";
63pub(in crate::nns) const NNS_PROPOSAL_SORT_DEADLINE_LABEL: &str = "deadline";
64pub(in crate::nns) const NNS_PROPOSAL_SORT_DECIDED_LABEL: &str = "decided";
65pub(in crate::nns) const NNS_PROPOSAL_SORT_EXECUTED_LABEL: &str = "executed";
66pub(in crate::nns) const NNS_PROPOSAL_SORT_FAILED_LABEL: &str = "failed";
67pub(in crate::nns) const NNS_PROPOSAL_SORT_ASC_LABEL: &str = "asc";
68pub(in crate::nns) const NNS_PROPOSAL_SORT_DESC_LABEL: &str = "desc";
69pub(in crate::nns) const NNS_PROPOSAL_SORT_NONE_LABEL: &str = "none";
70
71governance_code_enum! {
72 #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
79 pub enum NnsProposalStatus {
80 Unspecified = 0 => "unspecified",
82 Open = 1 => "open",
84 Rejected = 2 => "rejected",
86 Adopted = 3 => "adopted",
88 Executed = 4 => "executed",
90 Failed = 5 => "failed",
92 }
93}
94
95governance_code_enum! {
96 #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
103 pub enum NnsProposalRewardStatus {
104 Unspecified = 0 => "unspecified",
106 AcceptVotes = 1 => "accept-votes",
108 ReadyToSettle = 2 => "ready-to-settle",
110 Settled = 3 => "settled",
112 Ineligible = 4 => "ineligible",
114 }
115}
116
117governance_code_enum! {
118 #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
125 pub enum NnsProposalTopic {
126 Unspecified = 0 => "unspecified",
128 NeuronManagement = 1 => "neuron-management",
130 ExchangeRate = 2 => "exchange-rate",
132 NetworkEconomics = 3 => "network-economics",
134 Governance = 4 => "governance",
136 NodeAdmin = 5 => "node-admin",
138 ParticipantManagement = 6 => "participant-management",
140 SubnetManagement = 7 => "subnet-management",
142 NetworkCanisterManagement = 8 => "network-canister-management",
144 Kyc = 9 => "kyc",
146 NodeProviderRewards = 10 => "node-provider-rewards",
148 IcOsVersionDeployment = 12 => "ic-os-version-deployment",
150 IcOsVersionElection = 13 => "ic-os-version-election",
152 SnsAndCommunityFund = 14 => "sns-and-community-fund",
154 ApiBoundaryNodeManagement = 15 => "api-boundary-node-management",
156 SubnetRental = 16 => "subnet-rental",
158 ApplicationCanisterManagement = 17 => "application-canister-management",
160 ProtocolCanisterManagement = 18 => "protocol-canister-management",
162 }
163}
164
165#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
172pub enum NnsProposalListSort {
173 #[default]
175 Api,
176 Id,
178 Status,
180 RewardStatus,
182 Topic,
184 Proposer,
186 Title,
188 Action,
190 Yes,
192 No,
194 TotalVotes,
196 TallyTime,
198 VotingPower,
200 Ballots,
202 RejectCost,
204 RewardRound,
206 Proposed,
208 Deadline,
210 Decided,
212 Executed,
214 Failed,
216}
217
218impl NnsProposalListSort {
219 #[must_use]
221 pub const fn as_str(self) -> &'static str {
222 match self {
223 Self::Api => NNS_PROPOSAL_SORT_API_LABEL,
224 Self::Id => NNS_PROPOSAL_SORT_ID_LABEL,
225 Self::Status => NNS_PROPOSAL_SORT_STATUS_LABEL,
226 Self::RewardStatus => NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL,
227 Self::Topic => NNS_PROPOSAL_SORT_TOPIC_LABEL,
228 Self::Proposer => NNS_PROPOSAL_SORT_PROPOSER_LABEL,
229 Self::Title => NNS_PROPOSAL_SORT_TITLE_LABEL,
230 Self::Action => NNS_PROPOSAL_SORT_ACTION_LABEL,
231 Self::Yes => NNS_PROPOSAL_SORT_YES_LABEL,
232 Self::No => NNS_PROPOSAL_SORT_NO_LABEL,
233 Self::TotalVotes => NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL,
234 Self::TallyTime => NNS_PROPOSAL_SORT_TALLY_TIME_LABEL,
235 Self::VotingPower => NNS_PROPOSAL_SORT_VOTING_POWER_LABEL,
236 Self::Ballots => NNS_PROPOSAL_SORT_BALLOTS_LABEL,
237 Self::RejectCost => NNS_PROPOSAL_SORT_REJECT_COST_LABEL,
238 Self::RewardRound => NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL,
239 Self::Proposed => NNS_PROPOSAL_SORT_PROPOSED_LABEL,
240 Self::Deadline => NNS_PROPOSAL_SORT_DEADLINE_LABEL,
241 Self::Decided => NNS_PROPOSAL_SORT_DECIDED_LABEL,
242 Self::Executed => NNS_PROPOSAL_SORT_EXECUTED_LABEL,
243 Self::Failed => NNS_PROPOSAL_SORT_FAILED_LABEL,
244 }
245 }
246
247 #[must_use]
249 pub const fn default_direction(self) -> NnsProposalSortDirection {
250 match self {
251 Self::Status
252 | Self::RewardStatus
253 | Self::Topic
254 | Self::Proposer
255 | Self::Title
256 | Self::Action => NnsProposalSortDirection::Asc,
257 _ => NnsProposalSortDirection::Desc,
258 }
259 }
260
261 #[must_use]
263 pub const fn uses_local_direction(self) -> bool {
264 !matches!(self, Self::Api)
265 }
266
267 #[must_use]
269 pub const fn direction_label(self, direction: NnsProposalSortDirection) -> &'static str {
270 match self {
271 Self::Api => NNS_PROPOSAL_SORT_NONE_LABEL,
272 _ => direction.as_str(),
273 }
274 }
275}
276
277#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
284pub enum NnsProposalSortDirection {
285 Asc,
287 #[default]
289 Desc,
290}
291
292impl NnsProposalSortDirection {
293 #[must_use]
295 pub const fn as_str(self) -> &'static str {
296 match self {
297 Self::Asc => NNS_PROPOSAL_SORT_ASC_LABEL,
298 Self::Desc => NNS_PROPOSAL_SORT_DESC_LABEL,
299 }
300 }
301}
302
303#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
310pub enum NnsProposalStatusFilter {
311 #[default]
313 Any,
314 Open,
316 Rejected,
318 Adopted,
320 Executed,
322 Failed,
324}
325
326#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
333pub enum NnsProposalRewardStatusFilter {
334 #[default]
336 Any,
337 AcceptVotes,
339 ReadyToSettle,
341 Settled,
343 Ineligible,
345}
346
347impl NnsProposalRewardStatusFilter {
348 #[must_use]
350 pub const fn as_str(self) -> &'static str {
351 match self.classification() {
352 None => "any",
353 Some(value) => value.as_str(),
354 }
355 }
356
357 #[cfg(any(
358 feature = "nns-host",
359 all(feature = "canister", target_arch = "wasm32")
360 ))]
361 pub(in crate::nns) const fn governance_reward_status_code(self) -> Option<i32> {
362 match self.classification() {
363 None => None,
364 Some(value) => Some(value.code()),
365 }
366 }
367
368 const fn classification(self) -> Option<NnsProposalRewardStatus> {
369 match self {
370 Self::Any => None,
371 Self::AcceptVotes => Some(NnsProposalRewardStatus::AcceptVotes),
372 Self::ReadyToSettle => Some(NnsProposalRewardStatus::ReadyToSettle),
373 Self::Settled => Some(NnsProposalRewardStatus::Settled),
374 Self::Ineligible => Some(NnsProposalRewardStatus::Ineligible),
375 }
376 }
377}
378
379impl NnsProposalStatusFilter {
380 #[must_use]
382 pub const fn as_str(self) -> &'static str {
383 match self.classification() {
384 None => "any",
385 Some(value) => value.as_str(),
386 }
387 }
388
389 #[cfg(any(
390 feature = "nns-host",
391 all(feature = "canister", target_arch = "wasm32")
392 ))]
393 pub(in crate::nns) const fn governance_status_code(self) -> Option<i32> {
394 match self.classification() {
395 None => None,
396 Some(value) => Some(value.code()),
397 }
398 }
399
400 const fn classification(self) -> Option<NnsProposalStatus> {
401 match self {
402 Self::Any => None,
403 Self::Open => Some(NnsProposalStatus::Open),
404 Self::Rejected => Some(NnsProposalStatus::Rejected),
405 Self::Adopted => Some(NnsProposalStatus::Adopted),
406 Self::Executed => Some(NnsProposalStatus::Executed),
407 Self::Failed => Some(NnsProposalStatus::Failed),
408 }
409 }
410}
411
412#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
419pub enum NnsProposalTopicFilter {
420 #[default]
422 Any,
423 NeuronManagement,
425 ExchangeRate,
427 NetworkEconomics,
429 Governance,
431 NodeAdmin,
433 ParticipantManagement,
435 SubnetManagement,
437 NetworkCanisterManagement,
439 Kyc,
441 NodeProviderRewards,
443 IcOsVersionDeployment,
445 IcOsVersionElection,
447 SnsAndCommunityFund,
449 ApiBoundaryNodeManagement,
451 SubnetRental,
453 ApplicationCanisterManagement,
455 ProtocolCanisterManagement,
457}
458
459impl NnsProposalTopicFilter {
460 #[must_use]
462 pub const fn as_str(self) -> &'static str {
463 match self.classification() {
464 None => "any",
465 Some(value) => value.as_str(),
466 }
467 }
468
469 pub(in crate::nns) const fn topic_code(self) -> Option<i32> {
470 match self.classification() {
471 None => None,
472 Some(value) => Some(value.code()),
473 }
474 }
475
476 const fn classification(self) -> Option<NnsProposalTopic> {
477 match self {
478 Self::Any => None,
479 Self::NeuronManagement => Some(NnsProposalTopic::NeuronManagement),
480 Self::ExchangeRate => Some(NnsProposalTopic::ExchangeRate),
481 Self::NetworkEconomics => Some(NnsProposalTopic::NetworkEconomics),
482 Self::Governance => Some(NnsProposalTopic::Governance),
483 Self::NodeAdmin => Some(NnsProposalTopic::NodeAdmin),
484 Self::ParticipantManagement => Some(NnsProposalTopic::ParticipantManagement),
485 Self::SubnetManagement => Some(NnsProposalTopic::SubnetManagement),
486 Self::NetworkCanisterManagement => Some(NnsProposalTopic::NetworkCanisterManagement),
487 Self::Kyc => Some(NnsProposalTopic::Kyc),
488 Self::NodeProviderRewards => Some(NnsProposalTopic::NodeProviderRewards),
489 Self::IcOsVersionDeployment => Some(NnsProposalTopic::IcOsVersionDeployment),
490 Self::IcOsVersionElection => Some(NnsProposalTopic::IcOsVersionElection),
491 Self::SnsAndCommunityFund => Some(NnsProposalTopic::SnsAndCommunityFund),
492 Self::ApiBoundaryNodeManagement => Some(NnsProposalTopic::ApiBoundaryNodeManagement),
493 Self::SubnetRental => Some(NnsProposalTopic::SubnetRental),
494 Self::ApplicationCanisterManagement => {
495 Some(NnsProposalTopic::ApplicationCanisterManagement)
496 }
497 Self::ProtocolCanisterManagement => Some(NnsProposalTopic::ProtocolCanisterManagement),
498 }
499 }
500}
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505
506 #[test]
507 fn proposal_status_codes_and_labels_round_trip() {
508 for (status, code, label) in [
509 (NnsProposalStatus::Unspecified, 0, "unspecified"),
510 (NnsProposalStatus::Open, 1, "open"),
511 (NnsProposalStatus::Rejected, 2, "rejected"),
512 (NnsProposalStatus::Adopted, 3, "adopted"),
513 (NnsProposalStatus::Executed, 4, "executed"),
514 (NnsProposalStatus::Failed, 5, "failed"),
515 ] {
516 assert_eq!(status.code(), code);
517 assert_eq!(NnsProposalStatus::from_code(code), status);
518 assert_json_label(status, label);
519 }
520 assert_eq!(
521 NnsProposalStatus::from_code(99),
522 NnsProposalStatus::Unspecified
523 );
524 }
525
526 #[test]
527 fn proposal_reward_status_codes_and_labels_round_trip() {
528 for (status, code, label) in [
529 (NnsProposalRewardStatus::Unspecified, 0, "unspecified"),
530 (NnsProposalRewardStatus::AcceptVotes, 1, "accept-votes"),
531 (NnsProposalRewardStatus::ReadyToSettle, 2, "ready-to-settle"),
532 (NnsProposalRewardStatus::Settled, 3, "settled"),
533 (NnsProposalRewardStatus::Ineligible, 4, "ineligible"),
534 ] {
535 assert_eq!(status.code(), code);
536 assert_eq!(NnsProposalRewardStatus::from_code(code), status);
537 assert_json_label(status, label);
538 }
539 assert_eq!(
540 NnsProposalRewardStatus::from_code(99),
541 NnsProposalRewardStatus::Unspecified
542 );
543 }
544
545 #[test]
546 fn proposal_topic_codes_and_labels_round_trip() {
547 for (topic, code, label) in [
548 (NnsProposalTopic::Unspecified, 0, "unspecified"),
549 (NnsProposalTopic::NeuronManagement, 1, "neuron-management"),
550 (NnsProposalTopic::ExchangeRate, 2, "exchange-rate"),
551 (NnsProposalTopic::NetworkEconomics, 3, "network-economics"),
552 (NnsProposalTopic::Governance, 4, "governance"),
553 (NnsProposalTopic::NodeAdmin, 5, "node-admin"),
554 (
555 NnsProposalTopic::ParticipantManagement,
556 6,
557 "participant-management",
558 ),
559 (NnsProposalTopic::SubnetManagement, 7, "subnet-management"),
560 (
561 NnsProposalTopic::NetworkCanisterManagement,
562 8,
563 "network-canister-management",
564 ),
565 (NnsProposalTopic::Kyc, 9, "kyc"),
566 (
567 NnsProposalTopic::NodeProviderRewards,
568 10,
569 "node-provider-rewards",
570 ),
571 (
572 NnsProposalTopic::IcOsVersionDeployment,
573 12,
574 "ic-os-version-deployment",
575 ),
576 (
577 NnsProposalTopic::IcOsVersionElection,
578 13,
579 "ic-os-version-election",
580 ),
581 (
582 NnsProposalTopic::SnsAndCommunityFund,
583 14,
584 "sns-and-community-fund",
585 ),
586 (
587 NnsProposalTopic::ApiBoundaryNodeManagement,
588 15,
589 "api-boundary-node-management",
590 ),
591 (NnsProposalTopic::SubnetRental, 16, "subnet-rental"),
592 (
593 NnsProposalTopic::ApplicationCanisterManagement,
594 17,
595 "application-canister-management",
596 ),
597 (
598 NnsProposalTopic::ProtocolCanisterManagement,
599 18,
600 "protocol-canister-management",
601 ),
602 ] {
603 assert_eq!(topic.code(), code);
604 assert_eq!(NnsProposalTopic::from_code(code), topic);
605 assert_json_label(topic, label);
606 }
607 assert_eq!(
608 NnsProposalTopic::from_code(11),
609 NnsProposalTopic::Unspecified
610 );
611 assert_eq!(
612 NnsProposalTopic::from_code(99),
613 NnsProposalTopic::Unspecified
614 );
615 }
616
617 fn assert_json_label<T>(value: T, label: &str)
618 where
619 T: Copy + std::fmt::Debug + Eq + Serialize + serde::de::DeserializeOwned,
620 {
621 assert_eq!(
622 serde_json::to_string(&value).unwrap(),
623 format!("\"{label}\"")
624 );
625 assert_eq!(
626 serde_json::from_str::<T>(&format!("\"{label}\"")).unwrap(),
627 value
628 );
629 }
630}