use serde::{Deserialize, Serialize};
macro_rules! governance_code_enum {
(
$(#[$enum_attr:meta])*
pub enum $name:ident {
$($(#[$variant_attr:meta])* $variant:ident = $code:literal => $label:literal,)*
}
) => {
$(#[$enum_attr])*
pub enum $name {
$($(#[$variant_attr])* #[serde(rename = $label)] $variant,)*
}
impl $name {
#[must_use]
pub const fn from_code(code: i32) -> Self {
match code {
$($code => Self::$variant,)*
_ => Self::Unspecified,
}
}
#[must_use]
pub const fn code(self) -> i32 {
match self { $(Self::$variant => $code,)* }
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self { $(Self::$variant => $label,)* }
}
}
};
}
pub(in crate::nns) const NNS_PROPOSAL_SORT_API_LABEL: &str = "api";
pub(in crate::nns) const NNS_PROPOSAL_SORT_ID_LABEL: &str = "id";
pub(in crate::nns) const NNS_PROPOSAL_SORT_STATUS_LABEL: &str = "status";
pub(in crate::nns) const NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL: &str = "reward-status";
pub(in crate::nns) const NNS_PROPOSAL_SORT_TOPIC_LABEL: &str = "topic";
pub(in crate::nns) const NNS_PROPOSAL_SORT_PROPOSER_LABEL: &str = "proposer";
pub(in crate::nns) const NNS_PROPOSAL_SORT_TITLE_LABEL: &str = "title";
pub(in crate::nns) const NNS_PROPOSAL_SORT_ACTION_LABEL: &str = "action";
pub(in crate::nns) const NNS_PROPOSAL_SORT_YES_LABEL: &str = "yes";
pub(in crate::nns) const NNS_PROPOSAL_SORT_NO_LABEL: &str = "no";
pub(in crate::nns) const NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL: &str = "total-votes";
pub(in crate::nns) const NNS_PROPOSAL_SORT_TALLY_TIME_LABEL: &str = "tally-time";
pub(in crate::nns) const NNS_PROPOSAL_SORT_VOTING_POWER_LABEL: &str = "voting-power";
pub(in crate::nns) const NNS_PROPOSAL_SORT_BALLOTS_LABEL: &str = "ballots";
pub(in crate::nns) const NNS_PROPOSAL_SORT_REJECT_COST_LABEL: &str = "reject-cost";
pub(in crate::nns) const NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL: &str = "reward-round";
pub(in crate::nns) const NNS_PROPOSAL_SORT_PROPOSED_LABEL: &str = "proposed";
pub(in crate::nns) const NNS_PROPOSAL_SORT_DEADLINE_LABEL: &str = "deadline";
pub(in crate::nns) const NNS_PROPOSAL_SORT_DECIDED_LABEL: &str = "decided";
pub(in crate::nns) const NNS_PROPOSAL_SORT_EXECUTED_LABEL: &str = "executed";
pub(in crate::nns) const NNS_PROPOSAL_SORT_FAILED_LABEL: &str = "failed";
pub(in crate::nns) const NNS_PROPOSAL_SORT_ASC_LABEL: &str = "asc";
pub(in crate::nns) const NNS_PROPOSAL_SORT_DESC_LABEL: &str = "desc";
pub(in crate::nns) const NNS_PROPOSAL_SORT_NONE_LABEL: &str = "none";
governance_code_enum! {
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum NnsProposalStatus {
Unspecified = 0 => "unspecified",
Open = 1 => "open",
Rejected = 2 => "rejected",
Adopted = 3 => "adopted",
Executed = 4 => "executed",
Failed = 5 => "failed",
}
}
governance_code_enum! {
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum NnsProposalRewardStatus {
Unspecified = 0 => "unspecified",
AcceptVotes = 1 => "accept-votes",
ReadyToSettle = 2 => "ready-to-settle",
Settled = 3 => "settled",
Ineligible = 4 => "ineligible",
}
}
governance_code_enum! {
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum NnsProposalTopic {
Unspecified = 0 => "unspecified",
NeuronManagement = 1 => "neuron-management",
ExchangeRate = 2 => "exchange-rate",
NetworkEconomics = 3 => "network-economics",
Governance = 4 => "governance",
NodeAdmin = 5 => "node-admin",
ParticipantManagement = 6 => "participant-management",
SubnetManagement = 7 => "subnet-management",
NetworkCanisterManagement = 8 => "network-canister-management",
Kyc = 9 => "kyc",
NodeProviderRewards = 10 => "node-provider-rewards",
IcOsVersionDeployment = 12 => "ic-os-version-deployment",
IcOsVersionElection = 13 => "ic-os-version-election",
SnsAndCommunityFund = 14 => "sns-and-community-fund",
ApiBoundaryNodeManagement = 15 => "api-boundary-node-management",
SubnetRental = 16 => "subnet-rental",
ApplicationCanisterManagement = 17 => "application-canister-management",
ProtocolCanisterManagement = 18 => "protocol-canister-management",
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum NnsProposalListSort {
#[default]
Api,
Id,
Status,
RewardStatus,
Topic,
Proposer,
Title,
Action,
Yes,
No,
TotalVotes,
TallyTime,
VotingPower,
Ballots,
RejectCost,
RewardRound,
Proposed,
Deadline,
Decided,
Executed,
Failed,
}
impl NnsProposalListSort {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Api => NNS_PROPOSAL_SORT_API_LABEL,
Self::Id => NNS_PROPOSAL_SORT_ID_LABEL,
Self::Status => NNS_PROPOSAL_SORT_STATUS_LABEL,
Self::RewardStatus => NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL,
Self::Topic => NNS_PROPOSAL_SORT_TOPIC_LABEL,
Self::Proposer => NNS_PROPOSAL_SORT_PROPOSER_LABEL,
Self::Title => NNS_PROPOSAL_SORT_TITLE_LABEL,
Self::Action => NNS_PROPOSAL_SORT_ACTION_LABEL,
Self::Yes => NNS_PROPOSAL_SORT_YES_LABEL,
Self::No => NNS_PROPOSAL_SORT_NO_LABEL,
Self::TotalVotes => NNS_PROPOSAL_SORT_TOTAL_VOTES_LABEL,
Self::TallyTime => NNS_PROPOSAL_SORT_TALLY_TIME_LABEL,
Self::VotingPower => NNS_PROPOSAL_SORT_VOTING_POWER_LABEL,
Self::Ballots => NNS_PROPOSAL_SORT_BALLOTS_LABEL,
Self::RejectCost => NNS_PROPOSAL_SORT_REJECT_COST_LABEL,
Self::RewardRound => NNS_PROPOSAL_SORT_REWARD_ROUND_LABEL,
Self::Proposed => NNS_PROPOSAL_SORT_PROPOSED_LABEL,
Self::Deadline => NNS_PROPOSAL_SORT_DEADLINE_LABEL,
Self::Decided => NNS_PROPOSAL_SORT_DECIDED_LABEL,
Self::Executed => NNS_PROPOSAL_SORT_EXECUTED_LABEL,
Self::Failed => NNS_PROPOSAL_SORT_FAILED_LABEL,
}
}
#[must_use]
pub const fn default_direction(self) -> NnsProposalSortDirection {
match self {
Self::Status
| Self::RewardStatus
| Self::Topic
| Self::Proposer
| Self::Title
| Self::Action => NnsProposalSortDirection::Asc,
_ => NnsProposalSortDirection::Desc,
}
}
#[must_use]
pub const fn uses_local_direction(self) -> bool {
!matches!(self, Self::Api)
}
#[must_use]
pub const fn direction_label(self, direction: NnsProposalSortDirection) -> &'static str {
match self {
Self::Api => NNS_PROPOSAL_SORT_NONE_LABEL,
_ => direction.as_str(),
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum NnsProposalSortDirection {
Asc,
#[default]
Desc,
}
impl NnsProposalSortDirection {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Asc => NNS_PROPOSAL_SORT_ASC_LABEL,
Self::Desc => NNS_PROPOSAL_SORT_DESC_LABEL,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum NnsProposalStatusFilter {
#[default]
Any,
Open,
Rejected,
Adopted,
Executed,
Failed,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum NnsProposalRewardStatusFilter {
#[default]
Any,
AcceptVotes,
ReadyToSettle,
Settled,
Ineligible,
}
impl NnsProposalRewardStatusFilter {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self.classification() {
None => "any",
Some(value) => value.as_str(),
}
}
#[cfg(any(
feature = "nns-host",
all(feature = "canister", target_arch = "wasm32")
))]
pub(in crate::nns) const fn governance_reward_status_code(self) -> Option<i32> {
match self.classification() {
None => None,
Some(value) => Some(value.code()),
}
}
const fn classification(self) -> Option<NnsProposalRewardStatus> {
match self {
Self::Any => None,
Self::AcceptVotes => Some(NnsProposalRewardStatus::AcceptVotes),
Self::ReadyToSettle => Some(NnsProposalRewardStatus::ReadyToSettle),
Self::Settled => Some(NnsProposalRewardStatus::Settled),
Self::Ineligible => Some(NnsProposalRewardStatus::Ineligible),
}
}
}
impl NnsProposalStatusFilter {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self.classification() {
None => "any",
Some(value) => value.as_str(),
}
}
#[cfg(any(
feature = "nns-host",
all(feature = "canister", target_arch = "wasm32")
))]
pub(in crate::nns) const fn governance_status_code(self) -> Option<i32> {
match self.classification() {
None => None,
Some(value) => Some(value.code()),
}
}
const fn classification(self) -> Option<NnsProposalStatus> {
match self {
Self::Any => None,
Self::Open => Some(NnsProposalStatus::Open),
Self::Rejected => Some(NnsProposalStatus::Rejected),
Self::Adopted => Some(NnsProposalStatus::Adopted),
Self::Executed => Some(NnsProposalStatus::Executed),
Self::Failed => Some(NnsProposalStatus::Failed),
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum NnsProposalTopicFilter {
#[default]
Any,
NeuronManagement,
ExchangeRate,
NetworkEconomics,
Governance,
NodeAdmin,
ParticipantManagement,
SubnetManagement,
NetworkCanisterManagement,
Kyc,
NodeProviderRewards,
IcOsVersionDeployment,
IcOsVersionElection,
SnsAndCommunityFund,
ApiBoundaryNodeManagement,
SubnetRental,
ApplicationCanisterManagement,
ProtocolCanisterManagement,
}
impl NnsProposalTopicFilter {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self.classification() {
None => "any",
Some(value) => value.as_str(),
}
}
pub(in crate::nns) const fn topic_code(self) -> Option<i32> {
match self.classification() {
None => None,
Some(value) => Some(value.code()),
}
}
const fn classification(self) -> Option<NnsProposalTopic> {
match self {
Self::Any => None,
Self::NeuronManagement => Some(NnsProposalTopic::NeuronManagement),
Self::ExchangeRate => Some(NnsProposalTopic::ExchangeRate),
Self::NetworkEconomics => Some(NnsProposalTopic::NetworkEconomics),
Self::Governance => Some(NnsProposalTopic::Governance),
Self::NodeAdmin => Some(NnsProposalTopic::NodeAdmin),
Self::ParticipantManagement => Some(NnsProposalTopic::ParticipantManagement),
Self::SubnetManagement => Some(NnsProposalTopic::SubnetManagement),
Self::NetworkCanisterManagement => Some(NnsProposalTopic::NetworkCanisterManagement),
Self::Kyc => Some(NnsProposalTopic::Kyc),
Self::NodeProviderRewards => Some(NnsProposalTopic::NodeProviderRewards),
Self::IcOsVersionDeployment => Some(NnsProposalTopic::IcOsVersionDeployment),
Self::IcOsVersionElection => Some(NnsProposalTopic::IcOsVersionElection),
Self::SnsAndCommunityFund => Some(NnsProposalTopic::SnsAndCommunityFund),
Self::ApiBoundaryNodeManagement => Some(NnsProposalTopic::ApiBoundaryNodeManagement),
Self::SubnetRental => Some(NnsProposalTopic::SubnetRental),
Self::ApplicationCanisterManagement => {
Some(NnsProposalTopic::ApplicationCanisterManagement)
}
Self::ProtocolCanisterManagement => Some(NnsProposalTopic::ProtocolCanisterManagement),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn proposal_status_codes_and_labels_round_trip() {
for (status, code, label) in [
(NnsProposalStatus::Unspecified, 0, "unspecified"),
(NnsProposalStatus::Open, 1, "open"),
(NnsProposalStatus::Rejected, 2, "rejected"),
(NnsProposalStatus::Adopted, 3, "adopted"),
(NnsProposalStatus::Executed, 4, "executed"),
(NnsProposalStatus::Failed, 5, "failed"),
] {
assert_eq!(status.code(), code);
assert_eq!(NnsProposalStatus::from_code(code), status);
assert_json_label(status, label);
}
assert_eq!(
NnsProposalStatus::from_code(99),
NnsProposalStatus::Unspecified
);
}
#[test]
fn proposal_reward_status_codes_and_labels_round_trip() {
for (status, code, label) in [
(NnsProposalRewardStatus::Unspecified, 0, "unspecified"),
(NnsProposalRewardStatus::AcceptVotes, 1, "accept-votes"),
(NnsProposalRewardStatus::ReadyToSettle, 2, "ready-to-settle"),
(NnsProposalRewardStatus::Settled, 3, "settled"),
(NnsProposalRewardStatus::Ineligible, 4, "ineligible"),
] {
assert_eq!(status.code(), code);
assert_eq!(NnsProposalRewardStatus::from_code(code), status);
assert_json_label(status, label);
}
assert_eq!(
NnsProposalRewardStatus::from_code(99),
NnsProposalRewardStatus::Unspecified
);
}
#[test]
fn proposal_topic_codes_and_labels_round_trip() {
for (topic, code, label) in [
(NnsProposalTopic::Unspecified, 0, "unspecified"),
(NnsProposalTopic::NeuronManagement, 1, "neuron-management"),
(NnsProposalTopic::ExchangeRate, 2, "exchange-rate"),
(NnsProposalTopic::NetworkEconomics, 3, "network-economics"),
(NnsProposalTopic::Governance, 4, "governance"),
(NnsProposalTopic::NodeAdmin, 5, "node-admin"),
(
NnsProposalTopic::ParticipantManagement,
6,
"participant-management",
),
(NnsProposalTopic::SubnetManagement, 7, "subnet-management"),
(
NnsProposalTopic::NetworkCanisterManagement,
8,
"network-canister-management",
),
(NnsProposalTopic::Kyc, 9, "kyc"),
(
NnsProposalTopic::NodeProviderRewards,
10,
"node-provider-rewards",
),
(
NnsProposalTopic::IcOsVersionDeployment,
12,
"ic-os-version-deployment",
),
(
NnsProposalTopic::IcOsVersionElection,
13,
"ic-os-version-election",
),
(
NnsProposalTopic::SnsAndCommunityFund,
14,
"sns-and-community-fund",
),
(
NnsProposalTopic::ApiBoundaryNodeManagement,
15,
"api-boundary-node-management",
),
(NnsProposalTopic::SubnetRental, 16, "subnet-rental"),
(
NnsProposalTopic::ApplicationCanisterManagement,
17,
"application-canister-management",
),
(
NnsProposalTopic::ProtocolCanisterManagement,
18,
"protocol-canister-management",
),
] {
assert_eq!(topic.code(), code);
assert_eq!(NnsProposalTopic::from_code(code), topic);
assert_json_label(topic, label);
}
assert_eq!(
NnsProposalTopic::from_code(11),
NnsProposalTopic::Unspecified
);
assert_eq!(
NnsProposalTopic::from_code(99),
NnsProposalTopic::Unspecified
);
}
fn assert_json_label<T>(value: T, label: &str)
where
T: Copy + std::fmt::Debug + Eq + Serialize + serde::de::DeserializeOwned,
{
assert_eq!(
serde_json::to_string(&value).unwrap(),
format!("\"{label}\"")
);
assert_eq!(
serde_json::from_str::<T>(&format!("\"{label}\"")).unwrap(),
value
);
}
}