memberlist_proto/compression/
from_str.rs1use core::str::FromStr;
2
3use super::CompressAlgorithm;
4
5#[derive(Debug, thiserror::Error)]
7#[error("invalid compress algorithm: {0}")]
8pub struct ParseCompressAlgorithmError(String);
9
10impl FromStr for CompressAlgorithm {
13 type Err = ParseCompressAlgorithmError;
14
15 fn from_str(s: &str) -> Result<Self, Self::Err> {
16 Ok(match s.trim() {
17 "lz4" | "Lz4" | "LZ4" => {
18 #[cfg(not(feature = "lz4"))]
19 return Err(ParseCompressAlgorithmError(
20 "feature `lz4` is disabled".to_string(),
21 ));
22
23 #[cfg(feature = "lz4")]
24 Self::Lz4
25 }
26 "snappy" | "Snappy" | "SNAPPY" | "snap" | "Snap" | "SNAP" => {
27 #[cfg(not(feature = "snappy"))]
28 return Err(ParseCompressAlgorithmError(
29 "feature `snappy` is disabled".to_string(),
30 ));
31
32 #[cfg(feature = "snappy")]
33 Self::Snappy
34 }
35 val if contains(&["unknown", "Unknown", "UNKNOWN"], val) => {
36 let val = strip(&["unknown", "Unknown", "UNKNOWN"], val)
37 .unwrap()
38 .trim_start_matches("(")
39 .trim_end_matches(")");
40 Self::Unknown(
41 val
42 .parse::<u8>()
43 .map_err(|_| ParseCompressAlgorithmError(val.to_string()))?,
44 )
45 }
46 val if contains(&["brotli", "Brotli", "BROTLI"], val) => {
47 #[cfg(not(feature = "brotli"))]
48 return Err(ParseCompressAlgorithmError(
49 "feature `brotli` is disabled".to_string(),
50 ));
51
52 #[cfg(feature = "brotli")]
53 {
54 let suffix = strip(&["brotli", "Brotli", "BROTLI"], val).unwrap();
55 let val = trim_parentheses(suffix).unwrap_or("");
56
57 if val.is_empty() {
58 Self::Brotli(Default::default())
59 } else {
60 Self::Brotli(
61 val
62 .parse()
63 .map_err(|_| ParseCompressAlgorithmError(val.to_string()))?,
64 )
65 }
66 }
67 }
68 val if contains(&["zstd", "Zstd", "ZSTD"], val) => {
69 #[cfg(not(feature = "zstd"))]
70 return Err(ParseCompressAlgorithmError(
71 "feature `zstd` is disabled".to_string(),
72 ));
73
74 #[cfg(feature = "zstd")]
75 {
76 let suffix = strip(&["zstd", "Zstd", "ZSTD"], val).unwrap();
77 let val = trim_parentheses(suffix).unwrap_or("");
78
79 if val.is_empty() {
80 Self::Zstd(Default::default())
81 } else {
82 Self::Zstd(
83 val
84 .parse()
85 .map_err(|_| ParseCompressAlgorithmError(val.to_string()))?,
86 )
87 }
88 }
89 }
90 val => return Err(ParseCompressAlgorithmError(val.to_string())),
91 })
92 }
93}
94
95#[inline]
96fn strip<'a>(possible_values: &'a [&'a str], s: &'a str) -> Option<&'a str> {
97 possible_values.iter().find_map(|&m| s.strip_prefix(m))
98}
99
100#[allow(unused)]
101#[inline]
102fn trim_parentheses(s: &str) -> Option<&str> {
103 s.strip_prefix('(').and_then(|s| s.strip_suffix(')'))
104}
105
106#[inline]
107fn contains<'a>(possible_values: &'a [&'a str], s: &'a str) -> bool {
108 possible_values.iter().any(|&m| s.strip_prefix(m).is_some())
109}
110
111#[cfg(test)]
112mod tests {
113 use crate::{BrotliAlgorithm, ZstdCompressionLevel};
114
115 use super::*;
116
117 #[test]
118 fn test_compress_algorithm_from_str() {
119 #[cfg(feature = "lz4")]
120 assert_eq!(
121 "lz4".parse::<CompressAlgorithm>().unwrap(),
122 CompressAlgorithm::Lz4
123 );
124 assert_eq!(
125 "unknown(33)".parse::<CompressAlgorithm>().unwrap(),
126 CompressAlgorithm::Unknown(33)
127 );
128 assert!("unknown".parse::<CompressAlgorithm>().is_err());
129 #[cfg(feature = "brotli")]
130 assert_eq!(
131 "brotli(11, 22)".parse::<CompressAlgorithm>().unwrap(),
132 CompressAlgorithm::Brotli(BrotliAlgorithm::with_quality_and_window(
133 11.into(),
134 22.into()
135 ))
136 );
137 #[cfg(feature = "brotli")]
138 assert_eq!(
139 "brotli".parse::<CompressAlgorithm>().unwrap(),
140 CompressAlgorithm::Brotli(BrotliAlgorithm::default())
141 );
142 #[cfg(feature = "brotli")]
143 assert_eq!(
144 "brotli()".parse::<CompressAlgorithm>().unwrap(),
145 CompressAlgorithm::Brotli(BrotliAlgorithm::default())
146 );
147 #[cfg(feature = "brotli")]
148 assert!("brotli(-)".parse::<CompressAlgorithm>().is_err());
149 #[cfg(feature = "zstd")]
150 assert_eq!(
151 "zstd(3)".parse::<CompressAlgorithm>().unwrap(),
152 CompressAlgorithm::Zstd(ZstdCompressionLevel::with_level(3))
153 );
154 #[cfg(feature = "zstd")]
155 assert_eq!(
156 "zstd".parse::<CompressAlgorithm>().unwrap(),
157 CompressAlgorithm::Zstd(ZstdCompressionLevel::default())
158 );
159 #[cfg(feature = "zstd")]
160 assert_eq!(
161 "zstd()".parse::<CompressAlgorithm>().unwrap(),
162 CompressAlgorithm::Zstd(ZstdCompressionLevel::default())
163 );
164 #[cfg(feature = "zstd")]
165 assert_eq!(
166 "zstd(-)".parse::<CompressAlgorithm>().unwrap(),
167 CompressAlgorithm::Zstd(ZstdCompressionLevel::new())
168 );
169 }
170}