xmtp_proto/types/
app_version.rs1use tonic::metadata::{Ascii, MetadataValue, errors::InvalidMetadataValue};
2
3#[derive(Clone, PartialEq, Eq)]
5pub struct AppVersion(String);
6
7impl From<String> for AppVersion {
8 fn from(value: String) -> Self {
9 AppVersion(value)
10 }
11}
12
13impl From<&str> for AppVersion {
14 fn from(value: &str) -> Self {
15 AppVersion(value.to_string())
16 }
17}
18
19impl From<&String> for AppVersion {
20 fn from(value: &String) -> Self {
21 AppVersion(value.to_string())
22 }
23}
24
25impl Default for AppVersion {
26 fn default() -> Self {
27 Self("0.0.0".to_string())
28 }
29}
30
31impl TryFrom<AppVersion> for MetadataValue<Ascii> {
32 type Error = InvalidMetadataValue;
33
34 fn try_from(value: AppVersion) -> Result<Self, Self::Error> {
35 MetadataValue::try_from(value.0)
36 }
37}
38
39impl TryFrom<&AppVersion> for MetadataValue<Ascii> {
40 type Error = InvalidMetadataValue;
41
42 fn try_from(value: &AppVersion) -> Result<Self, Self::Error> {
43 MetadataValue::try_from(&value.0)
44 }
45}
46
47impl std::fmt::Display for AppVersion {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 write!(f, "{}", self.0)
50 }
51}
52
53impl std::fmt::Debug for AppVersion {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_tuple("AppVersion").field(&self.0).finish()
56 }
57}
58
59impl PartialEq<String> for AppVersion {
60 fn eq(&self, other: &String) -> bool {
61 self.0 == *other
62 }
63}
64
65impl PartialEq<AppVersion> for String {
66 fn eq(&self, other: &AppVersion) -> bool {
67 other.0 == *self
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use rstest::rstest;
75 use tonic::metadata::MetadataValue;
76
77 #[rstest]
78 #[case("1.2.3")]
79 #[case("2.0.1")]
80 #[case("")]
81 #[case("v1.0.0")]
82 #[xmtp_common::test]
83 async fn test_from_conversions(#[case] version: &str) {
84 assert_eq!(AppVersion::from(version).to_string(), version);
85 assert_eq!(AppVersion::from(version.to_string()).to_string(), version);
86 }
87
88 #[rstest]
89 #[case("1.0.0", true)]
90 #[case("", true)]
91 #[case("1.0.0\u{1F600}", true)] #[xmtp_common::test]
93 async fn test_metadata_value_conversion(#[case] version: &str, #[case] should_succeed: bool) {
94 assert_eq!(
95 TryInto::<MetadataValue<Ascii>>::try_into(AppVersion::from(version).clone()).is_ok(),
96 should_succeed
97 );
98
99 if should_succeed && version.is_ascii() {
100 assert_eq!(
101 TryInto::<MetadataValue<Ascii>>::try_into(AppVersion::from(version).clone())
102 .unwrap()
103 .to_str()
104 .unwrap(),
105 version
106 );
107 }
108 }
109
110 #[rstest]
111 #[case("1.0.0-alpha")]
112 #[case("2.1.3-beta.1")]
113 #[case("3.0.0-rc.2+build.123")]
114 #[case("v1.2.3")]
115 #[case("1.0.0+build.1")]
116 #[xmtp_common::test]
117 async fn test_complex_versions(#[case] version: &str) {
118 assert_eq!(AppVersion::from(version).to_string(), version);
119 assert!(
120 TryInto::<MetadataValue<Ascii>>::try_into(&AppVersion::from(version)).is_ok(),
121 "Version {} should be valid ASCII",
122 version
123 );
124 }
125}