aws_smithy_compression/
lib.rs1#![cfg_attr(docsrs, feature(doc_cfg))]
8#![allow(clippy::derive_partial_eq_without_eq)]
10#![warn(
11 missing_docs,
12 rustdoc::missing_crate_level_docs,
13 unreachable_pub,
14 rust_2018_idioms
15)]
16
17use aws_smithy_runtime_api::box_error::BoxError;
20use aws_smithy_types::config_bag::{Storable, StoreReplace};
21use std::io::Write;
22use std::str::FromStr;
23
24pub mod body;
25mod gzip;
26pub mod http;
27
28pub const GZIP_NAME: &str = "gzip";
31
32const MAX_MIN_COMPRESSION_SIZE_BYTES: u32 = 10_485_760;
34
35pub trait Compress: Send + Sync {
41 fn compress_bytes(&mut self, bytes: &[u8], writer: &mut dyn Write) -> Result<(), BoxError>;
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49#[non_exhaustive]
50pub struct CompressionOptions {
51 level: u32,
53 min_compression_size_bytes: u32,
54 enabled: bool,
55}
56
57impl Default for CompressionOptions {
58 fn default() -> Self {
59 Self {
60 level: 6,
61 min_compression_size_bytes: 10240,
62 enabled: true,
63 }
64 }
65}
66
67impl CompressionOptions {
68 pub fn level(&self) -> u32 {
70 self.level
71 }
72
73 pub fn min_compression_size_bytes(&self) -> u32 {
77 self.min_compression_size_bytes
78 }
79
80 pub fn is_enabled(&self) -> bool {
82 self.enabled
83 }
84
85 pub fn with_enabled(self, enabled: bool) -> Self {
87 Self { enabled, ..self }
88 }
89
90 pub fn with_level(self, level: u32) -> Result<Self, BoxError> {
94 Self::validate_level(level)?;
95 Ok(Self { level, ..self })
96 }
97
98 pub fn with_min_compression_size_bytes(
103 self,
104 min_compression_size_bytes: u32,
105 ) -> Result<Self, BoxError> {
106 Self::validate_min_compression_size_bytes(min_compression_size_bytes)?;
107 Ok(Self {
108 min_compression_size_bytes,
109 ..self
110 })
111 }
112
113 fn validate_level(level: u32) -> Result<(), BoxError> {
114 if level > 9 {
115 return Err(format!(
116 "compression level `{}` is invalid, valid values are 0..=9",
117 level
118 )
119 .into());
120 };
121 Ok(())
122 }
123
124 fn validate_min_compression_size_bytes(
125 min_compression_size_bytes: u32,
126 ) -> Result<(), BoxError> {
127 if min_compression_size_bytes > MAX_MIN_COMPRESSION_SIZE_BYTES {
128 return Err(format!(
129 "min compression size `{}` is invalid, valid values are 0..=10_485_760",
130 min_compression_size_bytes
131 )
132 .into());
133 };
134 Ok(())
135 }
136}
137
138impl Storable for CompressionOptions {
139 type Storer = StoreReplace<Self>;
140}
141
142#[derive(Debug, Copy, Clone, PartialEq, Eq)]
144#[non_exhaustive]
145pub enum CompressionAlgorithm {
146 Gzip,
148}
149
150impl FromStr for CompressionAlgorithm {
151 type Err = BoxError;
152
153 fn from_str(compression_algorithm: &str) -> Result<Self, Self::Err> {
160 if compression_algorithm.eq_ignore_ascii_case(GZIP_NAME) {
161 Ok(Self::Gzip)
162 } else {
163 Err(format!("unknown compression algorithm `{compression_algorithm}`").into())
164 }
165 }
166}
167
168impl CompressionAlgorithm {
169 pub fn into_impl_http_body_1_x(
171 self,
172 options: &CompressionOptions,
173 ) -> Box<dyn http::CompressRequest> {
174 match self {
175 Self::Gzip => Box::new(gzip::Gzip::from(options)),
176 }
177 }
178
179 pub fn as_str(&self) -> &'static str {
181 match self {
182 Self::Gzip { .. } => GZIP_NAME,
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use crate::CompressionAlgorithm;
190 use pretty_assertions::assert_eq;
191
192 #[test]
193 fn test_compression_algorithm_from_str_unknown() {
194 let error = "some unknown compression algorithm"
195 .parse::<CompressionAlgorithm>()
196 .expect_err("it should error");
197 assert_eq!(
198 "unknown compression algorithm `some unknown compression algorithm`",
199 error.to_string()
200 );
201 }
202
203 #[test]
204 fn test_compression_algorithm_from_str_gzip() {
205 let algo = "gzip".parse::<CompressionAlgorithm>().unwrap();
206 assert_eq!("gzip", algo.as_str());
207 }
208}