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 NnsProposalTopic {
166 Unspecified,
168 NeuronManagement,
170 ExchangeRate,
172 NetworkEconomics,
174 Governance,
176 NodeAdmin,
178 ParticipantManagement,
180 SubnetManagement,
182 NetworkCanisterManagement,
184 Kyc,
186 NodeProviderRewards,
188 IcOsVersionDeployment,
190 IcOsVersionElection,
192 SnsAndCommunityFund,
194 ApiBoundaryNodeManagement,
196 SubnetRental,
198 ApplicationCanisterManagement,
200 ProtocolCanisterManagement,
202}
203
204impl NnsProposalTopic {
205 #[must_use]
207 pub const fn from_code(code: i32) -> Self {
208 match code {
209 1 => Self::NeuronManagement,
210 2 => Self::ExchangeRate,
211 3 => Self::NetworkEconomics,
212 4 => Self::Governance,
213 5 => Self::NodeAdmin,
214 6 => Self::ParticipantManagement,
215 7 => Self::SubnetManagement,
216 8 => Self::NetworkCanisterManagement,
217 9 => Self::Kyc,
218 10 => Self::NodeProviderRewards,
219 12 => Self::IcOsVersionDeployment,
220 13 => Self::IcOsVersionElection,
221 14 => Self::SnsAndCommunityFund,
222 15 => Self::ApiBoundaryNodeManagement,
223 16 => Self::SubnetRental,
224 17 => Self::ApplicationCanisterManagement,
225 18 => Self::ProtocolCanisterManagement,
226 _ => Self::Unspecified,
227 }
228 }
229
230 #[must_use]
232 pub const fn code(self) -> i32 {
233 match self {
234 Self::Unspecified => 0,
235 Self::NeuronManagement => 1,
236 Self::ExchangeRate => 2,
237 Self::NetworkEconomics => 3,
238 Self::Governance => 4,
239 Self::NodeAdmin => 5,
240 Self::ParticipantManagement => 6,
241 Self::SubnetManagement => 7,
242 Self::NetworkCanisterManagement => 8,
243 Self::Kyc => 9,
244 Self::NodeProviderRewards => 10,
245 Self::IcOsVersionDeployment => 12,
246 Self::IcOsVersionElection => 13,
247 Self::SnsAndCommunityFund => 14,
248 Self::ApiBoundaryNodeManagement => 15,
249 Self::SubnetRental => 16,
250 Self::ApplicationCanisterManagement => 17,
251 Self::ProtocolCanisterManagement => 18,
252 }
253 }
254
255 #[must_use]
257 pub const fn as_str(self) -> &'static str {
258 match self {
259 Self::Unspecified => "unspecified",
260 Self::NeuronManagement => "neuron-management",
261 Self::ExchangeRate => "exchange-rate",
262 Self::NetworkEconomics => "network-economics",
263 Self::Governance => "governance",
264 Self::NodeAdmin => "node-admin",
265 Self::ParticipantManagement => "participant-management",
266 Self::SubnetManagement => "subnet-management",
267 Self::NetworkCanisterManagement => "network-canister-management",
268 Self::Kyc => "kyc",
269 Self::NodeProviderRewards => "node-provider-rewards",
270 Self::IcOsVersionDeployment => "ic-os-version-deployment",
271 Self::IcOsVersionElection => "ic-os-version-election",
272 Self::SnsAndCommunityFund => "sns-and-community-fund",
273 Self::ApiBoundaryNodeManagement => "api-boundary-node-management",
274 Self::SubnetRental => "subnet-rental",
275 Self::ApplicationCanisterManagement => "application-canister-management",
276 Self::ProtocolCanisterManagement => "protocol-canister-management",
277 }
278 }
279}
280
281#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
288pub enum NnsProposalListSort {
289 #[default]
291 Api,
292 Id,
294 Status,
296 RewardStatus,
298 Topic,
300 Proposer,
302 Title,
304 Action,
306 Yes,
308 No,
310 TotalVotes,
312 TallyTime,
314 VotingPower,
316 Ballots,
318 RejectCost,
320 RewardRound,
322 Proposed,
324 Deadline,
326 Decided,
328 Executed,
330 Failed,
332}
333
334impl NnsProposalListSort {
335 #[must_use]
337 pub const fn as_str(self) -> &'static str {
338 match self {
339 Self::Api => NNS_PROPOSAL_SORT_API_LABEL,
340 Self::Id => NNS_PROPOSAL_SORT_ID_LABEL,
341 Self::Status => NNS_PROPOSAL_SORT_STATUS_LABEL,
342 Self::RewardStatus => NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL,
343 Self::Topic => NNS_PROPOSAL_SORT_TOPIC_LABEL,
344 Self::Proposer => NNS_PROPOSAL_SORT_PROPOSER_LABEL,
345 Self::Title => NNS_PROPOSAL_SORT_TITLE_LABEL,
346 Self::Action => NNS_PROPOSAL_SORT_ACTION_LABEL,
347 Self::Yes => NNS_PROPOSAL_SORT_YES_LABEL,
348 Self::No => NNS_PROPOSAL_SORT_NO_LABEL,
349 Self::TotalVotes => NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL,
350 Self::TallyTime => NNS_PROPOSAL_SORT_TALLY_TIME_LABEL,
351 Self::VotingPower => NNS_PROPOSAL_SORT_VOTING_POWER_LABEL,
352 Self::Ballots => NNS_PROPOSAL_SORT_BALLOTS_LABEL,
353 Self::RejectCost => NNS_PROPOSAL_SORT_REJECT_COST_LABEL,
354 Self::RewardRound => NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL,
355 Self::Proposed => NNS_PROPOSAL_SORT_PROPOSED_LABEL,
356 Self::Deadline => NNS_PROPOSAL_SORT_DEADLINE_LABEL,
357 Self::Decided => NNS_PROPOSAL_SORT_DECIDED_LABEL,
358 Self::Executed => NNS_PROPOSAL_SORT_EXECUTED_LABEL,
359 Self::Failed => NNS_PROPOSAL_SORT_FAILED_LABEL,
360 }
361 }
362
363 #[must_use]
365 pub const fn default_direction(self) -> NnsProposalSortDirection {
366 match self {
367 Self::Status
368 | Self::RewardStatus
369 | Self::Topic
370 | Self::Proposer
371 | Self::Title
372 | Self::Action => NnsProposalSortDirection::Asc,
373 _ => NnsProposalSortDirection::Desc,
374 }
375 }
376
377 #[must_use]
379 pub const fn uses_local_direction(self) -> bool {
380 !matches!(self, Self::Api)
381 }
382
383 #[must_use]
385 pub const fn direction_label(self, direction: NnsProposalSortDirection) -> &'static str {
386 match self {
387 Self::Api => NNS_PROPOSAL_SORT_NONE_LABEL,
388 _ => direction.as_str(),
389 }
390 }
391}
392
393#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
400pub enum NnsProposalSortDirection {
401 Asc,
403 #[default]
405 Desc,
406}
407
408impl NnsProposalSortDirection {
409 #[must_use]
411 pub const fn as_str(self) -> &'static str {
412 match self {
413 Self::Asc => NNS_PROPOSAL_SORT_ASC_LABEL,
414 Self::Desc => NNS_PROPOSAL_SORT_DESC_LABEL,
415 }
416 }
417}
418
419#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
426pub enum NnsProposalStatusFilter {
427 #[default]
429 Any,
430 Open,
432 Rejected,
434 Adopted,
436 Executed,
438 Failed,
440}
441
442#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
449pub enum NnsProposalRewardStatusFilter {
450 #[default]
452 Any,
453 AcceptVotes,
455 ReadyToSettle,
457 Settled,
459 Ineligible,
461}
462
463impl NnsProposalRewardStatusFilter {
464 #[must_use]
466 pub const fn as_str(self) -> &'static str {
467 match self {
468 Self::Any => "any",
469 Self::AcceptVotes => NnsProposalRewardStatus::AcceptVotes.as_str(),
470 Self::ReadyToSettle => NnsProposalRewardStatus::ReadyToSettle.as_str(),
471 Self::Settled => NnsProposalRewardStatus::Settled.as_str(),
472 Self::Ineligible => NnsProposalRewardStatus::Ineligible.as_str(),
473 }
474 }
475
476 #[cfg(any(
477 feature = "nns-host",
478 all(feature = "canister", target_arch = "wasm32")
479 ))]
480 pub(in crate::nns) const fn governance_reward_status_code(self) -> Option<i32> {
481 match self {
482 Self::Any => None,
483 Self::AcceptVotes => Some(NnsProposalRewardStatus::AcceptVotes.code()),
484 Self::ReadyToSettle => Some(NnsProposalRewardStatus::ReadyToSettle.code()),
485 Self::Settled => Some(NnsProposalRewardStatus::Settled.code()),
486 Self::Ineligible => Some(NnsProposalRewardStatus::Ineligible.code()),
487 }
488 }
489}
490
491impl NnsProposalStatusFilter {
492 #[must_use]
494 pub const fn as_str(self) -> &'static str {
495 match self {
496 Self::Any => "any",
497 Self::Open => NnsProposalStatus::Open.as_str(),
498 Self::Rejected => NnsProposalStatus::Rejected.as_str(),
499 Self::Adopted => NnsProposalStatus::Adopted.as_str(),
500 Self::Executed => NnsProposalStatus::Executed.as_str(),
501 Self::Failed => NnsProposalStatus::Failed.as_str(),
502 }
503 }
504
505 #[cfg(any(
506 feature = "nns-host",
507 all(feature = "canister", target_arch = "wasm32")
508 ))]
509 pub(in crate::nns) const fn governance_status_code(self) -> Option<i32> {
510 match self {
511 Self::Any => None,
512 Self::Open => Some(NnsProposalStatus::Open.code()),
513 Self::Rejected => Some(NnsProposalStatus::Rejected.code()),
514 Self::Adopted => Some(NnsProposalStatus::Adopted.code()),
515 Self::Executed => Some(NnsProposalStatus::Executed.code()),
516 Self::Failed => Some(NnsProposalStatus::Failed.code()),
517 }
518 }
519}
520
521#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
528pub enum NnsProposalTopicFilter {
529 #[default]
531 Any,
532 NeuronManagement,
534 ExchangeRate,
536 NetworkEconomics,
538 Governance,
540 NodeAdmin,
542 ParticipantManagement,
544 SubnetManagement,
546 NetworkCanisterManagement,
548 Kyc,
550 NodeProviderRewards,
552 IcOsVersionDeployment,
554 IcOsVersionElection,
556 SnsAndCommunityFund,
558 ApiBoundaryNodeManagement,
560 SubnetRental,
562 ApplicationCanisterManagement,
564 ProtocolCanisterManagement,
566}
567
568impl NnsProposalTopicFilter {
569 #[must_use]
571 pub const fn as_str(self) -> &'static str {
572 match self {
573 Self::Any => "any",
574 Self::NeuronManagement => NnsProposalTopic::NeuronManagement.as_str(),
575 Self::ExchangeRate => NnsProposalTopic::ExchangeRate.as_str(),
576 Self::NetworkEconomics => NnsProposalTopic::NetworkEconomics.as_str(),
577 Self::Governance => NnsProposalTopic::Governance.as_str(),
578 Self::NodeAdmin => NnsProposalTopic::NodeAdmin.as_str(),
579 Self::ParticipantManagement => NnsProposalTopic::ParticipantManagement.as_str(),
580 Self::SubnetManagement => NnsProposalTopic::SubnetManagement.as_str(),
581 Self::NetworkCanisterManagement => NnsProposalTopic::NetworkCanisterManagement.as_str(),
582 Self::Kyc => NnsProposalTopic::Kyc.as_str(),
583 Self::NodeProviderRewards => NnsProposalTopic::NodeProviderRewards.as_str(),
584 Self::IcOsVersionDeployment => NnsProposalTopic::IcOsVersionDeployment.as_str(),
585 Self::IcOsVersionElection => NnsProposalTopic::IcOsVersionElection.as_str(),
586 Self::SnsAndCommunityFund => NnsProposalTopic::SnsAndCommunityFund.as_str(),
587 Self::ApiBoundaryNodeManagement => NnsProposalTopic::ApiBoundaryNodeManagement.as_str(),
588 Self::SubnetRental => NnsProposalTopic::SubnetRental.as_str(),
589 Self::ApplicationCanisterManagement => {
590 NnsProposalTopic::ApplicationCanisterManagement.as_str()
591 }
592 Self::ProtocolCanisterManagement => {
593 NnsProposalTopic::ProtocolCanisterManagement.as_str()
594 }
595 }
596 }
597
598 pub(in crate::nns) const fn topic_code(self) -> Option<i32> {
599 match self {
600 Self::Any => None,
601 Self::NeuronManagement => Some(NnsProposalTopic::NeuronManagement.code()),
602 Self::ExchangeRate => Some(NnsProposalTopic::ExchangeRate.code()),
603 Self::NetworkEconomics => Some(NnsProposalTopic::NetworkEconomics.code()),
604 Self::Governance => Some(NnsProposalTopic::Governance.code()),
605 Self::NodeAdmin => Some(NnsProposalTopic::NodeAdmin.code()),
606 Self::ParticipantManagement => Some(NnsProposalTopic::ParticipantManagement.code()),
607 Self::SubnetManagement => Some(NnsProposalTopic::SubnetManagement.code()),
608 Self::NetworkCanisterManagement => {
609 Some(NnsProposalTopic::NetworkCanisterManagement.code())
610 }
611 Self::Kyc => Some(NnsProposalTopic::Kyc.code()),
612 Self::NodeProviderRewards => Some(NnsProposalTopic::NodeProviderRewards.code()),
613 Self::IcOsVersionDeployment => Some(NnsProposalTopic::IcOsVersionDeployment.code()),
614 Self::IcOsVersionElection => Some(NnsProposalTopic::IcOsVersionElection.code()),
615 Self::SnsAndCommunityFund => Some(NnsProposalTopic::SnsAndCommunityFund.code()),
616 Self::ApiBoundaryNodeManagement => {
617 Some(NnsProposalTopic::ApiBoundaryNodeManagement.code())
618 }
619 Self::SubnetRental => Some(NnsProposalTopic::SubnetRental.code()),
620 Self::ApplicationCanisterManagement => {
621 Some(NnsProposalTopic::ApplicationCanisterManagement.code())
622 }
623 Self::ProtocolCanisterManagement => {
624 Some(NnsProposalTopic::ProtocolCanisterManagement.code())
625 }
626 }
627 }
628}
629
630#[cfg(test)]
631mod tests {
632 use super::*;
633
634 #[test]
635 fn proposal_status_codes_and_labels_round_trip() {
636 for (status, code, label) in [
637 (NnsProposalStatus::Unspecified, 0, "unspecified"),
638 (NnsProposalStatus::Open, 1, "open"),
639 (NnsProposalStatus::Rejected, 2, "rejected"),
640 (NnsProposalStatus::Adopted, 3, "adopted"),
641 (NnsProposalStatus::Executed, 4, "executed"),
642 (NnsProposalStatus::Failed, 5, "failed"),
643 ] {
644 assert_eq!(status.code(), code);
645 assert_eq!(NnsProposalStatus::from_code(code), status);
646 assert_json_label(status, label);
647 }
648 assert_eq!(
649 NnsProposalStatus::from_code(99),
650 NnsProposalStatus::Unspecified
651 );
652 }
653
654 #[test]
655 fn proposal_reward_status_codes_and_labels_round_trip() {
656 for (status, code, label) in [
657 (NnsProposalRewardStatus::Unspecified, 0, "unspecified"),
658 (NnsProposalRewardStatus::AcceptVotes, 1, "accept-votes"),
659 (NnsProposalRewardStatus::ReadyToSettle, 2, "ready-to-settle"),
660 (NnsProposalRewardStatus::Settled, 3, "settled"),
661 (NnsProposalRewardStatus::Ineligible, 4, "ineligible"),
662 ] {
663 assert_eq!(status.code(), code);
664 assert_eq!(NnsProposalRewardStatus::from_code(code), status);
665 assert_json_label(status, label);
666 }
667 assert_eq!(
668 NnsProposalRewardStatus::from_code(99),
669 NnsProposalRewardStatus::Unspecified
670 );
671 }
672
673 #[test]
674 fn proposal_topic_codes_and_labels_round_trip() {
675 for (topic, code, label) in [
676 (NnsProposalTopic::Unspecified, 0, "unspecified"),
677 (NnsProposalTopic::NeuronManagement, 1, "neuron-management"),
678 (NnsProposalTopic::ExchangeRate, 2, "exchange-rate"),
679 (NnsProposalTopic::NetworkEconomics, 3, "network-economics"),
680 (NnsProposalTopic::Governance, 4, "governance"),
681 (NnsProposalTopic::NodeAdmin, 5, "node-admin"),
682 (
683 NnsProposalTopic::ParticipantManagement,
684 6,
685 "participant-management",
686 ),
687 (NnsProposalTopic::SubnetManagement, 7, "subnet-management"),
688 (
689 NnsProposalTopic::NetworkCanisterManagement,
690 8,
691 "network-canister-management",
692 ),
693 (NnsProposalTopic::Kyc, 9, "kyc"),
694 (
695 NnsProposalTopic::NodeProviderRewards,
696 10,
697 "node-provider-rewards",
698 ),
699 (
700 NnsProposalTopic::IcOsVersionDeployment,
701 12,
702 "ic-os-version-deployment",
703 ),
704 (
705 NnsProposalTopic::IcOsVersionElection,
706 13,
707 "ic-os-version-election",
708 ),
709 (
710 NnsProposalTopic::SnsAndCommunityFund,
711 14,
712 "sns-and-community-fund",
713 ),
714 (
715 NnsProposalTopic::ApiBoundaryNodeManagement,
716 15,
717 "api-boundary-node-management",
718 ),
719 (NnsProposalTopic::SubnetRental, 16, "subnet-rental"),
720 (
721 NnsProposalTopic::ApplicationCanisterManagement,
722 17,
723 "application-canister-management",
724 ),
725 (
726 NnsProposalTopic::ProtocolCanisterManagement,
727 18,
728 "protocol-canister-management",
729 ),
730 ] {
731 assert_eq!(topic.code(), code);
732 assert_eq!(NnsProposalTopic::from_code(code), topic);
733 assert_json_label(topic, label);
734 }
735 assert_eq!(
736 NnsProposalTopic::from_code(11),
737 NnsProposalTopic::Unspecified
738 );
739 assert_eq!(
740 NnsProposalTopic::from_code(99),
741 NnsProposalTopic::Unspecified
742 );
743 }
744
745 fn assert_json_label<T>(value: T, label: &str)
746 where
747 T: Copy + std::fmt::Debug + Eq + Serialize + serde::de::DeserializeOwned,
748 {
749 assert_eq!(
750 serde_json::to_string(&value).unwrap(),
751 format!("\"{label}\"")
752 );
753 assert_eq!(
754 serde_json::from_str::<T>(&format!("\"{label}\"")).unwrap(),
755 value
756 );
757 }
758}