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]
339 Api,
340 Id,
341 Status,
342 RewardStatus,
343 Topic,
344 Proposer,
345 Title,
346 Action,
347 Yes,
348 No,
349 TotalVotes,
350 TallyTime,
351 VotingPower,
352 Ballots,
353 RejectCost,
354 RewardRound,
355 Proposed,
356 Deadline,
357 Decided,
358 Executed,
359 Failed,
360}
361
362impl NnsProposalListSort {
363 #[must_use]
364 pub const fn as_str(self) -> &'static str {
365 match self {
366 Self::Api => NNS_PROPOSAL_SORT_API_LABEL,
367 Self::Id => NNS_PROPOSAL_SORT_ID_LABEL,
368 Self::Status => NNS_PROPOSAL_SORT_STATUS_LABEL,
369 Self::RewardStatus => NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL,
370 Self::Topic => NNS_PROPOSAL_SORT_TOPIC_LABEL,
371 Self::Proposer => NNS_PROPOSAL_SORT_PROPOSER_LABEL,
372 Self::Title => NNS_PROPOSAL_SORT_TITLE_LABEL,
373 Self::Action => NNS_PROPOSAL_SORT_ACTION_LABEL,
374 Self::Yes => NNS_PROPOSAL_SORT_YES_LABEL,
375 Self::No => NNS_PROPOSAL_SORT_NO_LABEL,
376 Self::TotalVotes => NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL,
377 Self::TallyTime => NNS_PROPOSAL_SORT_TALLY_TIME_LABEL,
378 Self::VotingPower => NNS_PROPOSAL_SORT_VOTING_POWER_LABEL,
379 Self::Ballots => NNS_PROPOSAL_SORT_BALLOTS_LABEL,
380 Self::RejectCost => NNS_PROPOSAL_SORT_REJECT_COST_LABEL,
381 Self::RewardRound => NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL,
382 Self::Proposed => NNS_PROPOSAL_SORT_PROPOSED_LABEL,
383 Self::Deadline => NNS_PROPOSAL_SORT_DEADLINE_LABEL,
384 Self::Decided => NNS_PROPOSAL_SORT_DECIDED_LABEL,
385 Self::Executed => NNS_PROPOSAL_SORT_EXECUTED_LABEL,
386 Self::Failed => NNS_PROPOSAL_SORT_FAILED_LABEL,
387 }
388 }
389
390 #[must_use]
391 pub const fn default_direction(self) -> NnsProposalSortDirection {
392 match self {
393 Self::Status
394 | Self::RewardStatus
395 | Self::Topic
396 | Self::Proposer
397 | Self::Title
398 | Self::Action => NnsProposalSortDirection::Asc,
399 _ => NnsProposalSortDirection::Desc,
400 }
401 }
402
403 #[must_use]
404 pub const fn uses_local_direction(self) -> bool {
405 !matches!(self, Self::Api)
406 }
407
408 #[must_use]
409 pub const fn direction_label(self, direction: NnsProposalSortDirection) -> &'static str {
410 match self {
411 Self::Api => NNS_PROPOSAL_SORT_NONE_LABEL,
412 _ => direction.as_str(),
413 }
414 }
415}
416
417#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
424pub enum NnsProposalSortDirection {
425 Asc,
426 #[default]
427 Desc,
428}
429
430impl NnsProposalSortDirection {
431 #[must_use]
432 pub const fn as_str(self) -> &'static str {
433 match self {
434 Self::Asc => NNS_PROPOSAL_SORT_ASC_LABEL,
435 Self::Desc => NNS_PROPOSAL_SORT_DESC_LABEL,
436 }
437 }
438}
439
440#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
447pub enum NnsProposalStatusFilter {
448 #[default]
449 Any,
450 Open,
451 Rejected,
452 Adopted,
453 Executed,
454 Failed,
455}
456
457#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
464pub enum NnsProposalRewardStatusFilter {
465 #[default]
466 Any,
467 AcceptVotes,
468 ReadyToSettle,
469 Settled,
470 Ineligible,
471}
472
473impl NnsProposalRewardStatusFilter {
474 #[must_use]
475 pub const fn as_str(self) -> &'static str {
476 match self {
477 Self::Any => "any",
478 Self::AcceptVotes => NnsProposalRewardStatus::AcceptVotes.as_str(),
479 Self::ReadyToSettle => NnsProposalRewardStatus::ReadyToSettle.as_str(),
480 Self::Settled => NnsProposalRewardStatus::Settled.as_str(),
481 Self::Ineligible => NnsProposalRewardStatus::Ineligible.as_str(),
482 }
483 }
484
485 #[cfg(any(
486 feature = "nns-host",
487 all(feature = "canister", target_arch = "wasm32"),
488 test
489 ))]
490 pub(in crate::nns) const fn governance_reward_status_code(self) -> Option<i32> {
491 match self {
492 Self::Any => None,
493 Self::AcceptVotes => Some(NnsProposalRewardStatus::AcceptVotes.code()),
494 Self::ReadyToSettle => Some(NnsProposalRewardStatus::ReadyToSettle.code()),
495 Self::Settled => Some(NnsProposalRewardStatus::Settled.code()),
496 Self::Ineligible => Some(NnsProposalRewardStatus::Ineligible.code()),
497 }
498 }
499}
500
501impl NnsProposalStatusFilter {
502 #[must_use]
503 pub const fn as_str(self) -> &'static str {
504 match self {
505 Self::Any => "any",
506 Self::Open => NnsProposalStatus::Open.as_str(),
507 Self::Rejected => NnsProposalStatus::Rejected.as_str(),
508 Self::Adopted => NnsProposalStatus::Adopted.as_str(),
509 Self::Executed => NnsProposalStatus::Executed.as_str(),
510 Self::Failed => NnsProposalStatus::Failed.as_str(),
511 }
512 }
513
514 #[cfg(any(
515 feature = "nns-host",
516 all(feature = "canister", target_arch = "wasm32"),
517 test
518 ))]
519 pub(in crate::nns) const fn governance_status_code(self) -> Option<i32> {
520 match self {
521 Self::Any => None,
522 Self::Open => Some(NnsProposalStatus::Open.code()),
523 Self::Rejected => Some(NnsProposalStatus::Rejected.code()),
524 Self::Adopted => Some(NnsProposalStatus::Adopted.code()),
525 Self::Executed => Some(NnsProposalStatus::Executed.code()),
526 Self::Failed => Some(NnsProposalStatus::Failed.code()),
527 }
528 }
529}
530
531#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
538pub enum NnsProposalTopicFilter {
539 #[default]
540 Any,
541 NeuronManagement,
542 ExchangeRate,
543 NetworkEconomics,
544 Governance,
545 NodeAdmin,
546 ParticipantManagement,
547 SubnetManagement,
548 NetworkCanisterManagement,
549 Kyc,
550 NodeProviderRewards,
551 IcOsVersionDeployment,
552 IcOsVersionElection,
553 SnsAndCommunityFund,
554 ApiBoundaryNodeManagement,
555 SubnetRental,
556 ApplicationCanisterManagement,
557 ProtocolCanisterManagement,
558}
559
560impl NnsProposalTopicFilter {
561 #[must_use]
562 pub const fn as_str(self) -> &'static str {
563 match self {
564 Self::Any => "any",
565 Self::NeuronManagement => NnsProposalTopic::NeuronManagement.as_str(),
566 Self::ExchangeRate => NnsProposalTopic::ExchangeRate.as_str(),
567 Self::NetworkEconomics => NnsProposalTopic::NetworkEconomics.as_str(),
568 Self::Governance => NnsProposalTopic::Governance.as_str(),
569 Self::NodeAdmin => NnsProposalTopic::NodeAdmin.as_str(),
570 Self::ParticipantManagement => NnsProposalTopic::ParticipantManagement.as_str(),
571 Self::SubnetManagement => NnsProposalTopic::SubnetManagement.as_str(),
572 Self::NetworkCanisterManagement => NnsProposalTopic::NetworkCanisterManagement.as_str(),
573 Self::Kyc => NnsProposalTopic::Kyc.as_str(),
574 Self::NodeProviderRewards => NnsProposalTopic::NodeProviderRewards.as_str(),
575 Self::IcOsVersionDeployment => NnsProposalTopic::IcOsVersionDeployment.as_str(),
576 Self::IcOsVersionElection => NnsProposalTopic::IcOsVersionElection.as_str(),
577 Self::SnsAndCommunityFund => NnsProposalTopic::SnsAndCommunityFund.as_str(),
578 Self::ApiBoundaryNodeManagement => NnsProposalTopic::ApiBoundaryNodeManagement.as_str(),
579 Self::SubnetRental => NnsProposalTopic::SubnetRental.as_str(),
580 Self::ApplicationCanisterManagement => {
581 NnsProposalTopic::ApplicationCanisterManagement.as_str()
582 }
583 Self::ProtocolCanisterManagement => {
584 NnsProposalTopic::ProtocolCanisterManagement.as_str()
585 }
586 }
587 }
588
589 pub(in crate::nns) const fn topic_code(self) -> Option<i32> {
590 match self {
591 Self::Any => None,
592 Self::NeuronManagement => Some(NnsProposalTopic::NeuronManagement.code()),
593 Self::ExchangeRate => Some(NnsProposalTopic::ExchangeRate.code()),
594 Self::NetworkEconomics => Some(NnsProposalTopic::NetworkEconomics.code()),
595 Self::Governance => Some(NnsProposalTopic::Governance.code()),
596 Self::NodeAdmin => Some(NnsProposalTopic::NodeAdmin.code()),
597 Self::ParticipantManagement => Some(NnsProposalTopic::ParticipantManagement.code()),
598 Self::SubnetManagement => Some(NnsProposalTopic::SubnetManagement.code()),
599 Self::NetworkCanisterManagement => {
600 Some(NnsProposalTopic::NetworkCanisterManagement.code())
601 }
602 Self::Kyc => Some(NnsProposalTopic::Kyc.code()),
603 Self::NodeProviderRewards => Some(NnsProposalTopic::NodeProviderRewards.code()),
604 Self::IcOsVersionDeployment => Some(NnsProposalTopic::IcOsVersionDeployment.code()),
605 Self::IcOsVersionElection => Some(NnsProposalTopic::IcOsVersionElection.code()),
606 Self::SnsAndCommunityFund => Some(NnsProposalTopic::SnsAndCommunityFund.code()),
607 Self::ApiBoundaryNodeManagement => {
608 Some(NnsProposalTopic::ApiBoundaryNodeManagement.code())
609 }
610 Self::SubnetRental => Some(NnsProposalTopic::SubnetRental.code()),
611 Self::ApplicationCanisterManagement => {
612 Some(NnsProposalTopic::ApplicationCanisterManagement.code())
613 }
614 Self::ProtocolCanisterManagement => {
615 Some(NnsProposalTopic::ProtocolCanisterManagement.code())
616 }
617 }
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624
625 #[test]
626 fn proposal_status_codes_and_labels_round_trip() {
627 for (status, code, label) in [
628 (NnsProposalStatus::Unspecified, 0, "unspecified"),
629 (NnsProposalStatus::Open, 1, "open"),
630 (NnsProposalStatus::Rejected, 2, "rejected"),
631 (NnsProposalStatus::Adopted, 3, "adopted"),
632 (NnsProposalStatus::Executed, 4, "executed"),
633 (NnsProposalStatus::Failed, 5, "failed"),
634 ] {
635 assert_eq!(status.code(), code);
636 assert_eq!(NnsProposalStatus::from_code(code), status);
637 assert_json_label(status, label);
638 }
639 assert_eq!(
640 NnsProposalStatus::from_code(99),
641 NnsProposalStatus::Unspecified
642 );
643 }
644
645 #[test]
646 fn proposal_reward_status_codes_and_labels_round_trip() {
647 for (status, code, label) in [
648 (NnsProposalRewardStatus::Unspecified, 0, "unspecified"),
649 (NnsProposalRewardStatus::AcceptVotes, 1, "accept-votes"),
650 (NnsProposalRewardStatus::ReadyToSettle, 2, "ready-to-settle"),
651 (NnsProposalRewardStatus::Settled, 3, "settled"),
652 (NnsProposalRewardStatus::Ineligible, 4, "ineligible"),
653 ] {
654 assert_eq!(status.code(), code);
655 assert_eq!(NnsProposalRewardStatus::from_code(code), status);
656 assert_json_label(status, label);
657 }
658 assert_eq!(
659 NnsProposalRewardStatus::from_code(99),
660 NnsProposalRewardStatus::Unspecified
661 );
662 }
663
664 #[test]
665 fn proposal_vote_codes_and_labels_round_trip() {
666 for (vote, code, label) in [
667 (NnsProposalVote::Unspecified, 0, "unspecified"),
668 (NnsProposalVote::Yes, 1, "yes"),
669 (NnsProposalVote::No, 2, "no"),
670 ] {
671 assert_eq!(vote.code(), code);
672 assert_eq!(NnsProposalVote::from_code(code), vote);
673 assert_json_label(vote, label);
674 }
675 assert_eq!(NnsProposalVote::from_code(99), NnsProposalVote::Unspecified);
676 }
677
678 #[test]
679 fn proposal_topic_codes_and_labels_round_trip() {
680 for (topic, code, label) in [
681 (NnsProposalTopic::Unspecified, 0, "unspecified"),
682 (NnsProposalTopic::NeuronManagement, 1, "neuron-management"),
683 (NnsProposalTopic::ExchangeRate, 2, "exchange-rate"),
684 (NnsProposalTopic::NetworkEconomics, 3, "network-economics"),
685 (NnsProposalTopic::Governance, 4, "governance"),
686 (NnsProposalTopic::NodeAdmin, 5, "node-admin"),
687 (
688 NnsProposalTopic::ParticipantManagement,
689 6,
690 "participant-management",
691 ),
692 (NnsProposalTopic::SubnetManagement, 7, "subnet-management"),
693 (
694 NnsProposalTopic::NetworkCanisterManagement,
695 8,
696 "network-canister-management",
697 ),
698 (NnsProposalTopic::Kyc, 9, "kyc"),
699 (
700 NnsProposalTopic::NodeProviderRewards,
701 10,
702 "node-provider-rewards",
703 ),
704 (
705 NnsProposalTopic::IcOsVersionDeployment,
706 12,
707 "ic-os-version-deployment",
708 ),
709 (
710 NnsProposalTopic::IcOsVersionElection,
711 13,
712 "ic-os-version-election",
713 ),
714 (
715 NnsProposalTopic::SnsAndCommunityFund,
716 14,
717 "sns-and-community-fund",
718 ),
719 (
720 NnsProposalTopic::ApiBoundaryNodeManagement,
721 15,
722 "api-boundary-node-management",
723 ),
724 (NnsProposalTopic::SubnetRental, 16, "subnet-rental"),
725 (
726 NnsProposalTopic::ApplicationCanisterManagement,
727 17,
728 "application-canister-management",
729 ),
730 (
731 NnsProposalTopic::ProtocolCanisterManagement,
732 18,
733 "protocol-canister-management",
734 ),
735 ] {
736 assert_eq!(topic.code(), code);
737 assert_eq!(NnsProposalTopic::from_code(code), topic);
738 assert_json_label(topic, label);
739 }
740 assert_eq!(
741 NnsProposalTopic::from_code(11),
742 NnsProposalTopic::Unspecified
743 );
744 assert_eq!(
745 NnsProposalTopic::from_code(99),
746 NnsProposalTopic::Unspecified
747 );
748 }
749
750 fn assert_json_label<T>(value: T, label: &str)
751 where
752 T: Copy + std::fmt::Debug + Eq + Serialize + serde::de::DeserializeOwned,
753 {
754 assert_eq!(
755 serde_json::to_string(&value).unwrap(),
756 format!("\"{label}\"")
757 );
758 assert_eq!(
759 serde_json::from_str::<T>(&format!("\"{label}\"")).unwrap(),
760 value
761 );
762 }
763}