11// Copyright The OpenTelemetry Authors
22// SPDX-License-Identifier: Apache-2.0
33
4- //!
5- //! Defines a compression enum to abstract from tonic and allows the exporter and receiver to get the respective tonic equivalent
6- //!
4+ //! Defines a compression enum to abstract from tonic and allows the exporter and receiver to get
5+ //! the respective tonic equivalent.
76
8- use serde:: { Deserialize , Serialize } ;
7+ use serde:: { Deserialize , Deserializer , Serialize } ;
98use tonic:: codec:: CompressionEncoding ;
109
1110/// Enum to represent various compression methods
12- #[ derive( Debug , Clone , Serialize , Deserialize ) ]
11+ #[ derive( Debug , Clone , Copy , Serialize , Deserialize , PartialEq , Eq ) ]
1312#[ serde( rename_all = "snake_case" ) ]
1413pub enum CompressionMethod {
1514 /// Fastest compression
@@ -26,16 +25,73 @@ impl CompressionMethod {
2625 #[ must_use]
2726 pub const fn map_to_compression_encoding ( & self ) -> CompressionEncoding {
2827 match * self {
29- CompressionMethod :: Gzip => CompressionEncoding :: Gzip ,
3028 CompressionMethod :: Zstd => CompressionEncoding :: Zstd ,
29+ CompressionMethod :: Gzip => CompressionEncoding :: Gzip ,
3130 CompressionMethod :: Deflate => CompressionEncoding :: Deflate ,
3231 }
3332 }
3433}
3534
35+ /// Default set of compression methods that are accepted when no configuration is provided.
36+ pub const DEFAULT_COMPRESSION_METHODS : [ CompressionMethod ; 3 ] = [
37+ CompressionMethod :: Zstd ,
38+ CompressionMethod :: Gzip ,
39+ CompressionMethod :: Deflate ,
40+ ] ;
41+
42+ #[ derive( Deserialize ) ]
43+ #[ serde( untagged) ]
44+ enum CompressionConfigValue {
45+ Single ( CompressionMethod ) ,
46+ List ( Vec < CompressionMethod > ) ,
47+ NoneKeyword ( CompressionNone ) ,
48+ }
49+
50+ #[ derive( Deserialize ) ]
51+ #[ serde( rename_all = "snake_case" ) ]
52+ enum CompressionNone {
53+ None ,
54+ }
55+
56+ /// Deserializer that accepts either a single compression method, a list, or the string `"none"`.
57+ /// Absence of the field keeps the default behaviour (all methods).
58+ pub fn deserialize_compression_methods < ' de , D > (
59+ deserializer : D ,
60+ ) -> Result < Option < Vec < CompressionMethod > > , D :: Error >
61+ where
62+ D : Deserializer < ' de > ,
63+ {
64+ let value = Option :: < CompressionConfigValue > :: deserialize ( deserializer) ?;
65+ let Some ( value) = value else {
66+ return Ok ( None ) ;
67+ } ;
68+
69+ let methods = match value {
70+ CompressionConfigValue :: Single ( method) => vec ! [ method] ,
71+ CompressionConfigValue :: List ( methods) => methods,
72+ CompressionConfigValue :: NoneKeyword ( CompressionNone :: None ) => Vec :: new ( ) ,
73+ } ;
74+
75+ let mut deduped = Vec :: with_capacity ( methods. len ( ) ) ;
76+ for method in methods {
77+ if !deduped. contains ( & method) {
78+ deduped. push ( method) ;
79+ }
80+ }
81+
82+ Ok ( Some ( deduped) )
83+ }
84+
3685#[ cfg( test) ]
3786mod tests {
3887 use super :: * ;
88+ use serde:: Deserialize ;
89+
90+ #[ derive( Debug , Deserialize ) ]
91+ struct ConfWithCompression {
92+ #[ serde( default , deserialize_with = "deserialize_compression_methods" ) ]
93+ methods : Option < Vec < CompressionMethod > > ,
94+ }
3995
4096 #[ test]
4197 fn compression_method_accepts_snake_case_only ( ) {
@@ -52,4 +108,37 @@ mod tests {
52108 assert ! ( serde_json:: from_str:: <CompressionMethod >( "\" Zstd\" " ) . is_err( ) ) ;
53109 assert ! ( serde_json:: from_str:: <CompressionMethod >( "\" Deflate\" " ) . is_err( ) ) ;
54110 }
111+
112+ #[ test]
113+ fn deserialize_supports_single_value ( ) {
114+ let conf: ConfWithCompression = serde_json:: from_str ( r#"{ "methods": "gzip" }"# ) . unwrap ( ) ;
115+ assert_eq ! ( conf. methods, Some ( vec![ CompressionMethod :: Gzip ] ) ) ;
116+ }
117+
118+ #[ test]
119+ fn deserialize_supports_list ( ) {
120+ let conf: ConfWithCompression =
121+ serde_json:: from_str ( r#"{ "methods": ["gzip", "zstd", "gzip"] }"# ) . unwrap ( ) ;
122+ assert_eq ! (
123+ conf. methods,
124+ Some ( vec![ CompressionMethod :: Gzip , CompressionMethod :: Zstd ] )
125+ ) ;
126+ }
127+
128+ #[ test]
129+ fn deserialize_supports_none_keyword ( ) {
130+ let conf: ConfWithCompression = serde_json:: from_str ( r#"{ "methods": "none" }"# ) . unwrap ( ) ;
131+ assert_eq ! ( conf. methods, Some ( vec![ ] ) ) ;
132+ }
133+
134+ #[ test]
135+ fn deserialize_supports_absence ( ) {
136+ #[ derive( Debug , Deserialize ) ]
137+ struct Conf {
138+ #[ serde( default , deserialize_with = "deserialize_compression_methods" ) ]
139+ methods : Option < Vec < CompressionMethod > > ,
140+ }
141+ let conf: Conf = serde_json:: from_str ( "{}" ) . unwrap ( ) ;
142+ assert_eq ! ( conf. methods, None ) ;
143+ }
55144}
0 commit comments