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 ))]
489 pub(in crate::nns) const fn governance_reward_status_code(self) -> Option<i32> {
490 match self {
491 Self::Any => None,
492 Self::AcceptVotes => Some(NnsProposalRewardStatus::AcceptVotes.code()),
493 Self::ReadyToSettle => Some(NnsProposalRewardStatus::ReadyToSettle.code()),
494 Self::Settled => Some(NnsProposalRewardStatus::Settled.code()),
495 Self::Ineligible => Some(NnsProposalRewardStatus::Ineligible.code()),
496 }
497 }
498}
499
500impl NnsProposalStatusFilter {
501 #[must_use]
502 pub const fn as_str(self) -> &'static str {
503 match self {
504 Self::Any => "any",
505 Self::Open => NnsProposalStatus::Open.as_str(),
506 Self::Rejected => NnsProposalStatus::Rejected.as_str(),
507 Self::Adopted => NnsProposalStatus::Adopted.as_str(),
508 Self::Executed => NnsProposalStatus::Executed.as_str(),
509 Self::Failed => NnsProposalStatus::Failed.as_str(),
510 }
511 }
512
513 #[cfg(any(
514 feature = "nns-host",
515 all(feature = "canister", target_arch = "wasm32")
516 ))]
517 pub(in crate::nns) const fn governance_status_code(self) -> Option<i32> {
518 match self {
519 Self::Any => None,
520 Self::Open => Some(NnsProposalStatus::Open.code()),
521 Self::Rejected => Some(NnsProposalStatus::Rejected.code()),
522 Self::Adopted => Some(NnsProposalStatus::Adopted.code()),
523 Self::Executed => Some(NnsProposalStatus::Executed.code()),
524 Self::Failed => Some(NnsProposalStatus::Failed.code()),
525 }
526 }
527}
528
529#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
536pub enum NnsProposalTopicFilter {
537 #[default]
538 Any,
539 NeuronManagement,
540 ExchangeRate,
541 NetworkEconomics,
542 Governance,
543 NodeAdmin,
544 ParticipantManagement,
545 SubnetManagement,
546 NetworkCanisterManagement,
547 Kyc,
548 NodeProviderRewards,
549 IcOsVersionDeployment,
550 IcOsVersionElection,
551 SnsAndCommunityFund,
552 ApiBoundaryNodeManagement,
553 SubnetRental,
554 ApplicationCanisterManagement,
555 ProtocolCanisterManagement,
556}
557
558impl NnsProposalTopicFilter {
559 #[must_use]
560 pub const fn as_str(self) -> &'static str {
561 match self {
562 Self::Any => "any",
563 Self::NeuronManagement => NnsProposalTopic::NeuronManagement.as_str(),
564 Self::ExchangeRate => NnsProposalTopic::ExchangeRate.as_str(),
565 Self::NetworkEconomics => NnsProposalTopic::NetworkEconomics.as_str(),
566 Self::Governance => NnsProposalTopic::Governance.as_str(),
567 Self::NodeAdmin => NnsProposalTopic::NodeAdmin.as_str(),
568 Self::ParticipantManagement => NnsProposalTopic::ParticipantManagement.as_str(),
569 Self::SubnetManagement => NnsProposalTopic::SubnetManagement.as_str(),
570 Self::NetworkCanisterManagement => NnsProposalTopic::NetworkCanisterManagement.as_str(),
571 Self::Kyc => NnsProposalTopic::Kyc.as_str(),
572 Self::NodeProviderRewards => NnsProposalTopic::NodeProviderRewards.as_str(),
573 Self::IcOsVersionDeployment => NnsProposalTopic::IcOsVersionDeployment.as_str(),
574 Self::IcOsVersionElection => NnsProposalTopic::IcOsVersionElection.as_str(),
575 Self::SnsAndCommunityFund => NnsProposalTopic::SnsAndCommunityFund.as_str(),
576 Self::ApiBoundaryNodeManagement => NnsProposalTopic::ApiBoundaryNodeManagement.as_str(),
577 Self::SubnetRental => NnsProposalTopic::SubnetRental.as_str(),
578 Self::ApplicationCanisterManagement => {
579 NnsProposalTopic::ApplicationCanisterManagement.as_str()
580 }
581 Self::ProtocolCanisterManagement => {
582 NnsProposalTopic::ProtocolCanisterManagement.as_str()
583 }
584 }
585 }
586
587 pub(in crate::nns) const fn topic_code(self) -> Option<i32> {
588 match self {
589 Self::Any => None,
590 Self::NeuronManagement => Some(NnsProposalTopic::NeuronManagement.code()),
591 Self::ExchangeRate => Some(NnsProposalTopic::ExchangeRate.code()),
592 Self::NetworkEconomics => Some(NnsProposalTopic::NetworkEconomics.code()),
593 Self::Governance => Some(NnsProposalTopic::Governance.code()),
594 Self::NodeAdmin => Some(NnsProposalTopic::NodeAdmin.code()),
595 Self::ParticipantManagement => Some(NnsProposalTopic::ParticipantManagement.code()),
596 Self::SubnetManagement => Some(NnsProposalTopic::SubnetManagement.code()),
597 Self::NetworkCanisterManagement => {
598 Some(NnsProposalTopic::NetworkCanisterManagement.code())
599 }
600 Self::Kyc => Some(NnsProposalTopic::Kyc.code()),
601 Self::NodeProviderRewards => Some(NnsProposalTopic::NodeProviderRewards.code()),
602 Self::IcOsVersionDeployment => Some(NnsProposalTopic::IcOsVersionDeployment.code()),
603 Self::IcOsVersionElection => Some(NnsProposalTopic::IcOsVersionElection.code()),
604 Self::SnsAndCommunityFund => Some(NnsProposalTopic::SnsAndCommunityFund.code()),
605 Self::ApiBoundaryNodeManagement => {
606 Some(NnsProposalTopic::ApiBoundaryNodeManagement.code())
607 }
608 Self::SubnetRental => Some(NnsProposalTopic::SubnetRental.code()),
609 Self::ApplicationCanisterManagement => {
610 Some(NnsProposalTopic::ApplicationCanisterManagement.code())
611 }
612 Self::ProtocolCanisterManagement => {
613 Some(NnsProposalTopic::ProtocolCanisterManagement.code())
614 }
615 }
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 #[test]
624 fn proposal_status_codes_and_labels_round_trip() {
625 for (status, code, label) in [
626 (NnsProposalStatus::Unspecified, 0, "unspecified"),
627 (NnsProposalStatus::Open, 1, "open"),
628 (NnsProposalStatus::Rejected, 2, "rejected"),
629 (NnsProposalStatus::Adopted, 3, "adopted"),
630 (NnsProposalStatus::Executed, 4, "executed"),
631 (NnsProposalStatus::Failed, 5, "failed"),
632 ] {
633 assert_eq!(status.code(), code);
634 assert_eq!(NnsProposalStatus::from_code(code), status);
635 assert_json_label(status, label);
636 }
637 assert_eq!(
638 NnsProposalStatus::from_code(99),
639 NnsProposalStatus::Unspecified
640 );
641 }
642
643 #[test]
644 fn proposal_reward_status_codes_and_labels_round_trip() {
645 for (status, code, label) in [
646 (NnsProposalRewardStatus::Unspecified, 0, "unspecified"),
647 (NnsProposalRewardStatus::AcceptVotes, 1, "accept-votes"),
648 (NnsProposalRewardStatus::ReadyToSettle, 2, "ready-to-settle"),
649 (NnsProposalRewardStatus::Settled, 3, "settled"),
650 (NnsProposalRewardStatus::Ineligible, 4, "ineligible"),
651 ] {
652 assert_eq!(status.code(), code);
653 assert_eq!(NnsProposalRewardStatus::from_code(code), status);
654 assert_json_label(status, label);
655 }
656 assert_eq!(
657 NnsProposalRewardStatus::from_code(99),
658 NnsProposalRewardStatus::Unspecified
659 );
660 }
661
662 #[test]
663 fn proposal_vote_codes_and_labels_round_trip() {
664 for (vote, code, label) in [
665 (NnsProposalVote::Unspecified, 0, "unspecified"),
666 (NnsProposalVote::Yes, 1, "yes"),
667 (NnsProposalVote::No, 2, "no"),
668 ] {
669 assert_eq!(vote.code(), code);
670 assert_eq!(NnsProposalVote::from_code(code), vote);
671 assert_json_label(vote, label);
672 }
673 assert_eq!(NnsProposalVote::from_code(99), NnsProposalVote::Unspecified);
674 }
675
676 #[test]
677 fn proposal_topic_codes_and_labels_round_trip() {
678 for (topic, code, label) in [
679 (NnsProposalTopic::Unspecified, 0, "unspecified"),
680 (NnsProposalTopic::NeuronManagement, 1, "neuron-management"),
681 (NnsProposalTopic::ExchangeRate, 2, "exchange-rate"),
682 (NnsProposalTopic::NetworkEconomics, 3, "network-economics"),
683 (NnsProposalTopic::Governance, 4, "governance"),
684 (NnsProposalTopic::NodeAdmin, 5, "node-admin"),
685 (
686 NnsProposalTopic::ParticipantManagement,
687 6,
688 "participant-management",
689 ),
690 (NnsProposalTopic::SubnetManagement, 7, "subnet-management"),
691 (
692 NnsProposalTopic::NetworkCanisterManagement,
693 8,
694 "network-canister-management",
695 ),
696 (NnsProposalTopic::Kyc, 9, "kyc"),
697 (
698 NnsProposalTopic::NodeProviderRewards,
699 10,
700 "node-provider-rewards",
701 ),
702 (
703 NnsProposalTopic::IcOsVersionDeployment,
704 12,
705 "ic-os-version-deployment",
706 ),
707 (
708 NnsProposalTopic::IcOsVersionElection,
709 13,
710 "ic-os-version-election",
711 ),
712 (
713 NnsProposalTopic::SnsAndCommunityFund,
714 14,
715 "sns-and-community-fund",
716 ),
717 (
718 NnsProposalTopic::ApiBoundaryNodeManagement,
719 15,
720 "api-boundary-node-management",
721 ),
722 (NnsProposalTopic::SubnetRental, 16, "subnet-rental"),
723 (
724 NnsProposalTopic::ApplicationCanisterManagement,
725 17,
726 "application-canister-management",
727 ),
728 (
729 NnsProposalTopic::ProtocolCanisterManagement,
730 18,
731 "protocol-canister-management",
732 ),
733 ] {
734 assert_eq!(topic.code(), code);
735 assert_eq!(NnsProposalTopic::from_code(code), topic);
736 assert_json_label(topic, label);
737 }
738 assert_eq!(
739 NnsProposalTopic::from_code(11),
740 NnsProposalTopic::Unspecified
741 );
742 assert_eq!(
743 NnsProposalTopic::from_code(99),
744 NnsProposalTopic::Unspecified
745 );
746 }
747
748 fn assert_json_label<T>(value: T, label: &str)
749 where
750 T: Copy + std::fmt::Debug + Eq + Serialize + serde::de::DeserializeOwned,
751 {
752 assert_eq!(
753 serde_json::to_string(&value).unwrap(),
754 format!("\"{label}\"")
755 );
756 assert_eq!(
757 serde_json::from_str::<T>(&format!("\"{label}\"")).unwrap(),
758 value
759 );
760 }
761}