penumbra_sdk_governance/
vote.rs1use std::{
2 fmt::{self, Display},
3 str::FromStr,
4};
5
6use anyhow::anyhow;
7use penumbra_sdk_proto::{penumbra::core::component::governance::v1 as pb, DomainType};
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
12#[serde(try_from = "pb::Vote", into = "pb::Vote")]
13#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
14pub enum Vote {
15 Yes,
17 No,
19 Abstain,
21}
22
23impl FromStr for Vote {
24 type Err = anyhow::Error;
25
26 fn from_str(s: &str) -> anyhow::Result<Vote> {
27 match s {
28 "yes" | "y" => Ok(Vote::Yes),
29 "no" | "n" => Ok(Vote::No),
30 "abstain" | "a" => Ok(Vote::Abstain),
31 _ => Err(anyhow::anyhow!("invalid vote: {}", s)),
32 }
33 }
34}
35
36impl Display for Vote {
37 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), fmt::Error> {
38 match self {
39 Vote::Yes => write!(f, "yes"),
40 Vote::No => write!(f, "no"),
41 Vote::Abstain => write!(f, "abstain"),
42 }
43 }
44}
45
46impl From<Vote> for pb::Vote {
47 fn from(value: Vote) -> Self {
48 pb_from_vote(value)
49 }
50}
51
52const fn pb_from_vote(vote: Vote) -> pb::Vote {
54 match vote {
55 Vote::Yes => pb::Vote {
56 vote: pb::vote::Vote::Yes as i32,
57 },
58 Vote::No => pb::Vote {
59 vote: pb::vote::Vote::No as i32,
60 },
61 Vote::Abstain => pb::Vote {
62 vote: pb::vote::Vote::Abstain as i32,
63 },
64 }
65}
66
67impl TryFrom<pb::Vote> for Vote {
68 type Error = anyhow::Error;
69
70 fn try_from(msg: pb::Vote) -> Result<Self, Self::Error> {
71 let vote_state = pb::vote::Vote::try_from(msg.vote)
72 .map_err(|e| anyhow::anyhow!("invalid vote state, error: {e}"))?;
73
74 match vote_state {
75 pb::vote::Vote::Abstain => Ok(Vote::Abstain),
76 pb::vote::Vote::Yes => Ok(Vote::Yes),
77 pb::vote::Vote::No => Ok(Vote::No),
78 pb::vote::Vote::Unspecified => Err(anyhow!("unspecified vote state")),
79 }
80 }
81}
82
83#[cfg(test)]
84mod test {
85 use proptest::proptest;
86
87 proptest! {
88 #[test]
89 fn vote_roundtrip_serialize(vote: super::Vote) {
90 let pb_vote: super::pb::Vote = vote.into();
91 let vote2 = super::Vote::try_from(pb_vote).unwrap();
92 assert_eq!(vote, vote2);
93 }
94 }
95}
96
97impl DomainType for Vote {
98 type Proto = pb::Vote;
99}