1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use crate::db::models::vote_options::VoteOptions;
use crate::utils::datetime::unix_timestamp_to_datetime;
use serde::de::Visitor;
use serde::{ser::Error, Deserialize, Deserializer, Serializer};
use snapshot_lib::Fraction;
use std::fmt;
use std::str::FromStr;
use time::{format_description::well_known::Rfc3339, OffsetDateTime};

// this warning should be disable here since the interface for this function requires
// the first argument to be passed by value
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn serialize_unix_timestamp_as_rfc3339<S: Serializer>(
    timestamp: &i64,
    serializer: S,
) -> Result<S::Ok, S::Error> {
    let datetime = unix_timestamp_to_datetime(*timestamp);
    serializer.serialize_str(
        &datetime
            .format(&Rfc3339)
            .map_err(|e| S::Error::custom(format!("Could not serialize date: {}", e)))?,
    )
}

pub fn deserialize_unix_timestamp_from_rfc3339<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
    D: Deserializer<'de>,
{
    struct Rfc3339Deserializer();

    impl<'de> Visitor<'de> for Rfc3339Deserializer {
        type Value = OffsetDateTime;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("An rfc3339 compatible string is needed")
        }

        fn visit_str<E>(self, value: &str) -> Result<OffsetDateTime, E>
        where
            E: serde::de::Error,
        {
            OffsetDateTime::parse(value, &Rfc3339).map_err(|e| E::custom(format!("{}", e)))
        }
    }

    deserializer
        .deserialize_str(Rfc3339Deserializer())
        .map(|datetime| datetime.unix_timestamp())
}

pub fn serialize_bin_as_str<S: Serializer>(data: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
    serializer.serialize_str(&String::from_utf8(data.to_vec()).unwrap())
}

pub fn deserialize_string_as_bytes<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
    D: Deserializer<'de>,
{
    struct VecU8Deserializer();

    impl<'de> Visitor<'de> for VecU8Deserializer {
        type Value = Vec<u8>;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("A compatible utf8 string is needed")
        }

        fn visit_str<E>(self, value: &str) -> Result<Vec<u8>, E>
        where
            E: serde::de::Error,
        {
            let vec = value.as_bytes().to_vec();
            Ok(vec)
        }
    }

    deserializer.deserialize_str(VecU8Deserializer())
}

// this warning should be disable here since the interface for this function requires
// the first argument to be passed by value
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn serialize_i64_as_str<S: Serializer>(data: &i64, serializer: S) -> Result<S::Ok, S::Error> {
    serializer.serialize_str(&data.to_string())
}

pub fn deserialize_i64_from_str<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
    D: Deserializer<'de>,
{
    struct I64Deserializer();

    impl<'de> Visitor<'de> for I64Deserializer {
        type Value = i64;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a compatible i64 number or string with i64 format")
        }

        fn visit_str<E>(self, value: &str) -> Result<i64, E>
        where
            E: serde::de::Error,
        {
            value
                .parse()
                .map_err(|e| E::custom(format!("Error parsing {} to i64: {}", value, e)))
        }
    }
    deserializer.deserialize_str(I64Deserializer())
}

pub fn deserialize_vote_options_from_string<'de, D>(
    deserializer: D,
) -> Result<VoteOptions, D::Error>
where
    D: Deserializer<'de>,
{
    struct VoteOptionsDeserializer;

    impl<'de> Visitor<'de> for VoteOptionsDeserializer {
        type Value = VoteOptions;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("A coma separated values are needed")
        }

        fn visit_str<E>(self, value: &str) -> Result<VoteOptions, E>
        where
            E: serde::de::Error,
        {
            Ok(VoteOptions::parse_coma_separated_value(value))
        }
    }

    deserializer.deserialize_str(VoteOptionsDeserializer)
}

pub fn serialize_vote_options_to_string<S: Serializer>(
    data: &VoteOptions,
    serializer: S,
) -> Result<S::Ok, S::Error> {
    serializer.serialize_str(&data.as_csv_string())
}

pub fn deserialize_truthy_falsy<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    D: Deserializer<'de>,
{
    let truthy_value = <&str>::deserialize(deserializer)?;
    Ok(matches!(
        truthy_value.to_lowercase().as_ref(),
        "x" | "1" | "true"
    ))
}

pub fn serialize_fraction_to_string<S: Serializer>(
    fraction: &Fraction,
    serializer: S,
) -> Result<S::Ok, S::Error> {
    serializer.serialize_str(&fraction.to_string())
}

pub fn deserialize_fraction_from_string<'de, D>(deserializer: D) -> Result<Fraction, D::Error>
where
    D: Deserializer<'de>,
{
    struct FractionDeserializer;

    impl<'de> Visitor<'de> for FractionDeserializer {
        type Value = Fraction;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("A fraction value e.g. 1.0, 0.56")
        }

        fn visit_str<E>(self, value: &str) -> Result<Fraction, E>
        where
            E: serde::de::Error,
        {
            match value {
                "NaN" => Err(E::custom(
                    "Invalid value format, should be a number e.g. 1.0, 0.56".to_string(),
                )),
                _ => Ok(Fraction::from_str(value).map_err(|e| E::custom(e.to_string()))?),
            }
        }
    }

    deserializer.deserialize_str(FractionDeserializer)
}