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.iter().map(|setting| (setting.key.clone(), setting.value.to_string())).collect()
429    }
430
431    /// Converts settings to a vector of formatted strings.
432    ///
433    /// Each setting is formatted as `key = value`.
434    ///
435    /// # Example
436    /// ```rust,ignore
437    /// use clickhouse_arrow::query::settings::Settings;
438    ///
439    /// let settings = Settings::from([("max_threads".to_string(), 8_i32)]);
440    /// let strings = settings.encode_to_strings();
441    /// assert_eq!(strings, vec!["max_threads = 8"]);
442    /// ```
443    pub fn encode_to_strings(&self) -> Vec<String> {
444        self.0.iter().map(|setting| format!("{} = {}", setting.key, setting.value)).collect()
445    }
446
447    // TODO: Remove - docs
448    pub(crate) async fn encode<W: ClickHouseWrite>(
449        &self,
450        writer: &mut W,
451        revision: u64,
452    ) -> Result<()> {
453        for setting in &self.0 {
454            setting.encode(writer, revision).await?;
455        }
456        Ok(())
457    }
458
459    // TODO: Remove - docs
460    pub(crate) async fn encode_with_ignore<W: ClickHouseWrite>(
461        &self,
462        writer: &mut W,
463        revision: u64,
464        ignore: &Settings,
465    ) -> Result<()> {
466        for setting in &self.0 {
467            if ignore.get(&setting.key).is_some_and(|s| s.value == setting.value) {
468                continue;
469            }
470
471            setting.encode(writer, revision).await?;
472        }
473        Ok(())
474    }
475
476    /// Decodes a collection of settings from the `ClickHouse` native protocol.
477    ///
478    /// Based on `BaseSettings<TTraits>::read()` from cpp source, the format is:
479    /// 1. Loop reading setting name strings
480    /// 2. Empty string marks end of settings
481    /// 3. For each setting: name -> flags (if `STRINGS_WITH_FLAGS`) -> value
482    ///
483    /// # Arguments
484    /// - `reader`: The reader to deserialize the settings from.
485    /// - `revision`: The `ClickHouse` server protocol revision.
486    ///
487    /// # Errors
488    /// Returns an error if the data cannot be read or parsed correctly.
489    pub(crate) async fn decode<R: ClickHouseRead>(reader: &mut R) -> Result<Self> {
490        let mut settings = Vec::new();
491        loop {
492            // Read setting name
493            let key = reader.read_string().await?;
494            // Empty string marks end of settings
495            if key.is_empty() {
496                break;
497            }
498            settings
499                .push(Setting::decode(reader, String::from_utf8_lossy(&key).to_string()).await?);
500        }
501        Ok(Settings(settings))
502    }
503
504    /// Internal helper to find a specific settings
505    pub(crate) fn get(&self, key: &str) -> Option<&Setting> { self.0.iter().find(|s| s.key == key) }
506}
507
508impl<T, K, S> From<T> for Settings
509where
510    T: IntoIterator<Item = (K, S)>,
511    K: Into<String>,
512    SettingValue: From<S>,
513{
514    fn from(value: T) -> Self {
515        Self(
516            value
517                .into_iter()
518                .map(|(k, v)| Setting {
519                    key:       k.into(),
520                    value:     v.into(),
521                    important: false,
522                    custom:    false,
523                })
524                .collect(),
525        )
526    }
527}
528
529impl<K, S> FromIterator<(K, S)> for Settings
530where
531    K: Into<String>,
532    SettingValue: From<S>,
533{
534    fn from_iter<T>(iter: T) -> Self
535    where
536        T: IntoIterator<Item = (K, S)>,
537    {
538        iter.into_iter().collect()
539    }
540}
541
542impl std::ops::Deref for Settings {
543    type Target = [Setting];
544
545    fn deref(&self) -> &Self::Target { &self.0 }
546}
547
548#[cfg(feature = "serde")]
549pub mod deser {
550    use serde::{Deserialize, Serialize};
551
552    use super::*;
553
554    impl Serialize for SettingValue {
555        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
556        where
557            S: serde::Serializer,
558        {
559            match self {
560                SettingValue::Int(i) => ::serde::Serialize::serialize(i, serializer),
561                SettingValue::Bool(b) => ::serde::Serialize::serialize(b, serializer),
562                SettingValue::Float(f) => ::serde::Serialize::serialize(f, serializer),
563                SettingValue::String(s) => ::serde::Serialize::serialize(s, serializer),
564            }
565        }
566    }
567
568    impl<'de> Deserialize<'de> for SettingValue {
569        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
570        where
571            D: serde::Deserializer<'de>,
572        {
573            fn deserialize_setting<'d, De>(deserializer: De) -> Result<SettingValue, De::Error>
574            where
575                De: serde::Deserializer<'d>,
576            {
577                use serde::de::Visitor;
578
579                struct SettingVisitor;
580
581                type Result<E> = std::result::Result<SettingValue, E>;
582
583                impl Visitor<'_> for SettingVisitor {
584                    type Value = SettingValue;
585
586                    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
587                        formatter.write_str("a number, float or string")
588                    }
589
590                    fn visit_bool<E>(self, value: bool) -> Result<E>
591                    where
592                        E: serde::de::Error,
593                    {
594                        Ok(value.into())
595                    }
596
597                    fn visit_u8<E>(self, value: u8) -> Result<E>
598                    where
599                        E: serde::de::Error,
600                    {
601                        Ok(value.into())
602                    }
603
604                    fn visit_u16<E>(self, value: u16) -> Result<E>
605                    where
606                        E: serde::de::Error,
607                    {
608                        Ok(value.into())
609                    }
610
611                    fn visit_u32<E>(self, value: u32) -> Result<E>
612                    where
613                        E: serde::de::Error,
614                    {
615                        Ok(value.into())
616                    }
617
618                    fn visit_u64<E>(self, value: u64) -> Result<E>
619                    where
620                        E: serde::de::Error,
621                    {
622                        Ok(value.into())
623                    }
624
625                    fn visit_i8<E>(self, value: i8) -> Result<E>
626                    where
627                        E: serde::de::Error,
628                    {
629                        Ok(value.into())
630                    }
631
632                    fn visit_i16<E>(self, value: i16) -> Result<E>
633                    where
634                        E: serde::de::Error,
635                    {
636                        Ok(value.into())
637                    }
638
639                    fn visit_i32<E>(self, value: i32) -> Result<E>
640                    where
641                        E: serde::de::Error,
642                    {
643                        Ok(value.into())
644                    }
645
646                    fn visit_i64<E>(self, value: i64) -> Result<E>
647                    where
648                        E: serde::de::Error,
649                    {
650                        Ok(value.into())
651                    }
652
653                    fn visit_f32<E>(self, value: f32) -> Result<E>
654                    where
655                        E: serde::de::Error,
656                    {
657                        Ok(value.into())
658                    }
659
660                    fn visit_f64<E>(self, value: f64) -> Result<E>
661                    where
662                        E: serde::de::Error,
663                    {
664                        Ok(value.into())
665                    }
666
667                    fn visit_str<E>(self, value: &str) -> Result<E>
668                    where
669                        E: serde::de::Error,
670                    {
671                        Ok(value.into())
672                    }
673
674                    fn visit_string<E>(self, value: String) -> Result<E>
675                    where
676                        E: serde::de::Error,
677                    {
678                        self.visit_str(&value)
679                    }
680                }
681
682                deserializer.deserialize_any(SettingVisitor)
683            }
684            deserialize_setting(deserializer)
685        }
686    }
687
688    #[cfg(test)]
689    mod tests {
690        use super::*;
691
692        #[test]
693        fn test_setting_value_serialize_deserialize() {
694            // Test serialization and deserialization for all SettingValue variants
695            let values = vec![
696                SettingValue::Int(42),
697                SettingValue::Bool(true),
698                SettingValue::Float(3.15),
699                SettingValue::String("test".to_string()),
700            ];
701
702            for value in values {
703                // Serialize
704                let json = serde_json::to_string(&value).unwrap();
705
706                // Deserialize
707                let deserialized: SettingValue = serde_json::from_str(&json).unwrap();
708
709                // Verify round-trip
710                match (value, deserialized) {
711                    (SettingValue::Int(a), SettingValue::Int(b)) => assert_eq!(a, b),
712                    (SettingValue::Bool(a), SettingValue::Bool(b)) => assert_eq!(a, b),
713                    (SettingValue::Float(a), SettingValue::Float(b)) => {
714                        assert!((a - b).abs() < 1e-6);
715                    }
716                    (SettingValue::String(a), SettingValue::String(b)) => assert_eq!(a, b),
717                    _ => panic!("Mismatched variants"),
718                }
719            }
720        }
721
722        #[test]
723        fn test_setting_value_deserialize_variants() {
724            // Test deserialization from various JSON inputs
725            assert_eq!(serde_json::from_str::<SettingValue>("42").unwrap(), SettingValue::Int(42));
726            assert_eq!(
727                serde_json::from_str::<SettingValue>("true").unwrap(),
728                SettingValue::Bool(true)
729            );
730            assert!(
731                (serde_json::from_str::<SettingValue>("3.15").unwrap().unwrap_float() - 3.15).abs()
732                    < 1e-6
733            );
734            assert_eq!(
735                serde_json::from_str::<SettingValue>("\"test\"").unwrap(),
736                SettingValue::String("test".to_string())
737            );
738
739            // Test integer variants
740            assert_eq!(
741                serde_json::from_str::<SettingValue>("255").unwrap(),
742                SettingValue::Int(255)
743            ); // u8
744            assert_eq!(
745                serde_json::from_str::<SettingValue>("65535").unwrap(),
746                SettingValue::Int(65535)
747            ); // u16
748            assert_eq!(
749                serde_json::from_str::<SettingValue>("4294967295").unwrap(),
750                SettingValue::Int(4_294_967_295)
751            ); // u32
752            assert_eq!(
753                serde_json::from_str::<SettingValue>("-128").unwrap(),
754                SettingValue::Int(-128)
755            ); // i8
756            assert_eq!(
757                serde_json::from_str::<SettingValue>("-32768").unwrap(),
758                SettingValue::Int(-32768)
759            ); // i16
760            assert_eq!(
761                serde_json::from_str::<SettingValue>("-2147483648").unwrap(),
762                SettingValue::Int(-2_147_483_648)
763            ); // i32
764        }
765
766        #[test]
767        fn test_setting_value_deserialize_invalid() {
768            // Test deserialization of invalid JSON
769            assert!(serde_json::from_str::<SettingValue>("null").is_err());
770            assert!(serde_json::from_str::<SettingValue>("[]").is_err());
771            assert!(serde_json::from_str::<SettingValue>("{}").is_err());
772        }
773    }
774}
775
776#[cfg(test)]
777mod tests {
778    use std::io::Cursor;
779
780    use tokio::io::AsyncWriteExt;
781
782    use super::*;
783    use crate::io::ClickHouseRead;
784
785    type MockWriter = Cursor<Vec<u8>>;
786
787    // Helper to create a Setting
788    fn create_setting<S>(key: &str, value: S, important: bool, custom: bool) -> Setting
789    where
790        SettingValue: From<S>,
791    {
792        Setting { key: key.to_string(), value: value.into(), important, custom }
793    }
794
795    #[test]
796    fn test_setting_value_from_primitives() {
797        // Test all supported From implementations for SettingValue
798        assert_eq!(SettingValue::from(8_i8), SettingValue::Int(8));
799        assert_eq!(SettingValue::from(8_i16), SettingValue::Int(8));
800        assert_eq!(SettingValue::from(8_i32), SettingValue::Int(8));
801        assert_eq!(SettingValue::from(8_i64), SettingValue::Int(8));
802        assert_eq!(SettingValue::from(8_u8), SettingValue::Int(8));
803        assert_eq!(SettingValue::from(8_u16), SettingValue::Int(8));
804        assert_eq!(SettingValue::from(8_u32), SettingValue::Int(8));
805        assert_eq!(SettingValue::from(8_u64), SettingValue::Int(8));
806        assert_eq!(SettingValue::from(true), SettingValue::Bool(true));
807        assert!((SettingValue::from(3.15_f32).unwrap_float() - 3.15).abs() < 1e-6);
808        assert_eq!(SettingValue::from(3.15_f64), SettingValue::Float(3.15));
809        assert_eq!(SettingValue::from("test"), SettingValue::String("test".to_string()));
810        assert_eq!(
811            SettingValue::from("test".to_string()),
812            SettingValue::String("test".to_string())
813        );
814        assert_eq!(
815            SettingValue::from(Box::<str>::from("test")),
816            SettingValue::String("test".to_string())
817        );
818        assert_eq!(
819            SettingValue::from(std::sync::Arc::<str>::from("test")),
820            SettingValue::String("test".to_string())
821        );
822    }
823
824    #[test]
825    fn test_setting_value_display() {
826        // Test Display implementation for SettingValue
827        assert_eq!(SettingValue::Int(42).to_string(), "42");
828        assert_eq!(SettingValue::Bool(true).to_string(), "true");
829        assert_eq!(SettingValue::Float(3.15).to_string(), "3.15");
830        assert_eq!(SettingValue::String("test".to_string()).to_string(), "test");
831    }
832
833    #[test]
834    fn test_setting_encode_field_dump() {
835        // Test encode_field_dump for string values
836        let setting = create_setting("key", "value", false, true);
837        assert_eq!(setting.encode_field_dump().unwrap(), "value");
838
839        // Test string with quotes
840        let setting = create_setting("key", "val'ue", false, true);
841        assert_eq!(setting.encode_field_dump().unwrap(), "val'ue");
842
843        // Test non-string value (should error)
844        let setting = create_setting("key", 42_i32, false, true);
845        assert!(setting.encode_field_dump().is_err());
846    }
847
848    #[tokio::test]
849    async fn test_setting_encode_legacy_revision() {
850        // Test encode for legacy revision (≤ DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
851        let setting = create_setting("max_threads", 8_i32, false, false);
852        let mut writer = MockWriter::default();
853        setting
854            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
855            .await
856            .unwrap();
857        writer.flush().await.unwrap();
858
859        // Decode and verify using ClickHouseRead
860        let mut reader = Cursor::new(writer.into_inner());
861        let key = reader.read_utf8_string().await.unwrap();
862        assert_eq!(key, "max_threads");
863        let value = reader.read_var_uint().await.unwrap();
864        assert_eq!(value, 8);
865
866        // Test boolean setting
867        let setting = create_setting("allow_experimental", true, false, false);
868        let mut writer = MockWriter::default();
869        setting
870            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
871            .await
872            .unwrap();
873        writer.flush().await.unwrap();
874
875        let mut reader = Cursor::new(writer.into_inner());
876        let key = reader.read_utf8_string().await.unwrap();
877        assert_eq!(key, "allow_experimental");
878        let value = reader.read_var_uint().await.unwrap();
879        assert_eq!(value, 1);
880
881        // Test unsupported type (should error)
882        let setting = create_setting("default_format", "JSON", false, false);
883        let mut writer = MockWriter::default();
884        assert!(matches!(
885            setting
886                .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
887                .await,
888            Err(Error::UnsupportedSettingType(key)) if key == "default_format"
889        ));
890    }
891
892    #[tokio::test]
893    async fn test_setting_encode_modern_revision() {
894        // Test encode for modern revision (> DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS)
895        let setting = create_setting("max_threads", 8_i32, false, false);
896        let mut writer = MockWriter::default();
897        setting
898            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
899            .await
900            .unwrap();
901        writer.flush().await.unwrap();
902
903        // Decode and verify using ClickHouseRead
904        let mut reader = Cursor::new(writer.into_inner());
905        let key = reader.read_utf8_string().await.unwrap();
906        assert_eq!(key, "max_threads");
907        let flags = reader.read_var_uint().await.unwrap();
908        assert_eq!(flags, 0);
909        let value = reader.read_utf8_string().await.unwrap();
910        assert_eq!(value, "8");
911
912        // Test with important and custom flags
913        let setting = create_setting("custom_key", "value", true, true);
914        let mut writer = MockWriter::default();
915        setting
916            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
917            .await
918            .unwrap();
919        writer.flush().await.unwrap();
920
921        let mut reader = Cursor::new(writer.into_inner());
922        let key = reader.read_utf8_string().await.unwrap();
923        assert_eq!(key, "custom_key");
924        let flags = reader.read_var_uint().await.unwrap();
925        assert_eq!(flags, SETTING_FLAG_IMPORTANT | SETTING_FLAG_CUSTOM);
926        let value = reader.read_utf8_string().await.unwrap();
927        assert_eq!(value, "value");
928    }
929
930    #[test]
931    fn test_settings_add_setting() {
932        let mut settings = Settings::default();
933        settings.add_setting("max_threads", 8_i32);
934        settings.add_setting("default_format", "JSON");
935
936        assert_eq!(settings.0.len(), 2);
937        assert_eq!(settings.0[0].key, "max_threads");
938        assert_eq!(settings.0[0].value, SettingValue::Int(8));
939        assert_eq!(settings.0[1].key, "default_format");
940        assert_eq!(settings.0[1].value, SettingValue::String("JSON".to_string()));
941    }
942
943    #[test]
944    fn test_settings_from_iterator() {
945        let settings = Settings::from(vec![
946            ("max_threads".to_string(), SettingValue::Int(8)),
947            ("allow_experimental".to_string(), SettingValue::Bool(true)),
948        ]);
949
950        assert_eq!(settings.0.len(), 2);
951        assert_eq!(settings.0[0].key, "max_threads");
952        assert_eq!(settings.0[0].value, SettingValue::Int(8));
953        assert_eq!(settings.0[1].key, "allow_experimental");
954        assert_eq!(settings.0[1].value, SettingValue::Bool(true));
955    }
956
957    #[test]
958    fn test_settings_encode_to_key_value_strings() {
959        let settings = Settings::from(vec![
960            ("max_threads".to_string(), SettingValue::Int(8)),
961            ("default_format".to_string(), "JSON".into()),
962        ]);
963
964        let kv_pairs = settings.encode_to_key_value_strings();
965        assert_eq!(kv_pairs, vec![
966            ("max_threads".to_string(), "8".to_string()),
967            ("default_format".to_string(), "JSON".to_string()),
968        ]);
969    }
970
971    #[test]
972    fn test_settings_encode_to_strings() {
973        let settings = Settings::from(vec![
974            ("max_threads".to_string(), SettingValue::Int(8)),
975            ("default_format".to_string(), "JSON".into()),
976        ]);
977
978        let strings = settings.encode_to_strings();
979        assert_eq!(strings, vec!["max_threads = 8", "default_format = JSON"]);
980    }
981
982    #[tokio::test]
983    async fn test_settings_encode() {
984        let settings = Settings::from(vec![
985            ("max_threads".to_string(), SettingValue::Int(8)),
986            ("allow_experimental".to_string(), SettingValue::Bool(true)),
987        ]);
988
989        let mut writer = MockWriter::default();
990        settings
991            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
992            .await
993            .unwrap();
994        writer.write_string("").await.unwrap();
995        writer.flush().await.unwrap();
996
997        // Decode and verify using ClickHouseRead
998        let mut reader = Cursor::new(writer.into_inner());
999        let key1 = reader.read_utf8_string().await.unwrap();
1000        assert_eq!(key1, "max_threads");
1001        let flags1 = reader.read_var_uint().await.unwrap();
1002        assert_eq!(flags1, 0);
1003        let value1 = reader.read_utf8_string().await.unwrap();
1004        assert_eq!(value1, "8");
1005
1006        let key2 = reader.read_utf8_string().await.unwrap();
1007        assert_eq!(key2, "allow_experimental");
1008        let flags2 = reader.read_var_uint().await.unwrap();
1009        assert_eq!(flags2, 0);
1010        let value2 = reader.read_utf8_string().await.unwrap();
1011        assert_eq!(value2, "true");
1012    }
1013
1014    #[test]
1015    fn test_settings_deref() {
1016        let settings = Settings::from(vec![("max_threads".to_string(), 8_i32)]);
1017        let slice: &[Setting] = &settings;
1018        assert_eq!(slice.len(), 1);
1019        assert_eq!(slice[0].key, "max_threads");
1020    }
1021
1022    #[cfg(feature = "serde")]
1023    #[test]
1024    fn test_serde_serialization() {
1025        use serde_json;
1026
1027        let settings = Settings::from(vec![
1028            ("max_threads".to_string(), SettingValue::Int(8)),
1029            ("allow_experimental".to_string(), SettingValue::Bool(true)),
1030            ("default_format".to_string(), "JSON".into()),
1031        ]);
1032
1033        let json = serde_json::to_string(&settings).unwrap();
1034        let deserialized: Settings = serde_json::from_str(&json).unwrap();
1035        assert_eq!(settings, deserialized);
1036
1037        // Test single Setting
1038        let setting = create_setting("max_threads", 8_i32, true, false);
1039        let json = serde_json::to_string(&setting).unwrap();
1040        let deserialized: Setting = serde_json::from_str(&json).unwrap();
1041        assert_eq!(setting, deserialized);
1042    }
1043
1044    #[tokio::test]
1045    async fn test_settings_decode_empty() {
1046        // Test decoding empty settings (just end marker)
1047        let mut writer = Cursor::new(Vec::new());
1048
1049        // Write empty string as end marker
1050        writer.write_string("").await.unwrap();
1051        writer.flush().await.unwrap();
1052
1053        let mut reader = Cursor::new(writer.into_inner());
1054        let settings = Settings::decode(&mut reader).await.unwrap();
1055
1056        assert_eq!(settings.0.len(), 0);
1057    }
1058
1059    #[tokio::test]
1060    async fn test_settings_decode_single_standard_setting() {
1061        // Test decoding a single standard (non-custom) setting
1062        let mut writer = MockWriter::default();
1063
1064        // Write setting: name -> flags -> value -> end marker
1065        writer.write_string("max_threads").await.unwrap();
1066        writer.write_var_uint(0).await.unwrap(); // No flags
1067        writer.write_string("8").await.unwrap();
1068        writer.write_string("").await.unwrap(); // End marker
1069        writer.flush().await.unwrap();
1070
1071        let mut reader = Cursor::new(writer.into_inner());
1072        let settings = Settings::decode(&mut reader).await.unwrap();
1073
1074        assert_eq!(settings.0.len(), 1);
1075        assert_eq!(settings.0[0].key, "max_threads");
1076        assert_eq!(settings.0[0].value, SettingValue::Int(8));
1077        assert!(!settings.0[0].important);
1078        assert!(!settings.0[0].custom);
1079    }
1080
1081    #[tokio::test]
1082    async fn test_settings_decode_custom_setting() {
1083        // Test decoding a custom setting
1084        let mut writer = MockWriter::default();
1085
1086        // Write custom setting: name -> custom flag -> field dump -> end marker
1087        writer.write_string("custom_setting").await.unwrap();
1088        writer.write_var_uint(SETTING_FLAG_CUSTOM).await.unwrap();
1089        writer.write_string("custom_value").await.unwrap();
1090        writer.write_string("").await.unwrap(); // End marker
1091        writer.flush().await.unwrap();
1092
1093        let mut reader = Cursor::new(writer.into_inner());
1094        let settings = Settings::decode(&mut reader).await.unwrap();
1095
1096        assert_eq!(settings.0.len(), 1);
1097        assert_eq!(settings.0[0].key, "custom_setting");
1098        assert_eq!(settings.0[0].value, SettingValue::String("custom_value".to_string()));
1099        assert!(!settings.0[0].important);
1100        assert!(settings.0[0].custom);
1101    }
1102
1103    #[tokio::test]
1104    async fn test_settings_decode_important_setting() {
1105        // Test decoding an important setting
1106        let mut writer = MockWriter::default();
1107
1108        // Write important setting
1109        writer.write_string("critical_setting").await.unwrap();
1110        writer.write_var_uint(SETTING_FLAG_IMPORTANT).await.unwrap();
1111        writer.write_string("true").await.unwrap();
1112        writer.write_string("").await.unwrap(); // End marker
1113        writer.flush().await.unwrap();
1114
1115        let mut reader = Cursor::new(writer.into_inner());
1116        let settings = Settings::decode(&mut reader).await.unwrap();
1117
1118        assert_eq!(settings.0.len(), 1);
1119        assert_eq!(settings.0[0].key, "critical_setting");
1120        assert_eq!(settings.0[0].value, SettingValue::Bool(true));
1121        assert!(settings.0[0].important);
1122        assert!(!settings.0[0].custom);
1123    }
1124
1125    #[tokio::test]
1126    async fn test_settings_decode_multiple_settings() {
1127        // Test decoding multiple settings with different types and flags
1128        let mut writer = MockWriter::default();
1129
1130        // Setting 1: Standard integer
1131        writer.write_string("max_threads").await.unwrap();
1132        writer.write_var_uint(0).await.unwrap();
1133        writer.write_string("4").await.unwrap();
1134
1135        // Setting 2: Important boolean
1136        writer.write_string("allow_experimental").await.unwrap();
1137        writer.write_var_uint(SETTING_FLAG_IMPORTANT).await.unwrap();
1138        writer.write_string("false").await.unwrap();
1139
1140        // Setting 3: Custom setting
1141        writer.write_string("custom_config").await.unwrap();
1142        writer.write_var_uint(SETTING_FLAG_CUSTOM).await.unwrap();
1143        writer.write_string("custom_data").await.unwrap();
1144
1145        // Setting 4: Important + Custom
1146        writer.write_string("important_custom").await.unwrap();
1147        writer.write_var_uint(SETTING_FLAG_IMPORTANT | SETTING_FLAG_CUSTOM).await.unwrap();
1148        writer.write_string("special_value").await.unwrap();
1149
1150        // Setting 5: Float value
1151        writer.write_string("timeout_ratio").await.unwrap();
1152        writer.write_var_uint(0).await.unwrap();
1153        writer.write_string("1.5").await.unwrap();
1154
1155        // End marker
1156        writer.write_string("").await.unwrap();
1157        writer.flush().await.unwrap();
1158
1159        let mut reader = Cursor::new(writer.into_inner());
1160        let settings = Settings::decode(&mut reader).await.unwrap();
1161
1162        assert_eq!(settings.0.len(), 5);
1163
1164        // Verify Setting 1
1165        assert_eq!(settings.0[0].key, "max_threads");
1166        assert_eq!(settings.0[0].value, SettingValue::Int(4));
1167        assert!(!settings.0[0].important);
1168        assert!(!settings.0[0].custom);
1169
1170        // Verify Setting 2
1171        assert_eq!(settings.0[1].key, "allow_experimental");
1172        assert_eq!(settings.0[1].value, SettingValue::Bool(false));
1173        assert!(settings.0[1].important);
1174        assert!(!settings.0[1].custom);
1175
1176        // Verify Setting 3
1177        assert_eq!(settings.0[2].key, "custom_config");
1178        assert_eq!(settings.0[2].value, SettingValue::String("custom_data".to_string()));
1179        assert!(!settings.0[2].important);
1180        assert!(settings.0[2].custom);
1181
1182        // Verify Setting 4
1183        assert_eq!(settings.0[3].key, "important_custom");
1184        assert_eq!(settings.0[3].value, SettingValue::String("special_value".to_string()));
1185        assert!(settings.0[3].important);
1186        assert!(settings.0[3].custom);
1187
1188        // Verify Setting 5
1189        assert_eq!(settings.0[4].key, "timeout_ratio");
1190        assert_eq!(settings.0[4].value, SettingValue::Float(1.5));
1191        assert!(!settings.0[4].important);
1192        assert!(!settings.0[4].custom);
1193    }
1194
1195    #[tokio::test]
1196    async fn test_settings_decode_parse_setting_value_edge_cases() {
1197        // Test various edge cases for value parsing
1198        let mut writer = MockWriter::default();
1199
1200        // Test "0" as boolean false
1201        writer.write_string("bool_zero").await.unwrap();
1202        writer.write_var_uint(0).await.unwrap();
1203        writer.write_string("0").await.unwrap();
1204
1205        // Test "1" as boolean true
1206        writer.write_string("bool_one").await.unwrap();
1207        writer.write_var_uint(0).await.unwrap();
1208        writer.write_string("1").await.unwrap();
1209
1210        // Test negative integer
1211        writer.write_string("negative_int").await.unwrap();
1212        writer.write_var_uint(0).await.unwrap();
1213        writer.write_string("-42").await.unwrap();
1214
1215        // Test string that looks like number but isn't parseable as int/float
1216        writer.write_string("string_val").await.unwrap();
1217        writer.write_var_uint(0).await.unwrap();
1218        writer.write_string("not_a_number").await.unwrap();
1219
1220        // Test empty string value
1221        writer.write_string("empty_val").await.unwrap();
1222        writer.write_var_uint(0).await.unwrap();
1223        writer.write_string("").await.unwrap();
1224
1225        // End marker
1226        writer.write_string("").await.unwrap();
1227        writer.flush().await.unwrap();
1228
1229        let mut reader = Cursor::new(writer.into_inner());
1230        let settings = Settings::decode(&mut reader).await.unwrap();
1231
1232        assert_eq!(settings.0.len(), 5);
1233        assert_eq!(settings.0[0].value, SettingValue::Bool(false)); // "0" -> false
1234        assert_eq!(settings.0[1].value, SettingValue::Bool(true)); // "1" -> true
1235        assert_eq!(settings.0[2].value, SettingValue::Int(-42)); // "-42" -> int
1236        assert_eq!(settings.0[3].value, SettingValue::String("not_a_number".to_string())); // fallback to string
1237        assert_eq!(settings.0[4].value, SettingValue::String(String::new())); // empty string
1238    }
1239
1240    #[tokio::test]
1241    async fn test_settings_decode_roundtrip() {
1242        // Test that encode -> decode produces the same data
1243        let original_settings = Settings::from(vec![
1244            ("max_threads".to_string(), SettingValue::Int(8)),
1245            ("allow_experimental".to_string(), SettingValue::Bool(true)),
1246            ("custom_setting".to_string(), SettingValue::String("custom_value".to_string())),
1247        ]);
1248
1249        // Mark one as custom for testing
1250        let mut settings_with_custom = original_settings.clone();
1251        settings_with_custom.0[2].custom = true;
1252        settings_with_custom.0[1].important = true;
1253
1254        // Encode
1255        let mut writer = MockWriter::default();
1256        settings_with_custom
1257            .encode(&mut writer, DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS + 1)
1258            .await
1259            .unwrap();
1260        writer.write_string("").await.unwrap();
1261        writer.flush().await.unwrap();
1262
1263        // Decode
1264        let mut reader = Cursor::new(writer.into_inner());
1265
1266        let decoded_settings = Settings::decode(&mut reader).await.unwrap();
1267
1268        // Compare (note: encode doesn't write end marker, so we need to account for that)
1269        assert_eq!(decoded_settings.0.len(), 3);
1270        assert_eq!(decoded_settings.0[0].key, "max_threads");
1271        assert_eq!(decoded_settings.0[0].value, SettingValue::Int(8));
1272        assert!(!decoded_settings.0[0].important);
1273        assert!(!decoded_settings.0[0].custom);
1274
1275        assert_eq!(decoded_settings.0[1].key, "allow_experimental");
1276        assert_eq!(decoded_settings.0[1].value, SettingValue::Bool(true));
1277        assert!(decoded_settings.0[1].important);
1278        assert!(!decoded_settings.0[1].custom);
1279
1280        assert_eq!(decoded_settings.0[2].key, "custom_setting");
1281        assert_eq!(decoded_settings.0[2].value, SettingValue::String("custom_value".to_string()));
1282        assert!(!decoded_settings.0[2].important);
1283        assert!(decoded_settings.0[2].custom);
1284    }
1285}