winget_types/installer/
channel.rs1use core::{fmt, str::FromStr};
2
3use compact_str::CompactString;
4use thiserror::Error;
5
6#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(feature = "serde", serde(try_from = "CompactString"))]
9#[repr(transparent)]
10pub struct Channel(CompactString);
11
12#[derive(Debug, Error, Eq, PartialEq)]
13pub enum ChannelError {
14 #[error("Channel must not be empty")]
15 Empty,
16 #[error(
17 "Channel must not have more than {} characters but has {_0}",
18 Channel::MAX_CHAR_LENGTH
19 )]
20 TooLong(usize),
21}
22
23impl Channel {
24 pub const MAX_CHAR_LENGTH: usize = 16;
25
26 pub fn new<T: AsRef<str> + Into<CompactString>>(channel: T) -> Result<Self, ChannelError> {
47 let channel_str = channel.as_ref();
48
49 if channel_str.is_empty() {
50 return Err(ChannelError::Empty);
51 }
52
53 let char_count = channel_str.chars().count();
54 if char_count > Self::MAX_CHAR_LENGTH {
55 return Err(ChannelError::TooLong(char_count));
56 }
57
58 Ok(Self(channel.into()))
59 }
60
61 #[must_use]
68 #[inline]
69 pub unsafe fn new_unchecked<T: Into<CompactString>>(channel: T) -> Self {
70 Self(channel.into())
71 }
72
73 #[must_use]
75 #[inline]
76 pub fn as_str(&self) -> &str {
77 self.0.as_str()
78 }
79}
80
81impl AsRef<str> for Channel {
82 #[inline]
83 fn as_ref(&self) -> &str {
84 self.as_str()
85 }
86}
87
88impl fmt::Display for Channel {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 self.0.fmt(f)
91 }
92}
93
94impl FromStr for Channel {
95 type Err = ChannelError;
96
97 #[inline]
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
99 Self::new(s)
100 }
101}
102
103impl TryFrom<CompactString> for Channel {
104 type Error = ChannelError;
105
106 #[inline]
107 fn try_from(value: CompactString) -> Result<Self, Self::Error> {
108 Self::new(value)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use rstest::rstest;
115
116 use super::{Channel, ChannelError};
117
118 #[rstest]
119 #[case("stable")]
120 #[case("beta")]
121 #[case("dev")]
122 #[case("nightly")]
123 #[case("canary")]
124 fn valid_channel(#[case] channel: &str) {
125 assert!(channel.parse::<Channel>().is_ok());
126 }
127
128 #[test]
129 fn empty_channel() {
130 assert_eq!("".parse::<Channel>(), Err(ChannelError::Empty));
131 }
132
133 #[test]
134 fn channel_too_long() {
135 assert_eq!(
136 "frequent_nightly_builds".parse::<Channel>(),
137 Err(ChannelError::TooLong(23))
138 );
139 }
140}