1use serde::{Deserialize, Serialize};
8
9pub(in crate::nns) const NNS_PROPOSAL_SORT_API_LABEL: &str = "api";
10pub(in crate::nns) const NNS_PROPOSAL_SORT_ID_LABEL: &str = "id";
11pub(in crate::nns) const NNS_PROPOSAL_SORT_STATUS_LABEL: &str = "status";
12pub(in crate::nns) const NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL: &str = "reward-status";
13pub(in crate::nns) const NNS_PROPOSAL_SORT_TOPIC_LABEL: &str = "topic";
14pub(in crate::nns) const NNS_PROPOSAL_SORT_PROPOSER_LABEL: &str = "proposer";
15pub(in crate::nns) const NNS_PROPOSAL_SORT_TITLE_LABEL: &str = "title";
16pub(in crate::nns) const NNS_PROPOSAL_SORT_ACTION_LABEL: &str = "action";
17pub(in crate::nns) const NNS_PROPOSAL_SORT_YES_LABEL: &str = "yes";
18pub(in crate::nns) const NNS_PROPOSAL_SORT_NO_LABEL: &str = "no";
19pub(in crate::nns) const NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL: &str = "total-votes";
20pub(in crate::nns) const NNS_PROPOSAL_SORT_TALLY_TIME_LABEL: &str = "tally-time";
21pub(in crate::nns) const NNS_PROPOSAL_SORT_VOTING_POWER_LABEL: &str = "voting-power";
22pub(in crate::nns) const NNS_PROPOSAL_SORT_BALLOTS_LABEL: &str = "ballots";
23pub(in crate::nns) const NNS_PROPOSAL_SORT_REJECT_COST_LABEL: &str = "reject-cost";
24pub(in crate::nns) const NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL: &str = "reward-round";
25pub(in crate::nns) const NNS_PROPOSAL_SORT_PROPOSED_LABEL: &str = "proposed";
26pub(in crate::nns) const NNS_PROPOSAL_SORT_DEADLINE_LABEL: &str = "deadline";
27pub(in crate::nns) const NNS_PROPOSAL_SORT_DECIDED_LABEL: &str = "decided";
28pub(in crate::nns) const NNS_PROPOSAL_SORT_EXECUTED_LABEL: &str = "executed";
29pub(in crate::nns) const NNS_PROPOSAL_SORT_FAILED_LABEL: &str = "failed";
30pub(in crate::nns) const NNS_PROPOSAL_SORT_ASC_LABEL: &str = "asc";
31pub(in crate::nns) const NNS_PROPOSAL_SORT_DESC_LABEL: &str = "desc";
32pub(in crate::nns) const NNS_PROPOSAL_SORT_NONE_LABEL: &str = "none";
33
34#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
41#[serde(rename_all = "kebab-case")]
42pub enum NnsProposalStatus {
43 Unspecified,
45 Open,
47 Rejected,
49 Adopted,
51 Executed,
53 Failed,
55}
56
57impl NnsProposalStatus {
58 #[must_use]
60 pub const fn from_code(code: i32) -> Self {
61 match code {
62 1 => Self::Open,
63 2 => Self::Rejected,
64 3 => Self::Adopted,
65 4 => Self::Executed,
66 5 => Self::Failed,
67 _ => Self::Unspecified,
68 }
69 }
70
71 #[must_use]
73 pub const fn code(self) -> i32 {
74 match self {
75 Self::Unspecified => 0,
76 Self::Open => 1,
77 Self::Rejected => 2,
78 Self::Adopted => 3,
79 Self::Executed => 4,
80 Self::Failed => 5,
81 }
82 }
83
84 #[must_use]
86 pub const fn as_str(self) -> &'static str {
87 match self {
88 Self::Unspecified => "unspecified",
89 Self::Open => "open",
90 Self::Rejected => "rejected",
91 Self::Adopted => "adopted",
92 Self::Executed => "executed",
93 Self::Failed => "failed",
94 }
95 }
96}
97
98#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
105#[serde(rename_all = "kebab-case")]
106pub enum NnsProposalRewardStatus {
107 Unspecified,
109 AcceptVotes,
111 ReadyToSettle,
113 Settled,
115 Ineligible,
117}
118
119impl NnsProposalRewardStatus {
120 #[must_use]
122 pub const fn from_code(code: i32) -> Self {
123 match code {
124 1 => Self::AcceptVotes,
125 2 => Self::ReadyToSettle,
126 3 => Self::Settled,
127 4 => Self::Ineligible,
128 _ => Self::Unspecified,
129 }
130 }
131
132 #[must_use]
134 pub const fn code(self) -> i32 {
135 match self {
136 Self::Unspecified => 0,
137 Self::AcceptVotes => 1,
138 Self::ReadyToSettle => 2,
139 Self::Settled => 3,
140 Self::Ineligible => 4,
141 }
142 }
143
144 #[must_use]
146 pub const fn as_str(self) -> &'static str {
147 match self {
148 Self::Unspecified => "unspecified",
149 Self::AcceptVotes => "accept-votes",
150 Self::ReadyToSettle => "ready-to-settle",
151 Self::Settled => "settled",
152 Self::Ineligible => "ineligible",
153 }
154 }
155}
156
157#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
164#[serde(rename_all = "kebab-case")]
165pub enum NnsProposalVote {
166 Unspecified,
168 Yes,
170 No,
172}
173
174impl NnsProposalVote {
175 #[must_use]
177 pub const fn from_code(code: i32) -> Self {
178 match code {
179 1 => Self::Yes,
180 2 => Self::No,
181 _ => Self::Unspecified,
182 }
183 }
184
185 #[must_use]
187 pub const fn code(self) -> i32 {
188 match self {
189 Self::Unspecified => 0,
190 Self::Yes => 1,
191 Self::No => 2,
192 }
193 }
194
195 #[must_use]
197 pub const fn as_str(self) -> &'static str {
198 match self {
199 Self::Unspecified => "unspecified",
200 Self::Yes => "yes",
201 Self::No => "no",
202 }
203 }
204}
205
206#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
213#[serde(rename_all = "kebab-case")]
214pub enum NnsProposalTopic {
215 Unspecified,
217 NeuronManagement,
219 ExchangeRate,
221 NetworkEconomics,
223 Governance,
225 NodeAdmin,
227 ParticipantManagement,
229 SubnetManagement,
231 NetworkCanisterManagement,
233 Kyc,
235 NodeProviderRewards,
237 IcOsVersionDeployment,
239 IcOsVersionElection,
241 SnsAndCommunityFund,
243 ApiBoundaryNodeManagement,
245 SubnetRental,
247 ApplicationCanisterManagement,
249 ProtocolCanisterManagement,
251}
252
253impl NnsProposalTopic {
254 #[must_use]
256 pub const fn from_code(code: i32) -> Self {
257 match code {
258 1 => Self::NeuronManagement,
259 2 => Self::ExchangeRate,
260 3 => Self::NetworkEconomics,
261 4 => Self::Governance,
262 5 => Self::NodeAdmin,
263 6 => Self::ParticipantManagement,
264 7 => Self::SubnetManagement,
265 8 => Self::NetworkCanisterManagement,
266 9 => Self::Kyc,
267 10 => Self::NodeProviderRewards,
268 12 => Self::IcOsVersionDeployment,
269 13 => Self::IcOsVersionElection,
270 14 => Self::SnsAndCommunityFund,
271 15 => Self::ApiBoundaryNodeManagement,
272 16 => Self::SubnetRental,
273 17 => Self::ApplicationCanisterManagement,
274 18 => Self::ProtocolCanisterManagement,
275 _ => Self::Unspecified,
276 }
277 }
278
279 #[must_use]
281 pub const fn code(self) -> i32 {
282 match self {
283 Self::Unspecified => 0,
284 Self::NeuronManagement => 1,
285 Self::ExchangeRate => 2,
286 Self::NetworkEconomics => 3,
287 Self::Governance => 4,
288 Self::NodeAdmin => 5,
289 Self::ParticipantManagement => 6,
290 Self::SubnetManagement => 7,
291 Self::NetworkCanisterManagement => 8,
292 Self::Kyc => 9,
293 Self::NodeProviderRewards => 10,
294 Self::IcOsVersionDeployment => 12,
295 Self::IcOsVersionElection => 13,
296 Self::SnsAndCommunityFund => 14,
297 Self::ApiBoundaryNodeManagement => 15,
298 Self::SubnetRental => 16,
299 Self::ApplicationCanisterManagement => 17,
300 Self::ProtocolCanisterManagement => 18,
301 }
302 }
303
304 #[must_use]
306 pub const fn as_str(self) -> &'static str {
307 match self {
308 Self::Unspecified => "unspecified",
309 Self::NeuronManagement => "neuron-management",
310 Self::ExchangeRate => "exchange-rate",
311 Self::NetworkEconomics => "network-economics",
312 Self::Governance => "governance",
313 Self::NodeAdmin => "node-admin",
314 Self::ParticipantManagement => "participant-management",
315 Self::SubnetManagement => "subnet-management",
316 Self::NetworkCanisterManagement => "network-canister-management",
317 Self::Kyc => "kyc",
318 Self::NodeProviderRewards => "node-provider-rewards",
319 Self::IcOsVersionDeployment => "ic-os-version-deployment",
320 Self::IcOsVersionElection => "ic-os-version-election",
321 Self::SnsAndCommunityFund => "sns-and-community-fund",
322 Self::ApiBoundaryNodeManagement => "api-boundary-node-management",
323 Self::SubnetRental => "subnet-rental",
324 Self::ApplicationCanisterManagement => "application-canister-management",
325 Self::ProtocolCanisterManagement => "protocol-canister-management",
326 }
327 }
328}
329
330#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
337pub enum NnsProposalListSort {
338 #[default]
340 Api,
341 Id,
343 Status,
345 RewardStatus,
347 Topic,
349 Proposer,
351 Title,
353 Action,
355 Yes,
357 No,
359 TotalVotes,
361 TallyTime,
363 VotingPower,
365 Ballots,
367 RejectCost,
369 RewardRound,
371 Proposed,
373 Deadline,
375 Decided,
377 Executed,
379 Failed,
381}
382
383impl NnsProposalListSort {
384 #[must_use]
386 pub const fn as_str(self) -> &'static str {
387 match self {
388 Self::Api => NNS_PROPOSAL_SORT_API_LABEL,
389 Self::Id => NNS_PROPOSAL_SORT_ID_LABEL,
390 Self::Status => NNS_PROPOSAL_SORT_STATUS_LABEL,
391 Self::RewardStatus => NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL,
392 Self::Topic => NNS_PROPOSAL_SORT_TOPIC_LABEL,
393 Self::Proposer => NNS_PROPOSAL_SORT_PROPOSER_LABEL,
394 Self::Title => NNS_PROPOSAL_SORT_TITLE_LABEL,
395 Self::Action => NNS_PROPOSAL_SORT_ACTION_LABEL,
396 Self::Yes => NNS_PROPOSAL_SORT_YES_LABEL,
397 Self::No => NNS_PROPOSAL_SORT_NO_LABEL,
398 Self::TotalVotes => NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL,
399 Self::TallyTime => NNS_PROPOSAL_SORT_TALLY_TIME_LABEL,
400 Self::VotingPower => NNS_PROPOSAL_SORT_VOTING_POWER_LABEL,
401 Self::Ballots => NNS_PROPOSAL_SORT_BALLOTS_LABEL,
402 Self::RejectCost => NNS_PROPOSAL_SORT_REJECT_COST_LABEL,
403 Self::RewardRound => NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL,
404 Self::Proposed => NNS_PROPOSAL_SORT_PROPOSED_LABEL,
405 Self::Deadline => NNS_PROPOSAL_SORT_DEADLINE_LABEL,
406 Self::Decided => NNS_PROPOSAL_SORT_DECIDED_LABEL,
407 Self::Executed => NNS_PROPOSAL_SORT_EXECUTED_LABEL,
408 Self::Failed => NNS_PROPOSAL_SORT_FAILED_LABEL,
409 }
410 }
411
412 #[must_use]
414 pub const fn default_direction(self) -> NnsProposalSortDirection {
415 match self {
416 Self::Status
417 | Self::RewardStatus
418 | Self::Topic
419 | Self::Proposer
420 | Self::Title
421 | Self::Action => NnsProposalSortDirection::Asc,
422 _ => NnsProposalSortDirection::Desc,
423 }
424 }
425
426 #[must_use]
428 pub const fn uses_local_direction(self) -> bool {
429 !matches!(self, Self::Api)
430 }
431
432 #[must_use]
434 pub const fn direction_label(self, direction: NnsProposalSortDirection) -> &'static str {
435 match self {
436 Self::Api => NNS_PROPOSAL_SORT_NONE_LABEL,
437 _ => direction.as_str(),
438 }
439 }
440}
441
442#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
449pub enum NnsProposalSortDirection {
450 Asc,
452 #[default]
454 Desc,
455}
456
457impl NnsProposalSortDirection {
458 #[must_use]
460 pub const fn as_str(self) -> &'static str {
461 match self {
462 Self::Asc => NNS_PROPOSAL_SORT_ASC_LABEL,
463 Self::Desc => NNS_PROPOSAL_SORT_DESC_LABEL,
464 }
465 }
466}
467
468#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
475pub enum NnsProposalStatusFilter {
476 #[default]
478 Any,
479 Open,
481 Rejected,
483 Adopted,
485 Executed,
487 Failed,
489}
490
491#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
498pub enum NnsProposalRewardStatusFilter {
499 #[default]
501 Any,
502 AcceptVotes,
504 ReadyToSettle,
506 Settled,
508 Ineligible,
510}
511
512impl NnsProposalRewardStatusFilter {
513 #[must_use]
515 pub const fn as_str(self) -> &'static str {
516 match self {
517 Self::Any => "any",
518 Self::AcceptVotes => NnsProposalRewardStatus::AcceptVotes.as_str(),
519 Self::ReadyToSettle => NnsProposalRewardStatus::ReadyToSettle.as_str(),
520 Self::Settled => NnsProposalRewardStatus::Settled.as_str(),
521 Self::Ineligible => NnsProposalRewardStatus::Ineligible.as_str(),
522 }
523 }
524
525 #[cfg(any(
526 feature = "nns-host",
527 all(feature = "canister", target_arch = "wasm32")
528 ))]
529 pub(in crate::nns) const fn governance_reward_status_code(self) -> Option<i32> {
530 match self {
531 Self::Any => None,
532 Self::AcceptVotes => Some(NnsProposalRewardStatus::AcceptVotes.code()),
533 Self::ReadyToSettle => Some(NnsProposalRewardStatus::ReadyToSettle.code()),
534 Self::Settled => Some(NnsProposalRewardStatus::Settled.code()),
535 Self::Ineligible => Some(NnsProposalRewardStatus::Ineligible.code()),
536 }
537 }
538}
539
540impl NnsProposalStatusFilter {
541 #[must_use]
543 pub const fn as_str(self) -> &'static str {
544 match self {
545 Self::Any => "any",
546 Self::Open => NnsProposalStatus::Open.as_str(),
547 Self::Rejected => NnsProposalStatus::Rejected.as_str(),
548 Self::Adopted => NnsProposalStatus::Adopted.as_str(),
549 Self::Executed => NnsProposalStatus::Executed.as_str(),
550 Self::Failed => NnsProposalStatus::Failed.as_str(),
551 }
552 }
553
554 #[cfg(any(
555 feature = "nns-host",
556 all(feature = "canister", target_arch = "wasm32")
557 ))]
558 pub(in crate::nns) const fn governance_status_code(self) -> Option<i32> {
559 match self {
560 Self::Any => None,
561 Self::Open => Some(NnsProposalStatus::Open.code()),
562 Self::Rejected => Some(NnsProposalStatus::Rejected.code()),
563 Self::Adopted => Some(NnsProposalStatus::Adopted.code()),
564 Self::Executed => Some(NnsProposalStatus::Executed.code()),
565 Self::Failed => Some(NnsProposalStatus::Failed.code()),
566 }
567 }
568}
569
570#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
577pub enum NnsProposalTopicFilter {
578 #[default]
580 Any,
581 NeuronManagement,
583 ExchangeRate,
585 NetworkEconomics,
587 Governance,
589 NodeAdmin,
591 ParticipantManagement,
593 SubnetManagement,
595 NetworkCanisterManagement,
597 Kyc,
599 NodeProviderRewards,
601 IcOsVersionDeployment,
603 IcOsVersionElection,
605 SnsAndCommunityFund,
607 ApiBoundaryNodeManagement,
609 SubnetRental,
611 ApplicationCanisterManagement,
613 ProtocolCanisterManagement,
615}
616
617impl NnsProposalTopicFilter {
618 #[must_use]
620 pub const fn as_str(self) -> &'static str {
621 match self {
622 Self::Any => "any",
623 Self::NeuronManagement => NnsProposalTopic::NeuronManagement.as_str(),
624 Self::ExchangeRate => NnsProposalTopic::ExchangeRate.as_str(),
625 Self::NetworkEconomics => NnsProposalTopic::NetworkEconomics.as_str(),
626 Self::Governance => NnsProposalTopic::Governance.as_str(),
627 Self::NodeAdmin => NnsProposalTopic::NodeAdmin.as_str(),
628 Self::ParticipantManagement => NnsProposalTopic::ParticipantManagement.as_str(),
629 Self::SubnetManagement => NnsProposalTopic::SubnetManagement.as_str(),
630 Self::NetworkCanisterManagement => NnsProposalTopic::NetworkCanisterManagement.as_str(),
631 Self::Kyc => NnsProposalTopic::Kyc.as_str(),
632 Self::NodeProviderRewards => NnsProposalTopic::NodeProviderRewards.as_str(),
633 Self::IcOsVersionDeployment => NnsProposalTopic::IcOsVersionDeployment.as_str(),
634 Self::IcOsVersionElection => NnsProposalTopic::IcOsVersionElection.as_str(),
635 Self::SnsAndCommunityFund => NnsProposalTopic::SnsAndCommunityFund.as_str(),
636 Self::ApiBoundaryNodeManagement => NnsProposalTopic::ApiBoundaryNodeManagement.as_str(),
637 Self::SubnetRental => NnsProposalTopic::SubnetRental.as_str(),
638 Self::ApplicationCanisterManagement => {
639 NnsProposalTopic::ApplicationCanisterManagement.as_str()
640 }
641 Self::ProtocolCanisterManagement => {
642 NnsProposalTopic::ProtocolCanisterManagement.as_str()
643 }
644 }
645 }
646
647 pub(in crate::nns) const fn topic_code(self) -> Option<i32> {
648 match self {
649 Self::Any => None,
650 Self::NeuronManagement => Some(NnsProposalTopic::NeuronManagement.code()),
651 Self::ExchangeRate => Some(NnsProposalTopic::ExchangeRate.code()),
652 Self::NetworkEconomics => Some(NnsProposalTopic::NetworkEconomics.code()),
653 Self::Governance => Some(NnsProposalTopic::Governance.code()),
654 Self::NodeAdmin => Some(NnsProposalTopic::NodeAdmin.code()),
655 Self::ParticipantManagement => Some(NnsProposalTopic::ParticipantManagement.code()),
656 Self::SubnetManagement => Some(NnsProposalTopic::SubnetManagement.code()),
657 Self::NetworkCanisterManagement => {
658 Some(NnsProposalTopic::NetworkCanisterManagement.code())
659 }
660 Self::Kyc => Some(NnsProposalTopic::Kyc.code()),
661 Self::NodeProviderRewards => Some(NnsProposalTopic::NodeProviderRewards.code()),
662 Self::IcOsVersionDeployment => Some(NnsProposalTopic::IcOsVersionDeployment.code()),
663 Self::IcOsVersionElection => Some(NnsProposalTopic::IcOsVersionElection.code()),
664 Self::SnsAndCommunityFund => Some(NnsProposalTopic::SnsAndCommunityFund.code()),
665 Self::ApiBoundaryNodeManagement => {
666 Some(NnsProposalTopic::ApiBoundaryNodeManagement.code())
667 }
668 Self::SubnetRental => Some(NnsProposalTopic::SubnetRental.code()),
669 Self::ApplicationCanisterManagement => {
670 Some(NnsProposalTopic::ApplicationCanisterManagement.code())
671 }
672 Self::ProtocolCanisterManagement => {
673 Some(NnsProposalTopic::ProtocolCanisterManagement.code())
674 }
675 }
676 }
677}
678
679#[cfg(test)]
680mod tests {
681 use super::*;
682
683 #[test]
684 fn proposal_status_codes_and_labels_round_trip() {
685 for (status, code, label) in [
686 (NnsProposalStatus::Unspecified, 0, "unspecified"),
687 (NnsProposalStatus::Open, 1, "open"),
688 (NnsProposalStatus::Rejected, 2, "rejected"),
689 (NnsProposalStatus::Adopted, 3, "adopted"),
690 (NnsProposalStatus::Executed, 4, "executed"),
691 (NnsProposalStatus::Failed, 5, "failed"),
692 ] {
693 assert_eq!(status.code(), code);
694 assert_eq!(NnsProposalStatus::from_code(code), status);
695 assert_json_label(status, label);
696 }
697 assert_eq!(
698 NnsProposalStatus::from_code(99),
699 NnsProposalStatus::Unspecified
700 );
701 }
702
703 #[test]
704 fn proposal_reward_status_codes_and_labels_round_trip() {
705 for (status, code, label) in [
706 (NnsProposalRewardStatus::Unspecified, 0, "unspecified"),
707 (NnsProposalRewardStatus::AcceptVotes, 1, "accept-votes"),
708 (NnsProposalRewardStatus::ReadyToSettle, 2, "ready-to-settle"),
709 (NnsProposalRewardStatus::Settled, 3, "settled"),
710 (NnsProposalRewardStatus::Ineligible, 4, "ineligible"),
711 ] {
712 assert_eq!(status.code(), code);
713 assert_eq!(NnsProposalRewardStatus::from_code(code), status);
714 assert_json_label(status, label);
715 }
716 assert_eq!(
717 NnsProposalRewardStatus::from_code(99),
718 NnsProposalRewardStatus::Unspecified
719 );
720 }
721
722 #[test]
723 fn proposal_vote_codes_and_labels_round_trip() {
724 for (vote, code, label) in [
725 (NnsProposalVote::Unspecified, 0, "unspecified"),
726 (NnsProposalVote::Yes, 1, "yes"),
727 (NnsProposalVote::No, 2, "no"),
728 ] {
729 assert_eq!(vote.code(), code);
730 assert_eq!(NnsProposalVote::from_code(code), vote);
731 assert_json_label(vote, label);
732 }
733 assert_eq!(NnsProposalVote::from_code(99), NnsProposalVote::Unspecified);
734 }
735
736 #[test]
737 fn proposal_topic_codes_and_labels_round_trip() {
738 for (topic, code, label) in [
739 (NnsProposalTopic::Unspecified, 0, "unspecified"),
740 (NnsProposalTopic::NeuronManagement, 1, "neuron-management"),
741 (NnsProposalTopic::ExchangeRate, 2, "exchange-rate"),
742 (NnsProposalTopic::NetworkEconomics, 3, "network-economics"),
743 (NnsProposalTopic::Governance, 4, "governance"),
744 (NnsProposalTopic::NodeAdmin, 5, "node-admin"),
745 (
746 NnsProposalTopic::ParticipantManagement,
747 6,
748 "participant-management",
749 ),
750 (NnsProposalTopic::SubnetManagement, 7, "subnet-management"),
751 (
752 NnsProposalTopic::NetworkCanisterManagement,
753 8,
754 "network-canister-management",
755 ),
756 (NnsProposalTopic::Kyc, 9, "kyc"),
757 (
758 NnsProposalTopic::NodeProviderRewards,
759 10,
760 "node-provider-rewards",
761 ),
762 (
763 NnsProposalTopic::IcOsVersionDeployment,
764 12,
765 "ic-os-version-deployment",
766 ),
767 (
768 NnsProposalTopic::IcOsVersionElection,
769 13,
770 "ic-os-version-election",
771 ),
772 (
773 NnsProposalTopic::SnsAndCommunityFund,
774 14,
775 "sns-and-community-fund",
776 ),
777 (
778 NnsProposalTopic::ApiBoundaryNodeManagement,
779 15,
780 "api-boundary-node-management",
781 ),
782 (NnsProposalTopic::SubnetRental, 16, "subnet-rental"),
783 (
784 NnsProposalTopic::ApplicationCanisterManagement,
785 17,
786 "application-canister-management",
787 ),
788 (
789 NnsProposalTopic::ProtocolCanisterManagement,
790 18,
791 "protocol-canister-management",
792 ),
793 ] {
794 assert_eq!(topic.code(), code);
795 assert_eq!(NnsProposalTopic::from_code(code), topic);
796 assert_json_label(topic, label);
797 }
798 assert_eq!(
799 NnsProposalTopic::from_code(11),
800 NnsProposalTopic::Unspecified
801 );
802 assert_eq!(
803 NnsProposalTopic::from_code(99),
804 NnsProposalTopic::Unspecified
805 );
806 }
807
808 fn assert_json_label<T>(value: T, label: &str)
809 where
810 T: Copy + std::fmt::Debug + Eq + Serialize + serde::de::DeserializeOwned,
811 {
812 assert_eq!(
813 serde_json::to_string(&value).unwrap(),
814 format!("\"{label}\"")
815 );
816 assert_eq!(
817 serde_json::from_str::<T>(&format!("\"{label}\"")).unwrap(),
818 value
819 );
820 }
821}