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