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(
116 format!("compression level `{level}` is invalid, valid values are 0..=9").into(),
117 );
118 };
119 Ok(())
120 }
121
122 fn validate_min_compression_size_bytes(
123 min_compression_size_bytes: u32,
124 ) -> Result<(), BoxError> {
125 if min_compression_size_bytes > MAX_MIN_COMPRESSION_SIZE_BYTES {
126 return Err(format!(
127 "min compression size `{min_compression_size_bytes}` is invalid, valid values are 0..=10_485_760"
128 )
129 .into());
130 };
131 Ok(())
132 }
133}
134
135impl Storable for CompressionOptions {
136 type Storer = StoreReplace<Self>;
137}
138
139#[derive(Debug, Copy, Clone, PartialEq, Eq)]
141#[non_exhaustive]
142pub enum CompressionAlgorithm {
143 Gzip,
145}
146
147impl FromStr for CompressionAlgorithm {
148 type Err = BoxError;
149
150 fn from_str(compression_algorithm: &str) -> Result<Self, Self::Err> {
157 if compression_algorithm.eq_ignore_ascii_case(GZIP_NAME) {
158 Ok(Self::Gzip)
159 } else {
160 Err(format!("unknown compression algorithm `{compression_algorithm}`").into())
161 }
162 }
163}
164
165impl CompressionAlgorithm {
166 pub fn into_impl_http_body_1_x(
168 self,
169 options: &CompressionOptions,
170 ) -> Box<dyn http::CompressRequest> {
171 match self {
172 Self::Gzip => Box::new(gzip::Gzip::from(options)),
173 }
174 }
175
176 pub fn as_str(&self) -> &'static str {
178 match self {
179 Self::Gzip { .. } => GZIP_NAME,
180 }
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use crate::CompressionAlgorithm;
187 use pretty_assertions::assert_eq;
188
189 #[test]
190 fn test_compression_algorithm_from_str_unknown() {
191 let error = "some unknown compression algorithm"
192 .parse::<CompressionAlgorithm>()
193 .expect_err("it should error");
194 assert_eq!(
195 "unknown compression algorithm `some unknown compression algorithm`",
196 error.to_string()
197 );
198 }
199
200 #[test]
201 fn test_compression_algorithm_from_str_gzip() {
202 let algo = "gzip".parse::<CompressionAlgorithm>().unwrap();
203 assert_eq!("gzip", algo.as_str());
204 }
205}