clickhouse_arrow/
settings.rs

1/// Manages `ClickHouse` query settings for the native protocol.
2///
3/// This module provides types and methods to define, manipulate, and serialize
4/// `ClickHouse` query settings, which are key-value pairs sent with queries to
5/// configure server behavior (e.g., `max_threads`, `allow_experimental_features`).
6/// The [`Settings`] struct holds a collection of [`Setting`]s, each representing
7/// a key, value, and optional flags (`important`, `custom`). The [`SettingValue`]
8/// enum supports various data types (integers, booleans, floats, strings) with
9/// conversions from Rust primitives.
10///
11/// # Features
12/// - Converts Rust primitives (e.g., `i32`, `&str`, `bool`) to [`SettingValue`] using the
13///   `From` trait.
14/// - Serializes settings to the `ClickHouse` native protocol, supporting both legacy
15///   (pre-revision 54429) and modern formats.
16/// - Optional `serde` integration for serialization/deserialization (enabled with the `serde`
17///   feature).
18///
19/// # `ClickHouse` Documentation
20/// - For a list of available query settings, see the [ClickHouse Settings Reference](https://clickhouse.com/docs/en/operations/settings).
21/// - For details on the native protocol’s settings serialization, see the [ClickHouse Native Protocol Documentation](https://clickhouse.com/docs/en/interfaces/tcp).
22///
23/// # Example
24/// ```rust,ignore
25/// use clickhouse_arrow::query::settings::Settings;
26///
27/// // Create settings with key-value pairs
28/// let mut settings = Settings::from([
29///     ("max_threads".to_string(), 8_i32),
30///     ("allow_experimental_features".to_string(), true),
31/// ]);
32///
33/// // Add a setting
34/// settings.add_setting("max_execution_time", 300_i64);
35///
36/// // Convert to key-value strings
37/// let kv_pairs = settings.encode_to_key_value_strings();
38/// assert_eq!(kv_pairs, vec![
39///     ("max_threads".to_string(), "8".to_string()),
40///     ("allow_experimental_features".to_string(), "true".to_string()),
41///     ("max_execution_time".to_string(), "300".to_string()),
42/// ]);
43/// ```
44///
45/// # Notes
46/// - Settings are serialized according to the `ClickHouse` server’s protocol revision. For
47///   revisions ≤ 54429, only integer and boolean settings are supported.
48/// - The `serde` feature enables serialization/deserialization of [`Setting`] and [`Settings`]
49///   with `serde::Serialize` and `serde::Deserialize`.
50use std::fmt;
51
52use crate::io::{ClickHouseRead, ClickHouseWrite};
53use crate::native::protocol::DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS;
54use crate::{Error, Result};
55
56const SETTING_FLAG_IMPORTANT: u64 = 0x01;
57const SETTING_FLAG_CUSTOM: u64 = 0x02;
58
59/// Supported value types for `ClickHouse` query settings.
60///
61/// This enum represents the possible data types for a [`Setting`]'s value, including
62/// integers, booleans, floats, and strings. It implements `From` for various Rust
63/// primitive types (e.g., `i32`, `&str`, `f64`) to simplify setting creation.
64///
65/// # Variants
66/// - `Int(i64)`: A 64-bit integer (e.g., for `max_threads`).
67/// - `Bool(bool)`: A boolean (e.g., for `allow_experimental_features`).
68/// - `Float(f64)`: A 64-bit float (e.g., for `quantile`).
69/// - `String(String)`: A string (e.g., for `default_format`).
70///
71/// # Example
72/// ```rust,ignore
73/// use clickhouse_arrow::query::settings::SettingValue;
74///
75/// let int_value: SettingValue = 8_i32.into();
76/// let bool_value: SettingValue = true.into();
77/// let string_value: SettingValue = "JSON".to_string().into();
78/// assert!(matches!(int_value, SettingValue::Int(8)));
79/// ```
80#[derive(Debug, Clone, PartialEq, PartialOrd)]
81pub enum SettingValue {
82    Int(i64),
83    Bool(bool),
84    Float(f64),
85    String(String),
86}
87
88impl SettingValue {
89    // Helper to extract f64 from Float variant for testing
90    #[allow(unused)]
91    pub(crate) fn unwrap_float(&self) -> f64 {
92        match self {
93            SettingValue::Float(f) => *f,
94            _ => panic!("Expected Float variant"),
95        }
96    }
97}
98
99impl Eq for SettingValue {}
100
101macro_rules! setting_value {
102    ($ty:ident, $inner:ty) => {
103        impl From<$inner> for SettingValue {
104            fn from(value: $inner) -> Self { SettingValue::$ty(value) }
105        }
106    };
107    ($ty:ident, $inner:ty, $override:ty) => {
108        impl From<$override> for SettingValue {
109            #[allow(clippy::cast_lossless)]
110            #[allow(clippy::cast_possible_wrap)]
111            fn from(value: $override) -> Self { SettingValue::$ty(value as $inner) }
112        }
113    };
114    ($ty:ident, $inner:ty, $v:tt =>  { $override:expr }) => {
115        impl From<$inner> for SettingValue {
116            fn from($v: $inner) -> Self { SettingValue::$ty($override) }
117        }
118    };
119}
120
121setting_value!(Int, i64, u8);
122setting_value!(Int, i64, u16);
123setting_value!(Int, i64, u32);
124setting_value!(Int, i64, u64);
125setting_value!(Int, i64, i8);
126setting_value!(Int, i64, i16);
127setting_value!(Int, i64, i32);
128setting_value!(Int, i64);
129setting_value!(Bool, bool);
130setting_value!(Float, f64, f32);
131setting_value!(Float, f64);
132setting_value!(String, &str, v => { v.to_string() });
133setting_value!(String, Box<str>, v => { v.to_string() });
134setting_value!(String, std::sync::Arc<str>, v => { v.to_string() });
135setting_value!(String, String);
136
137impl fmt::Display for SettingValue {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        match self {
140            SettingValue::Int(i) => write!(f, "{i}"),
141            SettingValue::Bool(b) => write!(f, "{b}"),
142            SettingValue::Float(fl) => write!(f, "{fl}"),
143            SettingValue::String(s) => write!(f, "{s}"),
144        }
145    }
146}
147
148/// A single `ClickHouse` query setting, consisting of a key, value, and flags.
149///
150/// A setting represents a key-value pair sent to the `ClickHouse` server to
151/// configure query execution. The `key` is a string (e.g., `max_threads`), and
152/// the `value` is a [`SettingValue`] (integer, boolean, float, or string). The
153/// `important` and `custom` flags control serialization behavior in the native
154/// protocol.
155///
156/// # Fields
157/// - `key`: The setting name (e.g., `max_threads`).
158/// - `value`: The setting value, stored as a [`SettingValue`].
159/// - `important`: If `true`, marks the setting as important (affects serialization).
160/// - `custom`: If `true`, serializes the value as a custom string (e.g., for complex types).
161///
162/// # `ClickHouse` Reference
163/// See the [ClickHouse Settings Reference](https://clickhouse.com/docs/en/operations/settings)
164/// for valid setting names and their types.
165#[derive(Debug, Clone, PartialEq, PartialOrd)]
166#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
167pub struct Setting {
168    key:       String,
169    value:     SettingValue,
170    important: bool,
171    custom:    bool,
172}
173
174impl Setting {
175    /// Encodes the setting to the `ClickHouse` native protocol.
176    ///
177    /// For legacy revisions (≤ 54429), only integer and boolean settings are supported,
178    /// and attempting to encode a string or float will return an error. For modern revisions,
179    /// all setting types are supported, with strings optionally encoded as custom fields if
180    /// `custom` is `true`.
181    ///
182    /// # Arguments
183    /// - `writer`: The writer to serialize the setting to.
184    /// - `revision`: The `ClickHouse` server protocol revision.
185    ///
186    /// # Errors
187    /// Returns `Err(Error::UnsupportedSettingType)` if the setting value is a
188    /// string or float in legacy revisions.
189    async fn encode<W: ClickHouseWrite>(&self, writer: &mut W, revision: u64) -> Result<()> {
190        tracing::trace!(setting = ?self, "Writing setting");
191
192        if revision <= DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS {
193            if !matches!(self.value, SettingValue::Int(_) | SettingValue::Bool(_)) {
194                return Err(Error::UnsupportedSettingType(self.key.clone()));
195            }
196
197            // Write key
198            writer.write_string(&self.key).await?;
199
200            // Write value
201            #[expect(clippy::cast_sign_loss)]
202            match &self.value {
203                SettingValue::Int(i) => writer.write_var_uint(*i as u64).await?,
204                SettingValue::Bool(b) => writer.write_var_uint(u64::from(*b)).await?,
205                _ => unreachable!("Checked above"),
206            }
207        } else {
208            // Write key
209            writer.write_string(&self.key).await?;
210
211            // Write flags
212            let mut flags = 0u64;
213            if self.important {
214                flags |= SETTING_FLAG_IMPORTANT;
215            }
216            if self.custom {
217                flags |= SETTING_FLAG_CUSTOM;
218            }
219            writer.write_var_uint(flags).await?;
220
221            // Write value
222            if self.custom {
223                let field_dump = self.encode_field_dump()?;
224                writer.write_string(&field_dump).await?;
225            } else {
226                writer.write_string(self.value.to_string()).await?;
227            }
228        }
229
230        Ok(())
231    }
232
233    /// Decodes a setting from the `ClickHouse` native protocol.
234    ///
235    /// For legacy revisions (≤ 54429), only integer and boolean settings are supported.
236    /// For modern revisions, all setting types are supported, with custom settings
237    /// decoded from field dumps when the custom flag is set.
238    ///
239    /// # Arguments
240    /// - `reader`: The reader to deserialize the setting from.
241    /// - `revision`: The `ClickHouse` server protocol revision.
242    ///
243    /// # Errors
244    /// Returns an error if the data cannot be read or parsed correctly.
245    async fn decode<R: ClickHouseRead>(reader: &mut R, key: String) -> Result<Self> {
246        // Read flags (for STRINGS_WITH_FLAGS format)
247        let flags = reader.read_var_uint().await?;
248        let is_important = (flags & SETTING_FLAG_IMPORTANT) != 0;
249        let is_custom = (flags & SETTING_FLAG_CUSTOM) != 0;
250
251        // Read value based on whether it's custom or not
252        let value = if is_custom {
253            // Custom setting: read as field dump
254            let field_dump = reader.read_string().await?;
255            SettingValue::String(String::from_utf8_lossy(&field_dump).to_string())
256        } else {
257            // Standard setting: read as string and parse
258            let value_str = reader.read_string().await?;
259            Self::parse_setting_value(&String::from_utf8_lossy(&value_str))
260        };
261
262        Ok(Setting { key, value, important: is_important, custom: is_custom })
263    }
264
265    /// Encodes the setting value as a string for custom settings.
266    ///
267    /// For string values, the result is the raw string without additional escaping
268    /// (e.g., `"val'ue"` remains `"val'ue"`). Non-string values return an error.
269    ///
270    /// # Errors
271    /// Returns `Err(Error::UnsupportedFieldType)` if the value is not a string.
272    fn encode_field_dump(&self) -> Result<String> {
273        match &self.value {
274            SettingValue::String(s) => Ok(s.clone()),
275            _ => Err(Error::UnsupportedFieldType(format!("{:?}", self.value))),
276        }
277    }
278
279    /// Parses a setting value from its string representation.
280    ///
281    /// Attempts to parse the string as different types in order:
282    /// 1. Boolean (true/false, 1/0)
283    /// 2. Integer
284    /// 3. Float
285    /// 4. String (fallback)
286    ///
287    /// # Arguments
288    /// - `value_str`: The string representation of the setting value.
289    fn parse_setting_value(value_str: &str) -> SettingValue {
290        // Try parsing as boolean first
291        match value_str.to_lowercase().as_str() {
292            "true" | "1" => return SettingValue::Bool(true),
293            "false" | "0" => return SettingValue::Bool(false),
294            _ => {}
295        }
296
297        // Try parsing as integer
298        if let Ok(int_val) = value_str.parse::<i64>() {
299            return SettingValue::Int(int_val);
300        }
301
302        // Try parsing as float
303        if let Ok(float_val) = value_str.parse::<f64>() {
304            return SettingValue::Float(float_val);
305        }
306
307        // Default to string
308        SettingValue::String(value_str.to_string())
309    }
310}
311
312impl<T: Into<String>, U: Into<SettingValue>> From<(T, U)> for Setting {
313    fn from(value: (T, U)) -> Self {
314        Setting {
315            key:       value.0.into(),
316            value:     value.1.into(),
317            important: false,
318            custom:    false,
319        }
320    }
321}
322
323/// A collection of `ClickHouse` query settings.
324///
325/// This struct holds a list of [`Setting`]s and provides methods to add settings,
326/// convert them to strings, and serialize them to the `ClickHouse` native protocol.
327/// It implements `From` for iterators of key-value pairs and `Deref` to access
328/// the underlying settings as a slice.
329///
330/// # Example
331/// ```rust,ignore
332/// use clickhouse_arrow::query::settings::Settings;
333///
334/// let mut settings = Settings::default();
335/// settings.add_setting("max_threads", 8_i32);
336/// settings.add_setting("default_format", "JSON");
337///
338/// let strings = settings.encode_to_strings();
339/// assert_eq!(strings, vec!["max_threads = 8", "default_format = JSON"]);
340/// ```
341///
342/// # Serialization
343/// Settings are serialized according to the `ClickHouse` native protocol. For
344/// revisions ≤ 54429, only integer and boolean settings are supported. For newer
345/// revisions, all setting types are serialized as strings with optional flags.
346///
347/// # `ClickHouse` Reference
348/// See the [ClickHouse Native Protocol Documentation](https://clickhouse.com/docs/en/interfaces/tcp)
349/// for details on settings serialization.
350#[derive(Debug, Clone, Default, PartialEq, PartialOrd)]
351#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
352pub struct Settings(pub Vec<Setting>);
353
354impl Settings {
355    /// Adds a new setting with the given name and value.
356    ///
357    /// The value is converted to a [`SettingValue`] using the `From` trait. The
358    /// setting is marked as neither `important` nor `custom`.
359    ///
360    /// # Arguments
361    /// - `name`: The setting name (e.g., `max_threads`).
362    /// - `setting`: The setting value (e.g., `8_i32`, `true`, `"JSON"`).
363    ///
364    /// # Example
365    /// ```rust,ignore
366    /// use clickhouse_arrow::query::settings::Settings;
367    ///
368    /// let mut settings = Settings::default();
369    /// settings.add_setting("max_threads", 8_i32);
370    /// assert_eq!(settings.0.len(), 1);
371    /// ```
372    pub fn add_setting<S>(&mut self, name: impl Into<String>, setting: S)
373    where
374        SettingValue: From<S>,
375    {
376        let key = name.into();
377        if let Some(current) = self.0.iter_mut().find(|s| s.key == key) {
378            current.value = setting.into();
379        } else {
380            self.0.push(Setting { key, value: setting.into(), important: false, custom: false });
381        }
382    }
383
384    /// Return new settings with the given name and value added.
385    ///
386    /// The value is converted to a [`SettingValue`] using the `From` trait. The
387    /// setting is marked as neither `important` nor `custom`.
388    ///
389    /// # Arguments
390    /// - `name`: The setting name (e.g., `max_threads`).
391    /// - `setting`: The setting value (e.g., `8_i32`, `true`, `"JSON"`).
392    ///
393    /// # Example
394    /// ```rust,ignore
395    /// use clickhouse_arrow::query::settings::Settings;
396    ///
397    /// let mut settings = Settings::default();
398    /// settings.add_setting("max_threads", 8_i32);
399    /// assert_eq!(settings.0.len(), 1);
400    /// ```
401    #[must_use]
402    pub fn with_setting<S>(mut self, name: impl Into<String>, setting: S) -> Self
403    where
404        SettingValue: From<S>,
405    {
406        let key = name.into();
407        if let Some(current) = self.0.iter_mut().find(|s| s.key == key) {
408            current.value = setting.into();
409        } else {
410            self.0.push(Setting { key, value: setting.into(), important: false, custom: false });
411        }
412        self
413    }
414
415    /// Converts settings to a vector of key-value string pairs.
416    ///
417    /// Each setting is represented as a tuple of `(key, value.to_string())`.
418    ///
419    /// # Example
420    /// ```rust,ignore
421    /// use clickhouse_arrow::query::settings::Settings;
422    ///
423    /// let settings = Settings::from([("max_threads".to_string(), 8_i32)]);
424    /// let kv_pairs = settings.encode_to_key_value_strings();
425    /// assert_eq!(kv_pairs, vec![("max_threads".to_string(), "8".to_string())]);
426    /// ```
427    pub fn encode_to_key_value_strings(&self) -> Vec<(String, String)> {
428        self.0
429            .iter()
430            .map(|setting| (setting.key.to_string(), setting.value.to_string()))
431            .collect()
432    }
433
434    /// Converts settings to a vector of formatted strings.
435    ///
436    /// Each setting is formatted as `key = value`.
437    ///
438    /// # Example
439    /// ```rust,ignore
440    /// use clickhouse_arrow::query::settings::Settings;
441    ///
442    /// let settings = Settings::from([("max_threads".to_string(), 8_i32)]);
443    /// let strings = settings.encode_to_strings();
444    /// assert_eq!(strings, vec!["max_threads = 8"]);
445    /// ```
446    pub fn encode_to_strings(&self) -> Vec<String> {
447        self.0.iter().map(|setting| format!("{} = {}", setting.key, setting.value)).collect()
448    }
449
450    // TODO: Remove - docs
451    pub(crate) async fn encode<W: ClickHouseWrite>(
452        &self,
453        writer: &mut W,
454        revision: u64,
455    ) -> Result<()> {
456        for setting in &self.0 {
457            setting.encode(writer, revision).await?;
458        }
459        Ok(())
460    }
461
462    // TODO: Remove - docs
463    pub(crate) async fn encode_with_ignore<W: ClickHouseWrite>(
464        &self,
465        writer: &mut W,
466        revision: u64,
467        ignore: &Settings,
468    ) -> Result<()> {
469        for setting in &self.0 {
470            if ignore.get(&setting.key).is_some_and(|s| s.value == setting.value) {
471                continue;
472            }
473
474            setting.encode(writer, revision).await?;
475        }
476        Ok(())
477    }
478
479    /// Decodes a collection of settings from the `ClickHouse` native protocol.
480    ///
481    /// Based on `BaseSettings<TTraits>::read()` from cpp source, the format is:
482    /// 1. Loop reading setting name strings
483    /// 2. Empty string marks end of settings
484    /// 3. For each setting: name -> flags (if `STRINGS_WITH_FLAGS`) -> value
485    ///
486    /// # Arguments
487    /// - `reader`: The reader to deserialize the settings from.
488    /// - `revision`: The `ClickHouse` server protocol revision.
489    ///
490    /// # Errors
491    /// Returns an error if the data cannot be read or parsed correctly.
492    pub(crate) async fn decode<R: ClickHouseRead>(reader: &mut R) -> Result<Self> {
493        let mut settings = Vec::new();
494        loop {
495            // Read setting name
496            let key = reader.read_string().await?;
497            // Empty string marks end of settings
498            if key.is_empty() {
499                break;
500            }
501            settings
502                .push(Setting::decode(reader, String::from_utf8_lossy(&key).to_string()).await?);
503        }
504        Ok(Settings(settings))
505    }
506
507    /// Internal helper to find a specific settings
508    pub(crate) fn get(&self, key: &str) -> Option<&Setting> { self.0.iter().find(|s| s.key == key) }
509}
510
511impl<T, K, S> From<T> for Settings
512where
513    T: IntoIterator<Item = (K, S)>,
514    K: Into<String>,
515    SettingValue: From<S>,
516{
517    fn from(value: T) -> Self {
518        Self(
519            value
520                .into_iter()
521                .map(|(k, v)| Setting {
522                    key:       k.into(),
523                    value:     v.into(),
524                    important: false,
525                    custom:    false,
526                })
527                .collect(),
528        )
529    }
530}
531
532impl<K, S> FromIterator<(K, S)> for Settings
533where
534    K: Into<String>,
535    SettingValue: From<S>,
536{
537    fn from_iter<T>(iter: T) -> Self
538    where
539        T: IntoIterator<Item = (K, S)>,
540    {
541        iter.into_iter().collect()
542    }
543}
544
545impl std::ops::Deref for Settings {
546    type Target = [Setting];
547
548    fn deref(&self) -> &Self::Target { &self.0 }
549}
550
551#[cfg(feature = "serde")]
552pub mod deser {
553    use serde::{Deserialize, Serialize};
554
555    use super::*;
556
557    impl Serialize for SettingValue {
558        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
559        where
560            S: serde::Serializer,
561        {
562            match self {
563                SettingValue::Int(i) => ::serde::Serialize::serialize(i, serializer),
564                SettingValue::Bool(b) => ::serde::Serialize::serialize(b, serializer),
565                SettingValue::Float(f) => ::serde::Serialize::serialize(f, serializer),
566                SettingValue::String(s) => ::serde::Serialize::serialize(s, serializer),
567            }
568        }
569    }
570
571    impl<'de> Deserialize<'de> for SettingValue {
572        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
573        where
574            D: serde::Deserializer<'de>,
575        {
576            fn deserialize_setting<'d, De>(deserializer: De) -> Result<SettingValue, De::Error>
577            where
578                De: serde::Deserializer<'d>,
579            {
580                use serde::de::Visitor;
581
582                struct SettingVisitor;
583
584                type Result<E> = std::result::Result<SettingValue, E>;
585
586                impl Visitor<'_> for SettingVisitor {
587                    type Value = SettingValue;
588
589                    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
590                        formatter.write_str("a number, float or string")
591                    }
592
593                    fn visit_bool<E>(self, value: bool) -> Result<E>
594                    where
595                        E: serde::de::Error,
596                    {
597                        Ok(value.into())
598                    }
599
600                    fn visit_u8<E>(self, value: u8) -> Result<E>
601                    where
602                        E: serde::de::Error,
603                    {
604                        Ok(value.into())
605                    }
606
607                    fn visit_u16<E>(self, value: u16) -> Result<E>
608                    where
609                        E: serde::de::Error,
610                    {
611                        Ok(value.into())
612                    }
613
614                    fn visit_u32<E>(self, value: u32) -> Result<E>
615                    where
616                        E: serde::de::Error,
617                    {
618                        Ok(value.into())
619                    }
620
621                    fn visit_u64<E>(self, value: u64) -> Result<E>
622                    where
623                        E: serde::de::Error,
624                    {
625                        Ok(value.into())
626                    }
627
628                    fn visit_i8<E>(self, value: i8) -> Result<E>
629                    where
630                        E: serde::de::Error,
631                    {
632                        Ok(value.into())
633                    }
634
635                    fn visit_i16<E>(self, value: i16) -> Result<E>
636                    where
637                        E: serde::de::Error,
638                    {
639                        Ok(value.into())
640                    }
641
642                    fn visit_i32<E>(self, value: i32) -> Result<E>
643                    where
644                        E: serde::de::Error,
645                    {
646                        Ok(value.into())
647                    }
648
649                    fn visit_i64<E>(self, value: i64) -> Result<E>
650                    where
651                        E: serde::de::Error,
652                    {
653                        Ok(value.into())
654                    }
655
656                    fn visit_f32<E>(self, value: f32) -> Result<E>
657                    where
658                        E: serde::de::Error,
659                    {
660                        Ok(value.into())
661                    }
662
663                    fn visit_f64<E>(self, value: f64) -> Result<E>
664                    where
665                        E: serde::de::Error,
666                    {
667                        Ok(value.into())
668                    }
669
670                    fn visit_str<E>(self, value: &str) -> Result<E>
671                    where
672                        E: serde::de::Error,
673                    {
674                        Ok(value.into())
675                    }
676
677                    fn visit_string<E>(self, value: String) -> Result<E>
678                    where
679                        E: serde::de::Error,
680                    {
681                        self.visit_str(&value)
682                    }
683                }
684
685                deserializer.deserialize_any(SettingVisitor)
686            }
687            deserialize_setting(deserializer)
688        }
689    }
690
691    #[cfg(test)]
692    mod tests {
693        use super::*;
694
695        #[test]
696        fn test_setting_value_serialize_deserialize() {
697            // Test serialization and deserialization for all SettingValue variants
698            let values = vec![
699                SettingValue::Int(42),
700                SettingValue::Bool(true),
701                SettingValue::Float(3.15),
702                SettingValue::String("test".to_string()),
703            ];
704
705            for value in values {
706                // Serialize
707                let json = serde_json::to_string(&value).unwrap();
708
709                // Deserialize
710                let deserialized: SettingValue = serde_json::from_str(&json).unwrap();
711
712                // Verify round-trip
713                match (value, deserialized) {
714                    (SettingValue::Int(a), SettingValue::Int(b)) => assert_eq!(a, b),
715                    (SettingValue::Bool(a), SettingValue::Bool(b)) => assert_eq!(a, b),
716                    (SettingValue::Float(a), SettingValue::Float(b)) => {
717                        assert!((a - b).abs() < 1e-6);
718                    }
719                    (SettingValue::String(a), SettingValue::String(b)) => assert_eq!(a, b),
720                    _ => panic!("Mismatched variants"),
721                }
722            }
723        }
724
725        #[test]
726        fn test_setting_value_deserialize_variants() {
727            // Test deserialization from various JSON inputs
728            assert_eq!(serde_json::from_str::<SettingValue>("42").unwrap(), SettingValue::Int(42));
729            assert_eq!(
730                serde_json::from_str::<SettingValue>("true").unwrap(),
731                SettingValue::Bool(true)
732            );
733            assert!(
734                (serde_json::from_str::<SettingValue>("3.15").unwrap().unwrap_float() - 3.15).abs()
735                    < 1e-6
736            );
737            assert_eq!(
738                serde_json::from_str::<SettingValue>("\"test\"").unwrap(),
739                SettingValue::String("test".to_string())
740            );
741
742            // Test integer variants
743            assert_eq!(
744                serde_json::from_str::<SettingValue>("255").unwrap(),
745                SettingValue::Int(255)
746            ); // u8
747            assert_eq!(
748                serde_json::from_str::<SettingValue>("65535").unwrap(),
749                SettingValue::Int(65535)
750            ); // u16
751            assert_eq!(
752                serde_json::from_str::<SettingValue>("4294967295").unwrap(),
753                SettingValue::Int(4_294_967_295)
754            ); // u32
755            assert_eq!(
756                serde_json::from_str::<SettingValue>("-128").unwrap(),
757                SettingValue::Int(-128)
758            ); // i8
759            assert_eq!(
760                serde_json::from_str::<SettingValue>("-32768").unwrap(),
761                SettingValue::Int(-32768)
762            ); // i16
763            assert_eq!(
764                serde_json::from_str::<SettingValue>("-2147483648").unwrap(),
765                SettingValue::Int(-2_147_483_648)
766            ); // i32
767        }
768
769        #[test]
770        fn test_setting_value_deserialize_invalid() {
771            // Test deserialization of invalid JSON
772            assert!(serde_json::from_str::<SettingValue>("null").is_err());
773            assert!(serde_json::from_str::<SettingValue>("[]").is_err());
774            assert!(serde_json::from_str::<SettingValue>("{}").is_err());
775        }
776    }
777}
778
779#[cfg(test)]
780mod tests {
781    use std::io::Cursor;
782
783    use tokio::io::AsyncWriteExt;
784
785    use super::*;
786    use crate::io::ClickHouseRead;
787
788    type MockWriter = Cursor<Vec<u8>>;
789
790    // Helper to create a Setting
791    fn create_setting<S>(key: &str, value: S, important: bool, custom: bool) -> Setting
792    where
793        SettingValue: From<S>,
794    {
795        Setting { key: key.to_string(), value: value.into(), important, custom }
796    }
797
798    #[test]
799    fn test_setting_value_from_primitives() {
800        // Test all supported From implementations for SettingValue
801        assert_eq!(SettingValue::from(8_i8), SettingValue::Int(8));
802        assert_eq!(SettingValue::from(8_i16), SettingValue::Int(8));
803        assert_eq!(SettingValue::from(8_i32), SettingValue::Int(8));
804        assert_eq!(SettingValue::from(8_i64), SettingValue::Int(8));
805        assert_eq!(SettingValue::from(8_u8), SettingValue::Int(8));
806        assert_eq!(SettingValue::from(8_u16), SettingValue::Int(8));
807        assert_eq!(SettingValue::from(8_u32), SettingValue::Int(8));
808        assert_eq!(SettingValue::from(8_u64), SettingValue::Int(8));
809        assert_eq!(SettingValue::from(true), SettingValue::Bool(true));
810        assert!((SettingValue::from(3.15_f32).unwrap_float() - 3.15).abs() < 1e-6);
811        assert_eq!(SettingValue::from(3.15_f64), SettingValue::Float(3.15));
812        assert_eq!(SettingValue::from("test"), SettingValue::String("test".to_string()));
813        assert_eq!(
814            SettingValue::from("test".to_string()),
815            SettingValue::String("test".to_string())
816        );
817        assert_eq!(
818            SettingValue::from(Box::<str>::from("test")),
819            SettingValue::String("test".to_string())
820        );
821        assert_eq!(
822            SettingValue::from(std::sync::Arc::<str>::from("test")),
823            SettingValue::String("test".to_string())
824        );
825    }
826
827    #[test]
828    fn test_setting_value_display() {
829        // Test Display implementation for SettingValue
830        assert_eq!(SettingValue::Int(42).to_string(), "42");
831        assert_eq!(SettingValue::Bool(true).to_string(), "true");
832        assert_eq!(SettingValue::Float(3.15).to_string(), "3.15");
833        assert_eq!(SettingValue::String("test".to_string()).to_string(), "test");
834    }
835
836    #[test]
837    fn test_setting_encode_field_dump() {
838        // Test encode_field_dump for string values
839        let setting = create_setting("key", "value", false, true);
840        assert_eq!(setting.encode_field_dump().unwrap(), "value");
841
842        // Test string with quotes
843        let setting = create_setting("key", "val'ue", false, true);
844        assert_eq!(setting.encode_field_dump().unwrap(), "val'ue");
845
846        // Test non-string value (should error)
847        let setting = create_setting("key", 42_i32, false, true);
848        assert!(setting.encode_field_dump().is_err());
849    }
850
851    #[tokio::test]
852    async fn test_setting_encode_legacy_revision() {
853        // Test encode for legacy revision (≤ DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
854        let setting = create_setting("max_threads", 8_i32, false, false);
855        let mut writer = MockWriter::default();
856        setting
857            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
858            .await
859            .unwrap();
860        writer.flush().await.unwrap();
861
862        // Decode and verify using ClickHouseRead
863        let mut reader = Cursor::new(writer.into_inner());
864        let key = reader.read_utf8_string().await.unwrap();
865        assert_eq!(key, "max_threads");
866        let value = reader.read_var_uint().await.unwrap();
867        assert_eq!(value, 8);
868
869        // Test boolean setting
870        let setting = create_setting("allow_experimental", true, false, false);
871        let mut writer = MockWriter::default();
872        setting
873            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
874            .await
875            .unwrap();
876        writer.flush().await.unwrap();
877
878        let mut reader = Cursor::new(writer.into_inner());
879        let key = reader.read_utf8_string().await.unwrap();
880        assert_eq!(key, "allow_experimental");
881        let value = reader.read_var_uint().await.unwrap();
882        assert_eq!(value, 1);
883
884        // Test unsupported type (should error)
885        let setting = create_setting("default_format", "JSON", false, false);
886        let mut writer = MockWriter::default();
887        assert!(matches!(
888            setting
889                .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
890                .await,
891            Err(Error::UnsupportedSettingType(key)) if key == "default_format"
892        ));
893    }
894
895    #[tokio::test]
896    async fn test_setting_encode_modern_revision() {
897        // Test encode for modern revision (> DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
898        let setting = create_setting("max_threads", 8_i32, false, false);
899        let mut writer = MockWriter::default();
900        setting
901            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
902            .await
903            .unwrap();
904        writer.flush().await.unwrap();
905
906        // Decode and verify using ClickHouseRead
907        let mut reader = Cursor::new(writer.into_inner());
908        let key = reader.read_utf8_string().await.unwrap();
909        assert_eq!(key, "max_threads");
910        let flags = reader.read_var_uint().await.unwrap();
911        assert_eq!(flags, 0);
912        let value = reader.read_utf8_string().await.unwrap();
913        assert_eq!(value, "8");
914
915        // Test with important and custom flags
916        let setting = create_setting("custom_key", "value", true, true);
917        let mut writer = MockWriter::default();
918        setting
919            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
920            .await
921            .unwrap();
922        writer.flush().await.unwrap();
923
924        let mut reader = Cursor::new(writer.into_inner());
925        let key = reader.read_utf8_string().await.unwrap();
926        assert_eq!(key, "custom_key");
927        let flags = reader.read_var_uint().await.unwrap();
928        assert_eq!(flags, SETTING_FLAG_IMPORTANT | SETTING_FLAG_CUSTOM);
929        let value = reader.read_utf8_string().await.unwrap();
930        assert_eq!(value, "value");
931    }
932
933    #[test]
934    fn test_settings_add_setting() {
935        let mut settings = Settings::default();
936        settings.add_setting("max_threads", 8_i32);
937        settings.add_setting("default_format", "JSON");
938
939        assert_eq!(settings.0.len(), 2);
940        assert_eq!(settings.0[0].key, "max_threads");
941        assert_eq!(settings.0[0].value, SettingValue::Int(8));
942        assert_eq!(settings.0[1].key, "default_format");
943        assert_eq!(settings.0[1].value, SettingValue::String("JSON".to_string()));
944    }
945
946    #[test]
947    fn test_settings_from_iterator() {
948        let settings = Settings::from(vec![
949            ("max_threads".to_string(), SettingValue::Int(8)),
950            ("allow_experimental".to_string(), SettingValue::Bool(true)),
951        ]);
952
953        assert_eq!(settings.0.len(), 2);
954        assert_eq!(settings.0[0].key, "max_threads");
955        assert_eq!(settings.0[0].value, SettingValue::Int(8));
956        assert_eq!(settings.0[1].key, "allow_experimental");
957        assert_eq!(settings.0[1].value, SettingValue::Bool(true));
958    }
959
960    #[test]
961    fn test_settings_encode_to_key_value_strings() {
962        let settings = Settings::from(vec![
963            ("max_threads".to_string(), SettingValue::Int(8)),
964            ("default_format".to_string(), "JSON".into()),
965        ]);
966
967        let kv_pairs = settings.encode_to_key_value_strings();
968        assert_eq!(kv_pairs, vec![
969            ("max_threads".to_string(), "8".to_string()),
970            ("default_format".to_string(), "JSON".to_string()),
971        ]);
972    }
973
974    #[test]
975    fn test_settings_encode_to_strings() {
976        let settings = Settings::from(vec![
977            ("max_threads".to_string(), SettingValue::Int(8)),
978            ("default_format".to_string(), "JSON".into()),
979        ]);
980
981        let strings = settings.encode_to_strings();
982        assert_eq!(strings, vec!["max_threads = 8", "default_format = JSON"]);
983    }
984
985    #[tokio::test]
986    async fn test_settings_encode() {
987        let settings = Settings::from(vec![
988            ("max_threads".to_string(), SettingValue::Int(8)),
989            ("allow_experimental".to_string(), SettingValue::Bool(true)),
990        ]);
991
992        let mut writer = MockWriter::default();
993        settings
994            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
995            .await
996            .unwrap();
997        writer.write_string("").await.unwrap();
998        writer.flush().await.unwrap();
999
1000        // Decode and verify using ClickHouseRead
1001        let mut reader = Cursor::new(writer.into_inner());
1002        let key1 = reader.read_utf8_string().await.unwrap();
1003        assert_eq!(key1, "max_threads");
1004        let flags1 = reader.read_var_uint().await.unwrap();
1005        assert_eq!(flags1, 0);
1006        let value1 = reader.read_utf8_string().await.unwrap();
1007        assert_eq!(value1, "8");
1008
1009        let key2 = reader.read_utf8_string().await.unwrap();
1010        assert_eq!(key2, "allow_experimental");
1011        let flags2 = reader.read_var_uint().await.unwrap();
1012        assert_eq!(flags2, 0);
1013        let value2 = reader.read_utf8_string().await.unwrap();
1014        assert_eq!(value2, "true");
1015    }
1016
1017    #[test]
1018    fn test_settings_deref() {
1019        let settings = Settings::from(vec![("max_threads".to_string(), 8_i32)]);
1020        let slice: &[Setting] = &settings;
1021        assert_eq!(slice.len(), 1);
1022        assert_eq!(slice[0].key, "max_threads");
1023    }
1024
1025    #[cfg(feature = "serde")]
1026    #[test]
1027    fn test_serde_serialization() {
1028        use serde_json;
1029
1030        let settings = Settings::from(vec![
1031            ("max_threads".to_string(), SettingValue::Int(8)),
1032            ("allow_experimental".to_string(), SettingValue::Bool(true)),
1033            ("default_format".to_string(), "JSON".into()),
1034        ]);
1035
1036        let json = serde_json::to_string(&settings).unwrap();
1037        let deserialized: Settings = serde_json::from_str(&json).unwrap();
1038        assert_eq!(settings, deserialized);
1039
1040        // Test single Setting
1041        let setting = create_setting("max_threads", 8_i32, true, false);
1042        let json = serde_json::to_string(&setting).unwrap();
1043        let deserialized: Setting = serde_json::from_str(&json).unwrap();
1044        assert_eq!(setting, deserialized);
1045    }
1046
1047    #[tokio::test]
1048    async fn test_settings_decode_empty() {
1049        // Test decoding empty settings (just end marker)
1050        let mut writer = Cursor::new(Vec::new());
1051
1052        // Write empty string as end marker
1053        writer.write_string("").await.unwrap();
1054        writer.flush().await.unwrap();
1055
1056        let mut reader = Cursor::new(writer.into_inner());
1057        let settings = Settings::decode(&mut reader).await.unwrap();
1058
1059        assert_eq!(settings.0.len(), 0);
1060    }
1061
1062    #[tokio::test]
1063    async fn test_settings_decode_single_standard_setting() {
1064        // Test decoding a single standard (non-custom) setting
1065        let mut writer = MockWriter::default();
1066
1067        // Write setting: name -> flags -> value -> end marker
1068        writer.write_string("max_threads").await.unwrap();
1069        writer.write_var_uint(0).await.unwrap(); // No flags
1070        writer.write_string("8").await.unwrap();
1071        writer.write_string("").await.unwrap(); // End marker
1072        writer.flush().await.unwrap();
1073
1074        let mut reader = Cursor::new(writer.into_inner());
1075        let settings = Settings::decode(&mut reader).await.unwrap();
1076
1077        assert_eq!(settings.0.len(), 1);
1078        assert_eq!(settings.0[0].key, "max_threads");
1079        assert_eq!(settings.0[0].value, SettingValue::Int(8));
1080        assert!(!settings.0[0].important);
1081        assert!(!settings.0[0].custom);
1082    }
1083
1084    #[tokio::test]
1085    async fn test_settings_decode_custom_setting() {
1086        // Test decoding a custom setting
1087        let mut writer = MockWriter::default();
1088
1089        // Write custom setting: name -> custom flag -> field dump -> end marker
1090        writer.write_string("custom_setting").await.unwrap();
1091        writer.write_var_uint(SETTING_FLAG_CUSTOM).await.unwrap();
1092        writer.write_string("custom_value").await.unwrap();
1093        writer.write_string("").await.unwrap(); // End marker
1094        writer.flush().await.unwrap();
1095
1096        let mut reader = Cursor::new(writer.into_inner());
1097        let settings = Settings::decode(&mut reader).await.unwrap();
1098
1099        assert_eq!(settings.0.len(), 1);
1100        assert_eq!(settings.0[0].key, "custom_setting");
1101        assert_eq!(settings.0[0].value, SettingValue::String("custom_value".to_string()));
1102        assert!(!settings.0[0].important);
1103        assert!(settings.0[0].custom);
1104    }
1105
1106    #[tokio::test]
1107    async fn test_settings_decode_important_setting() {
1108        // Test decoding an important setting
1109        let mut writer = MockWriter::default();
1110
1111        // Write important setting
1112        writer.write_string("critical_setting").await.unwrap();
1113        writer.write_var_uint(SETTING_FLAG_IMPORTANT).await.unwrap();
1114        writer.write_string("true").await.unwrap();
1115        writer.write_string("").await.unwrap(); // End marker
1116        writer.flush().await.unwrap();
1117
1118        let mut reader = Cursor::new(writer.into_inner());
1119        let settings = Settings::decode(&mut reader).await.unwrap();
1120
1121        assert_eq!(settings.0.len(), 1);
1122        assert_eq!(settings.0[0].key, "critical_setting");
1123        assert_eq!(settings.0[0].value, SettingValue::Bool(true));
1124        assert!(settings.0[0].important);
1125        assert!(!settings.0[0].custom);
1126    }
1127
1128    #[tokio::test]
1129    async fn test_settings_decode_multiple_settings() {
1130        // Test decoding multiple settings with different types and flags
1131        let mut writer = MockWriter::default();
1132
1133        // Setting 1: Standard integer
1134        writer.write_string("max_threads").await.unwrap();
1135        writer.write_var_uint(0).await.unwrap();
1136        writer.write_string("4").await.unwrap();
1137
1138        // Setting 2: Important boolean
1139        writer.write_string("allow_experimental").await.unwrap();
1140        writer.write_var_uint(SETTING_FLAG_IMPORTANT).await.unwrap();
1141        writer.write_string("false").await.unwrap();
1142
1143        // Setting 3: Custom setting
1144        writer.write_string("custom_config").await.unwrap();
1145        writer.write_var_uint(SETTING_FLAG_CUSTOM).await.unwrap();
1146        writer.write_string("custom_data").await.unwrap();
1147
1148        // Setting 4: Important + Custom
1149        writer.write_string("important_custom").await.unwrap();
1150        writer.write_var_uint(SETTING_FLAG_IMPORTANT | SETTING_FLAG_CUSTOM).await.unwrap();
1151        writer.write_string("special_value").await.unwrap();
1152
1153        // Setting 5: Float value
1154        writer.write_string("timeout_ratio").await.unwrap();
1155        writer.write_var_uint(0).await.unwrap();
1156        writer.write_string("1.5").await.unwrap();
1157
1158        // End marker
1159        writer.write_string("").await.unwrap();
1160        writer.flush().await.unwrap();
1161
1162        let mut reader = Cursor::new(writer.into_inner());
1163        let settings = Settings::decode(&mut reader).await.unwrap();
1164
1165        assert_eq!(settings.0.len(), 5);
1166
1167        // Verify Setting 1
1168        assert_eq!(settings.0[0].key, "max_threads");
1169        assert_eq!(settings.0[0].value, SettingValue::Int(4));
1170        assert!(!settings.0[0].important);
1171        assert!(!settings.0[0].custom);
1172
1173        // Verify Setting 2
1174        assert_eq!(settings.0[1].key, "allow_experimental");
1175        assert_eq!(settings.0[1].value, SettingValue::Bool(false));
1176        assert!(settings.0[1].important);
1177        assert!(!settings.0[1].custom);
1178
1179        // Verify Setting 3
1180        assert_eq!(settings.0[2].key, "custom_config");
1181        assert_eq!(settings.0[2].value, SettingValue::String("custom_data".to_string()));
1182        assert!(!settings.0[2].important);
1183        assert!(settings.0[2].custom);
1184
1185        // Verify Setting 4
1186        assert_eq!(settings.0[3].key, "important_custom");
1187        assert_eq!(settings.0[3].value, SettingValue::String("special_value".to_string()));
1188        assert!(settings.0[3].important);
1189        assert!(settings.0[3].custom);
1190
1191        // Verify Setting 5
1192        assert_eq!(settings.0[4].key, "timeout_ratio");
1193        assert_eq!(settings.0[4].value, SettingValue::Float(1.5));
1194        assert!(!settings.0[4].important);
1195        assert!(!settings.0[4].custom);
1196    }
1197
1198    #[tokio::test]
1199    async fn test_settings_decode_parse_setting_value_edge_cases() {
1200        // Test various edge cases for value parsing
1201        let mut writer = MockWriter::default();
1202
1203        // Test "0" as boolean false
1204        writer.write_string("bool_zero").await.unwrap();
1205        writer.write_var_uint(0).await.unwrap();
1206        writer.write_string("0").await.unwrap();
1207
1208        // Test "1" as boolean true
1209        writer.write_string("bool_one").await.unwrap();
1210        writer.write_var_uint(0).await.unwrap();
1211        writer.write_string("1").await.unwrap();
1212
1213        // Test negative integer
1214        writer.write_string("negative_int").await.unwrap();
1215        writer.write_var_uint(0).await.unwrap();
1216        writer.write_string("-42").await.unwrap();
1217
1218        // Test string that looks like number but isn't parseable as int/float
1219        writer.write_string("string_val").await.unwrap();
1220        writer.write_var_uint(0).await.unwrap();
1221        writer.write_string("not_a_number").await.unwrap();
1222
1223        // Test empty string value
1224        writer.write_string("empty_val").await.unwrap();
1225        writer.write_var_uint(0).await.unwrap();
1226        writer.write_string("").await.unwrap();
1227
1228        // End marker
1229        writer.write_string("").await.unwrap();
1230        writer.flush().await.unwrap();
1231
1232        let mut reader = Cursor::new(writer.into_inner());
1233        let settings = Settings::decode(&mut reader).await.unwrap();
1234
1235        assert_eq!(settings.0.len(), 5);
1236        assert_eq!(settings.0[0].value, SettingValue::Bool(false)); // "0" -> false
1237        assert_eq!(settings.0[1].value, SettingValue::Bool(true)); // "1" -> true
1238        assert_eq!(settings.0[2].value, SettingValue::Int(-42)); // "-42" -> int
1239        assert_eq!(settings.0[3].value, SettingValue::String("not_a_number".to_string())); // fallback to string
1240        assert_eq!(settings.0[4].value, SettingValue::String(String::new())); // empty string
1241    }
1242
1243    #[tokio::test]
1244    async fn test_settings_decode_roundtrip() {
1245        // Test that encode -> decode produces the same data
1246        let original_settings = Settings::from(vec![
1247            ("max_threads".to_string(), SettingValue::Int(8)),
1248            ("allow_experimental".to_string(), SettingValue::Bool(true)),
1249            ("custom_setting".to_string(), SettingValue::String("custom_value".to_string())),
1250        ]);
1251
1252        // Mark one as custom for testing
1253        let mut settings_with_custom = original_settings.clone();
1254        settings_with_custom.0[2].custom = true;
1255        settings_with_custom.0[1].important = true;
1256
1257        // Encode
1258        let mut writer = MockWriter::default();
1259        settings_with_custom
1260            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
1261            .await
1262            .unwrap();
1263        writer.write_string("").await.unwrap();
1264        writer.flush().await.unwrap();
1265
1266        // Decode
1267        let mut reader = Cursor::new(writer.into_inner());
1268
1269        let decoded_settings = Settings::decode(&mut reader).await.unwrap();
1270
1271        // Compare (note: encode doesn't write end marker, so we need to account for that)
1272        assert_eq!(decoded_settings.0.len(), 3);
1273        assert_eq!(decoded_settings.0[0].key, "max_threads");
1274        assert_eq!(decoded_settings.0[0].value, SettingValue::Int(8));
1275        assert!(!decoded_settings.0[0].important);
1276        assert!(!decoded_settings.0[0].custom);
1277
1278        assert_eq!(decoded_settings.0[1].key, "allow_experimental");
1279        assert_eq!(decoded_settings.0[1].value, SettingValue::Bool(true));
1280        assert!(decoded_settings.0[1].important);
1281        assert!(!decoded_settings.0[1].custom);
1282
1283        assert_eq!(decoded_settings.0[2].key, "custom_setting");
1284        assert_eq!(decoded_settings.0[2].value, SettingValue::String("custom_value".to_string()));
1285        assert!(!decoded_settings.0[2].important);
1286        assert!(decoded_settings.0[2].custom);
1287    }
1288}