From cc6e116823c6bfb67c91913cf21456263a427198 Mon Sep 17 00:00:00 2001 From: "D. Scott Boggs" Date: Sun, 12 Feb 2023 13:11:21 -0500 Subject: Add Admin::Cohort example test; change conversion namespace --- entities/src/admin/cohort.rs | 42 +++++--- entities/src/admin/email_domain_block.rs | 4 +- entities/src/admin/measure.rs | 6 +- entities/src/conversion.rs | 163 +++++++++++++++++-------------- entities/src/instance.rs | 6 +- entities/src/status/scheduled.rs | 2 +- entities/src/tag.rs | 4 +- 7 files changed, 129 insertions(+), 98 deletions(-) diff --git a/entities/src/admin/cohort.rs b/entities/src/admin/cohort.rs index b66787c..14f925a 100644 --- a/entities/src/admin/cohort.rs +++ b/entities/src/admin/cohort.rs @@ -1,5 +1,7 @@ +use is_variant::IsVariant; use serde::{Deserialize, Serialize}; use time::{serde::iso8601, OffsetDateTime}; +use crate::conversion; /// Represents a retention metric. /// @@ -18,23 +20,13 @@ pub struct Cohort { /// The size of the bucket for the returned [`Cohort`] data. /// /// See also [the API documentation](https://docs.joinmastodon.org/entities/Admin_Cohort/#frequency) -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, IsVariant)] +#[serde(rename_all = "lowercase")] pub enum CohortFrequency { Day, Month, } -impl CohortFrequency { - #![allow(missing_docs)] - - pub fn is_day(&self) -> bool { - *self == Self::Day - } - pub fn is_month(&self) -> bool { - *self == Self::Month - } -} - /// Represents a single value from a set of retention metrics. /// /// See also [the API documentation](https://docs.joinmastodon.org/entities/Admin_Cohort/#CohortData) @@ -47,5 +39,31 @@ pub struct Data { /// were active for the given date bucket. pub rate: f64, /// How many users registered in the specified period and were active for the given date bucket. + #[serde(with = "conversion::string_to::i64")] pub value: i64, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cohort_example() { + let example = r#"{ + "period": "2022-09-01T00:00:00+00:00", + "frequency": "month", + "data": [ + { + "date": "2022-09-01T00:00:00+00:00", + "rate": 1.0, + "value": "2" + } + ] + }"#; + let subject: Cohort = serde_json::from_str(example).unwrap(); + assert!(subject.frequency.is_month()); + let data = &subject.data[0]; + assert_eq!(data.rate, 1.0); + assert_eq!(data.value, 2); + } +} diff --git a/entities/src/admin/email_domain_block.rs b/entities/src/admin/email_domain_block.rs index d6b6a73..0fbd333 100644 --- a/entities/src/admin/email_domain_block.rs +++ b/entities/src/admin/email_domain_block.rs @@ -25,10 +25,10 @@ pub struct History { #[serde(with = "conversion::date_from_timestamp")] pub day: Date, /// The counted accounts signup attempts using that email domain within that day. - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub accounts: u64, /// The counted IP signup attempts of that email domain within that day. - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub uses: u64, } diff --git a/entities/src/admin/measure.rs b/entities/src/admin/measure.rs index 08a39bc..3dd95c3 100644 --- a/entities/src/admin/measure.rs +++ b/entities/src/admin/measure.rs @@ -11,7 +11,7 @@ pub struct Measure { /// The units associated with this data item’s value, if applicable. pub unit: Option, /// The numeric total associated with the requested measure. - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub total: u64, /// A human-readable formatted value for this data item. #[serde(default)] @@ -20,7 +20,7 @@ pub struct Measure { /// period. Previous period is calculated by subtracting the start_at and /// end_at dates, then offsetting both start and end dates backwards by the /// length of the time period. - #[serde(with = "conversion::string_to_u64::option")] + #[serde(with = "conversion::string_to::u64::option")] pub previous_total: Option, /// The data available for the requested measure, split into daily buckets. pub data: Vec, @@ -33,7 +33,7 @@ pub struct Data { #[serde(with = "iso8601")] pub date: OffsetDateTime, /// The numeric value for the requested measure. - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub value: u64, } diff --git a/entities/src/conversion.rs b/entities/src/conversion.rs index ccfaa81..07320d0 100644 --- a/entities/src/conversion.rs +++ b/entities/src/conversion.rs @@ -1,94 +1,107 @@ -pub(crate) mod string_to_u64 { - use serde::{ - de::{self, Visitor}, - Deserializer, Serializer, - }; - - pub(crate) fn serialize(value: &u64, ser: S) -> Result - where - S: Serializer, - { - ser.serialize_str(&value.to_string()) - } - - pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct StringToIntVisitor; - - impl<'v> Visitor<'v> for StringToIntVisitor { - type Value = u64; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - formatter, - "a string which can be parsed as an unsigned, 64-bit integer" - ) - } - fn visit_str(self, v: &str) -> Result +macro_rules! string_conversion { + ($t:ident) => { + #[allow(dead_code)] + pub(crate) mod $t { + use serde::{ + de::{self, Visitor}, + Deserializer, Serializer, + }; + + pub(crate) fn serialize(value: &$t, ser: S) -> Result where - E: serde::de::Error, + S: Serializer, { - v.parse() - .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(v), &self)) + ser.serialize_str(&value.to_string()) } - } - deserializer.deserialize_str(StringToIntVisitor) - } + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<$t, D::Error> + where + D: Deserializer<'de>, + { + struct StringToIntVisitor; + + impl<'v> Visitor<'v> for StringToIntVisitor { + type Value = $t; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + formatter, + "a string which can be parsed as an unsigned, 64-bit integer" + ) + } + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse() + .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(v), &self)) + } + } - pub(crate) mod option { - use serde::{ - de::{self, Visitor}, - Deserializer, Serializer, - }; - - pub(crate) fn serialize(value: &Option, ser: S) -> Result - where - S: Serializer, - { - if let Some(value) = value { - ser.serialize_str(&value.to_string()) - } else { - ser.serialize_none() + deserializer.deserialize_str(StringToIntVisitor) } - } - pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - struct StringToIntVisitor; - - impl<'v> Visitor<'v> for StringToIntVisitor { - type Value = Option; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - formatter, - "a string which can be parsed as an unsigned, 64-bit integer" - ) - } - fn visit_str(self, v: &str) -> Result + pub(crate) mod option { + use serde::{ + de::{self, Visitor}, + Deserializer, Serializer, + }; + + pub(crate) fn serialize(value: &Option<$t>, ser: S) -> Result where - E: serde::de::Error, + S: Serializer, { - let v: u64 = v - .parse() - .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(v), &self))?; - Ok(Some(v)) + if let Some(value) = value { + ser.serialize_str(&value.to_string()) + } else { + ser.serialize_none() + } } - fn visit_none(self) -> Result + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where - E: de::Error, + D: Deserializer<'de>, { - Ok(None) + struct StringToIntVisitor; + + impl<'v> Visitor<'v> for StringToIntVisitor { + type Value = Option<$t>; + fn expecting( + &self, + formatter: &mut std::fmt::Formatter, + ) -> std::fmt::Result { + write!( + formatter, + "a string which can be parsed as an unsigned, 64-bit integer" + ) + } + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + let v: $t = v.parse().map_err(|_| { + de::Error::invalid_value(de::Unexpected::Str(v), &self) + })?; + Ok(Some(v)) + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + } + + deserializer.deserialize_any(StringToIntVisitor) } } - - deserializer.deserialize_any(StringToIntVisitor) } - } + }; +} + +pub(crate) mod string_to { + string_conversion!(u64); + string_conversion!(i64); } pub(crate) mod maybe_empty_url { diff --git a/entities/src/instance.rs b/entities/src/instance.rs index 95fb331..16d34d6 100644 --- a/entities/src/instance.rs +++ b/entities/src/instance.rs @@ -124,13 +124,13 @@ pub struct Activity { /// UNIX Timestamp at midnight at the first day of the week. pub week: String, /// The number of Statuses created since the week began (cast from an integer) - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub statuses: u64, /// The number of user logins since the week began (cast from an integer) - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub logins: u64, /// The number of user registrations since the week began (cast from an integer) - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub registrations: u64, } diff --git a/entities/src/status/scheduled.rs b/entities/src/status/scheduled.rs index 8bae30d..90673f2 100644 --- a/entities/src/status/scheduled.rs +++ b/entities/src/status/scheduled.rs @@ -59,7 +59,7 @@ pub struct Poll { /// The poll options to be used. pub options: Vec, /// How many seconds the poll should last before closing. - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub expires_in: u64, /// Whether the poll allows multiple choices. pub multiple: Option, diff --git a/entities/src/tag.rs b/entities/src/tag.rs index 30863b9..351ad01 100644 --- a/entities/src/tag.rs +++ b/entities/src/tag.rs @@ -26,10 +26,10 @@ pub struct History { #[serde(with = "conversion::date_from_timestamp")] pub day: Date, /// The counted usage of the tag within that day. - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub uses: u64, /// The total of accounts using the tag within that day. - #[serde(with = "conversion::string_to_u64")] + #[serde(with = "conversion::string_to::u64")] pub accounts: u64, } -- cgit v1.2.3