clickhouse_arrow/client/
options.rs

1use std::path::PathBuf;
2
3use tracing::warn;
4
5use super::CompressionMethod;
6use crate::native::protocol::ChunkedProtocolMode;
7use crate::prelude::Secret;
8
9/// Configuration options for a `ClickHouse` client connection and Arrow serialization.
10///
11/// The `ClientOptions` struct defines the settings used to establish a connection
12/// to a `ClickHouse` server and handle data serialization/deserialization with
13/// Apache Arrow. These options are typically set via [`super::builder::ClientBuilder`] methods
14/// (e.g., [`super::builder::ClientBuilder::with_username`],
15/// [`super::builder::ClientBuilder::with_tls`]) or constructed directly for use with
16/// [`crate::Client::connect`].
17///
18/// # Fields
19/// - `username`: The username for authenticating with `ClickHouse` (default: `"default"`).
20/// - `password`: The password for authentication, stored securely as a [`Secret`].
21/// - `default_database`: The default database for queries; if empty, uses `ClickHouse`'s
22///   `"default"` database.
23/// - `domain`: Optional domain for TLS verification; inferred from the destination if unset.
24/// - `ipv4_only`: If `true`, restricts address resolution to IPv4; if `false`, allows IPv6.
25/// - `cafile`: Optional path to a certificate authority file for TLS connections.
26/// - `use_tls`: If `true`, enables TLS for secure connections; if `false`, uses plain TCP.
27/// - `compression`: The compression method for data exchange (default: [`CompressionMethod::LZ4`]).
28/// - `arrow`: Optional Arrow-specific serialization options (see [`ArrowOptions`]).
29/// - `cloud`: Cloud-specific options for `ClickHouse` cloud instances (requires `cloud` feature).
30///
31/// # Examples
32/// ```rust,ignore
33/// use clickhouse_arrow::prelude::*;
34///
35/// let options = ClientOptions {
36///     username: "admin".to_string(),
37///     password: Secret::new("secret"),
38///     default_database: "my_db".to_string(),
39///     use_tls: true,
40///     ..ClientOptions::default()
41/// };
42///
43/// let client = Client::connect("localhost:9000", options, None, None)
44///     .await
45///     .unwrap();
46/// ```
47#[derive(Debug, Clone, PartialEq)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct ClientOptions {
50    /// Username credential
51    pub username:         String,
52    /// Password credential. [`Secret`] is used to minimize likelihood of exposure through logs
53    pub password:         Secret,
54    /// Scope this client to a specifc database, otherwise 'default' is used
55    pub default_database: String,
56    /// For tls, provide the domain, otherwise it will be determined from the endpoint.
57    pub domain:           Option<String>,
58    /// Whether any non-ipv4 socket addrs should be filtered out.
59    pub ipv4_only:        bool,
60    /// Provide a path to a certificate authority to use for tls.
61    pub cafile:           Option<PathBuf>,
62    /// Whether a connection should be made securely over tls.
63    pub use_tls:          bool,
64    /// The compression to use when sending data to clickhouse.
65    pub compression:      CompressionMethod,
66    /// Additional configuration not core to `ClickHouse` connections
67    #[cfg_attr(feature = "serde", serde(default))]
68    pub ext:              Extension,
69}
70
71impl Default for ClientOptions {
72    fn default() -> Self {
73        ClientOptions {
74            username:         "default".to_string(),
75            password:         Secret::new(""),
76            default_database: String::new(),
77            domain:           None,
78            ipv4_only:        false,
79            cafile:           None,
80            use_tls:          false,
81            compression:      CompressionMethod::default(),
82            ext:              Extension::default(),
83        }
84    }
85}
86
87impl ClientOptions {
88    /// Create a new `ClientOptions` with default values.
89    #[must_use]
90    pub fn new() -> Self { Self::default() }
91
92    #[must_use]
93    pub fn with_username(mut self, username: impl Into<String>) -> Self {
94        self.username = username.into();
95        self
96    }
97
98    #[must_use]
99    pub fn with_password(mut self, password: impl Into<Secret>) -> Self {
100        self.password = password.into();
101        self
102    }
103
104    #[must_use]
105    pub fn with_default_database(mut self, default_database: impl Into<String>) -> Self {
106        self.default_database = default_database.into();
107        self
108    }
109
110    #[must_use]
111    pub fn with_domain(mut self, domain: impl Into<String>) -> Self {
112        self.domain = Some(domain.into());
113        self
114    }
115
116    #[must_use]
117    pub fn with_ipv4_only(mut self, ipv4_only: bool) -> Self {
118        self.ipv4_only = ipv4_only;
119        self
120    }
121
122    #[must_use]
123    pub fn with_cafile<P: AsRef<std::path::Path>>(mut self, cafile: P) -> Self {
124        self.cafile = Some(cafile.as_ref().into());
125        self
126    }
127
128    #[must_use]
129    pub fn with_use_tls(mut self, use_tls: bool) -> Self {
130        self.use_tls = use_tls;
131        self
132    }
133
134    #[must_use]
135    pub fn with_compression(mut self, compression: CompressionMethod) -> Self {
136        self.compression = compression;
137        self
138    }
139
140    #[must_use]
141    pub fn with_extension(mut self, ext: Extension) -> Self {
142        self.ext = ext;
143        self
144    }
145
146    #[must_use]
147    pub fn extend(mut self, ext: impl Fn(Extension) -> Extension) -> Self {
148        self.ext = ext(self.ext);
149        self
150    }
151}
152
153/// Extra configuration options for `ClickHouse`.
154///
155/// These options are separated to allow extending the configuration capabilities of a connection
156/// without breaking the core [`ClientOptions`] that are unlikely to ever change. For this reason,
157/// `Extensions` is `non_exhaustive` so the api can change without breaking existing
158/// implementations.
159#[non_exhaustive]
160#[derive(Debug, Default, Clone, PartialEq)]
161#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
162pub struct Extension {
163    /// Options specific to (de)serializing arrow data.
164    pub arrow:          Option<ArrowOptions>,
165    /// Options specific to communicating with `ClickHouse` over their cloud offering.
166    #[cfg(feature = "cloud")]
167    pub cloud:          CloudOptions,
168    /// Options related to server/client protocol send chunking.
169    /// This may be removed, as it may be defaulted.
170    #[cfg_attr(feature = "serde", serde(default))]
171    pub chunked_send:   ChunkedProtocolMode,
172    /// Options related to server/client protocol recv chunking.
173    /// This may be removed, as it may be defaulted
174    #[cfg_attr(feature = "serde", serde(default))]
175    pub chunked_recv:   ChunkedProtocolMode,
176    /// Related to `inner_pool`, how many 'inner clients' to spawn. Currently capped at 4.
177    #[cfg(feature = "inner_pool")]
178    #[cfg_attr(feature = "serde", serde(default))]
179    pub fast_mode_size: Option<u8>,
180}
181
182/// Configuration extensions for specialized `ClickHouse` client behavior.
183///
184/// This type provides additional configuration options beyond the standard
185/// client settings, including Arrow format options and cloud-specific settings.
186impl Extension {
187    #[must_use]
188    pub fn with_arrow(mut self, options: ArrowOptions) -> Self {
189        self.arrow = Some(options);
190        self
191    }
192
193    #[must_use]
194    pub fn with_set_arrow(mut self, f: impl Fn(ArrowOptions) -> ArrowOptions) -> Self {
195        self.arrow = Some(f(self.arrow.unwrap_or_default()));
196        self
197    }
198
199    #[must_use]
200    pub fn with_chunked_send_mode(mut self, mode: ChunkedProtocolMode) -> Self {
201        self.chunked_send = mode;
202        self
203    }
204
205    #[must_use]
206    pub fn with_chunked_recv_mode(mut self, mode: ChunkedProtocolMode) -> Self {
207        self.chunked_recv = mode;
208        self
209    }
210
211    #[cfg(feature = "cloud")]
212    #[must_use]
213    pub fn with_cloud(mut self, options: CloudOptions) -> Self {
214        self.cloud = options;
215        self
216    }
217
218    #[cfg(feature = "inner_pool")]
219    #[must_use]
220    pub fn with_fast_mode_size(mut self, size: u8) -> Self {
221        self.fast_mode_size = Some(size);
222        self
223    }
224}
225
226// TODO: Remove - make the properties public!
227/// Configuration options for Arrow serialization and deserialization with `ClickHouse`.
228///
229/// The `ArrowOptions` struct defines settings that control how Apache Arrow data types
230/// are mapped to `ClickHouse` types during serialization (e.g., inserts), deserialization
231/// (e.g., queries), and schema creation (e.g., DDL operations). These options are used
232/// by [`crate::ArrowClient`] and set via [`super::builder::ClientBuilder::with_arrow_options`] or
233/// directly in [`ClientOptions`].
234///
235/// # Fields
236/// - `strings_as_strings`: If `true`, maps `ClickHouse` `String` to Arrow `Utf8`; if `false`, maps
237///   to `Binary` (default).
238/// - `use_date32_for_date`: If `true`, maps Arrow `Date32` to `ClickHouse` `Date32`; if `false`,
239///   maps to `Date` (default).
240/// - `strict_schema`: If `true`, enforces strict type mappings during serialization (inserts) and
241///   schema creation, causing errors on `ClickHouse` invariant violations (e.g.,
242///   `Nullable(LowCardinality(String))`); if `false`, attempts to correct violations (e.g., mapping
243///   to `LowCardinality(Nullable(String))`) (default).
244/// - `disable_strict_schema_ddl`: If `true`, prevents automatic strict mode during schema creation
245///   (via [`ArrowOptions::into_strict_ddl`]); if `false`, schema creation defaults to strict mode
246///   (default).
247/// - `nullable_array_default_empty`: If `true`, maps `Nullable(Array(...))` to `Array(...)` with
248///   `[]` for nulls during inserts and schema creation (if `disable_strict_schema_ddl = true`); if
249///   `false`, errors on `Nullable(Array(...))` (default).
250///
251/// # Notes
252/// - During schema creation, options are converted to strict mode (via
253///   [`ArrowOptions::into_strict_ddl`]) unless `disable_strict_schema_ddl` is `true`. Strict mode
254///   sets `strict_schema = true` and effectively enforces `nullable_array_default_empty = false`,
255///   ensuring non-nullable arrays.
256/// - When `strict_schema` is `false`, violations like `Nullable(LowCardinality(String))` are
257///   corrected, but arrays are handled per `nullable_array_default_empty` for inserts, while
258///   nullable arrays are ignored during schema creation.
259/// - If `strict_schema = true` and `nullable_array_default_empty = true`, non-array violations
260///   (e.g., `LowCardinality`) error, but arrays map to `[]` for nulls during insert. This is useful
261///   in cases where the arrow `Schema` is used to create the table, but arrays may come from
262///   different `RecordBatch`es.
263/// - This struct is `#[non_exhaustive]`, so future fields may be added (e.g., for new `ClickHouse`
264///   types or serialization options). Use [`ArrowOptions::new`] or [`ArrowOptions::default`] to
265///   construct instances.
266///
267/// # Examples
268/// ```rust,ignore
269/// use clickhouse_arrow::prelude::*;
270///
271/// let arrow_options = ArrowOptions::new()
272///     .with_strings_as_strings(true)
273///     .with_strict_schema(true)
274///     .with_nullable_array_default_empty(false);
275/// let options = ClientOptions {
276///     arrow: Some(arrow_options),
277///     ..ClientOptions::default()
278/// };
279/// ```
280#[expect(clippy::struct_excessive_bools)]
281#[non_exhaustive]
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
283#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
284pub struct ArrowOptions {
285    pub strings_as_strings:           bool,
286    pub use_date32_for_date:          bool,
287    pub strict_schema:                bool,
288    pub disable_strict_schema_ddl:    bool,
289    pub nullable_array_default_empty: bool,
290}
291
292impl Default for ArrowOptions {
293    /// Creates an `ArrowOptions` instance with default values.
294    ///
295    /// The default configuration uses relaxed type mappings suitable for most
296    /// `ClickHouse` and `Arrow` use cases:
297    /// - `ClickHouse` `String` maps to Arrow `Binary`.
298    /// - Arrow `Date32` maps to `ClickHouse` `Date`.
299    /// - Type mappings are relaxed, correcting `ClickHouse` invariant violations (e.g., mapping
300    ///   `Nullable(LowCardinality(String))` to `LowCardinality(Nullable(String))`).
301    /// - Schema creation defaults to strict mode (via [`ArrowOptions::into_strict_ddl`]).
302    /// - `Nullable(Array(...))` defaults to `Array(...)` with `[]` for nulls.
303    ///
304    /// Use this as a starting point and customize with methods like
305    /// [`ArrowOptions::with_strings_as_strings`].
306    ///
307    /// # Returns
308    /// An [`ArrowOptions`] instance with default settings.
309    ///
310    /// # Examples
311    /// ```rust,ignore
312    /// use clickhouse_arrow::arrow::ArrowOptions;
313    ///
314    /// let arrow_options = ArrowOptions::default();
315    /// println!("Nullable array default empty: {}", arrow_options.nullable_array_default_empty); // true
316    /// ```
317    fn default() -> Self { Self::new() }
318}
319
320impl ArrowOptions {
321    /// Creates a new `ArrowOptions` instance with default values.
322    ///
323    /// This method is equivalent to [`ArrowOptions::default`], initializing fields for
324    /// relaxed type mappings. Use this to start configuring Arrow
325    /// serialization/deserialization options for `ClickHouse`.
326    ///
327    /// # Returns
328    /// A new [`ArrowOptions`] instance with default settings.
329    ///
330    /// # Examples
331    /// ```rust,ignore
332    /// use clickhouse_arrow::arrow::ArrowOptions;
333    ///
334    /// let arrow_options = ArrowOptions::new();
335    /// println!("Nullable array default empty: {}", arrow_options.nullable_array_default_empty); // true
336    /// ```
337    pub const fn new() -> Self {
338        Self {
339            strings_as_strings:           false,
340            use_date32_for_date:          false,
341            strict_schema:                false,
342            disable_strict_schema_ddl:    false,
343            nullable_array_default_empty: true,
344        }
345    }
346
347    /// Creates an `ArrowOptions` instance with strict type mapping settings.
348    ///
349    /// This method configures options for strict type mappings, where `ClickHouse`
350    /// invariant violations (e.g., `Nullable(LowCardinality(String))` or
351    /// `Nullable(Array(...))`) cause errors during serialization (inserts) and schema
352    /// creation. It sets `strict_schema` to `true` and `nullable_array_default_empty` to
353    /// `false`, leaving other fields as `false`. Use this for operations where
354    /// `ClickHouse` invariants must be strictly enforced.
355    ///
356    /// # Returns
357    /// An [`ArrowOptions`] instance with strict settings.
358    ///
359    /// # Examples
360    /// ```rust,ignore
361    /// use clickhouse_arrow::arrow::ArrowOptions;
362    ///
363    /// let arrow_options = ArrowOptions::strict();
364    /// println!("Strict schema: {}", arrow_options.strict_schema); // true
365    /// ```
366    pub const fn strict() -> Self {
367        Self {
368            strings_as_strings:           false,
369            use_date32_for_date:          false,
370            strict_schema:                true,
371            disable_strict_schema_ddl:    false,
372            nullable_array_default_empty: false,
373        }
374    }
375
376    /// Converts the options to strict mode for schema creation, unless disabled.
377    ///
378    /// This method returns a new [`ArrowOptions`] with strict settings (equivalent to
379    /// [`ArrowOptions::strict`]) unless `disable_strict_schema_ddl` is `true`. If
380    /// `disable_strict_schema_ddl` is `true`, the original options are returned
381    /// unchanged. This method is called automatically during schema creation to enforce
382    /// `ClickHouse` invariants, including non-nullable arrays, unless explicitly disabled.
383    ///
384    /// # Returns
385    /// A new [`ArrowOptions`] instance with strict settings or the original options.
386    ///
387    /// # Examples
388    /// ```rust,ignore
389    /// use clickhouse_arrow::arrow::ArrowOptions;
390    ///
391    /// let options_strict_off = ArrowOptions::new()
392    ///     .with_disable_strict_schema_ddl(true)
393    ///     .into_strict_ddl();
394    /// assert!(!options_strict_off.strict_schema);
395    /// assert!(options_strict_off.nullable_array_default_empty);
396    ///
397    /// let options_strict = ArrowOptions::new()
398    ///     .with_disable_strict_schema_ddl(false) // Default
399    ///     .into_strict_ddl();
400    /// assert!(options_strict.strict_schema);
401    /// assert!(!options_strict.nullable_array_default_empty);
402    /// ```
403    #[must_use]
404    pub fn into_strict_ddl(self) -> Self {
405        if self.disable_strict_schema_ddl {
406            return self;
407        }
408
409        Self {
410            strings_as_strings: self.strings_as_strings,
411            use_date32_for_date: self.use_date32_for_date,
412            ..Self::strict()
413        }
414    }
415
416    /// Sets whether `ClickHouse` `String` types are deserialized as Arrow `Utf8`.
417    ///
418    /// By default, `ClickHouse` `String` types map to Arrow `Binary`. When this option
419    /// is enabled (`true`), they map to Arrow `Utf8`, which is more suitable for text
420    /// data. Use this to control serialization/deserialization behavior for string
421    /// columns.
422    ///
423    /// # Parameters
424    /// - `enabled`: If `true`, maps [`crate::Type::String`] to
425    ///   [`arrow::datatypes::DataType::Utf8`]; if `false`, maps to
426    ///   [`arrow::datatypes::DataType::Binary`].
427    ///
428    /// # Returns
429    /// A new [`ArrowOptions`] with the updated setting.
430    ///
431    /// # Examples
432    /// ```rust,ignore
433    /// use clickhouse_arrow::prelude::*;
434    ///
435    /// let arrow_options = ArrowOptions::new()
436    ///     .with_strings_as_strings(true);
437    /// println!("Strings as strings: {}", arrow_options.strings_as_strings); // true
438    /// ```
439    #[must_use]
440    pub fn with_strings_as_strings(mut self, enabled: bool) -> Self {
441        self.strings_as_strings = enabled;
442        self
443    }
444
445    /// Sets whether Arrow `Date32` is mapped to `ClickHouse` `Date` or `Date32`.
446    ///
447    /// By default, Arrow `Date32` maps to `ClickHouse` `Date` (days since 1970-01-01).
448    /// When this option is enabled (`true`), it maps to `ClickHouse` `Date32` (days
449    /// since 1900-01-01). Use this to control date serialization/deserialization
450    /// behavior.
451    ///
452    /// # Parameters
453    /// - `enabled`: If `true`, maps `Date32` to `ClickHouse` `Date32`; if `false`, maps to `Date`.
454    ///
455    /// # Returns
456    /// A new [`ArrowOptions`] with the updated setting.
457    ///
458    /// # Examples
459    /// ```rust,ignore
460    /// use clickhouse_arrow::prelude::*;
461    ///
462    /// let arrow_options = ArrowOptions::new()
463    ///     .with_use_date32_for_date(true);
464    /// println!("Use Date32 for Date: {}", arrow_options.use_date32_for_date); // true
465    /// ```
466    #[must_use]
467    pub fn with_use_date32_for_date(mut self, enabled: bool) -> Self {
468        self.use_date32_for_date = enabled;
469        self
470    }
471
472    /// Sets whether type mappings are strict during serialization and schema creation.
473    ///
474    /// By default, type mappings are relaxed, allowing `ClickHouse` invariant violations
475    /// (e.g., `Nullable(LowCardinality(String))`) to be corrected automatically (e.g.,
476    /// mapping to `LowCardinality(Nullable(String))`). When this option is enabled
477    /// (`true`), non-array violations cause errors during serialization (inserts) and
478    /// schema creation. Array violations are controlled by
479    /// [`ArrowOptions::with_nullable_array_default_empty`]. Schema creation defaults to
480    /// strict mode unless [`ArrowOptions::with_disable_strict_schema_ddl`] is enabled.
481    ///
482    /// # Parameters
483    /// - `enabled`: If `true`, enforces strict type mappings for non-array types; if `false`,
484    ///   allows relaxed corrections.
485    ///
486    /// # Returns
487    /// A new [`ArrowOptions`] with the updated setting.
488    ///
489    /// # Examples
490    /// ```rust,ignore
491    /// use clickhouse_arrow::arrow::ArrowOptions;
492    ///
493    /// let arrow_options = ArrowOptions::new()
494    ///     .with_strict_schema(true);
495    /// println!("Strict schema: {}", arrow_options.strict_schema); // true
496    /// ```
497    #[must_use]
498    pub fn with_strict_schema(mut self, enabled: bool) -> Self {
499        self.strict_schema = enabled;
500        self
501    }
502
503    /// Sets whether strict mode is disabled during schema creation.
504    ///
505    /// By default, schema creation (e.g., DDL operations) uses strict type mappings (via
506    /// [`ArrowOptions::into_strict_ddl`]), enforcing `ClickHouse` invariants and causing
507    /// errors on violations, including `Nullable(Array(...))`. When this option is
508    /// enabled (`true`), strict mode is disabled for schema creation, using the user’s
509    /// `strict_schema` and `nullable_array_default_empty` settings.
510    ///
511    /// # Parameters
512    /// - `enabled`: If `true`, disables strict mode for schema creation; if `false`, enables strict
513    ///   mode.
514    ///
515    /// # Returns
516    /// A new [`ArrowOptions`] with the updated setting.
517    ///
518    /// # Examples
519    /// ```rust,ignore
520    /// use clickhouse_arrow::arrow::ArrowOptions;
521    ///
522    /// let arrow_options = ArrowOptions::new()
523    ///     .with_disable_strict_schema_ddl(true);
524    /// assert!(arrow_options.disable_strict_schema_ddl);
525    /// ```
526    #[must_use]
527    pub fn with_disable_strict_schema_ddl(mut self, enabled: bool) -> Self {
528        self.disable_strict_schema_ddl = enabled;
529        self
530    }
531
532    /// Sets whether `Nullable(Array(...))` types default to empty arrays during inserts and are
533    /// coerced to non-nullable during DDL.
534    ///
535    /// By default, `Nullable(Array(...))` types are mapped to `Array(...)` with `[]` for
536    /// nulls during serialization (inserts) and schema creation (if
537    /// `disable_strict_schema_ddl = true`). When this option is disabled (`false`),
538    /// `Nullable(Array(...))` causes errors, enforcing non-nullable arrays. Schema
539    /// creation defaults to non-nullable arrays unless
540    /// [`ArrowOptions::with_disable_strict_schema_ddl`] is enabled.
541    ///
542    /// # Parameters
543    /// - `enabled`: If `true`, maps `Nullable(Array(...))` to `Array(...)` with `[]` for nulls; if
544    ///   `false`, errors on `Nullable(Array(...))`.
545    ///
546    /// # Returns
547    /// A new [`ArrowOptions`] with the updated setting.
548    ///
549    /// # Examples
550    /// ```rust,ignore
551    /// use clickhouse_arrow::arrow::ArrowOptions;
552    ///
553    /// let arrow_options = ArrowOptions::new()
554    ///     .with_nullable_array_default_empty(false);
555    /// assert!(!arrow_options.nullable_array_default_empty);
556    /// ```
557    #[must_use]
558    pub fn with_nullable_array_default_empty(mut self, enabled: bool) -> Self {
559        self.nullable_array_default_empty = enabled;
560        self
561    }
562
563    /// Sets an Arrow option by name and value.
564    ///
565    /// This method updates a specific option identified by `name` to the given boolean
566    /// `value`. Currently supported names are:
567    /// - `"strings_as_strings"`: Maps `ClickHouse` `String` to Arrow `Utf8`.
568    /// - `"use_date32_for_date"`: Maps Arrow `Date32` to `ClickHouse` `Date32`.
569    /// - `"strict_schema"`: Enforces strict type mappings for non-array types.
570    /// - `"disable_strict_schema_ddl"`: Disables strict mode for schema creation.
571    /// - `"nullable_array_default_empty"`: Maps `Nullable(Array(...))` to `Array(...)` with `[]`
572    ///   for nulls.
573    ///
574    /// If an unrecognized name is provided, a warning is logged, and the options are
575    /// returned unchanged. Use this for dynamic configuration or when options are
576    /// specified as key-value pairs.
577    ///
578    /// # Parameters
579    /// - `name`: The name of the option to set.
580    /// - `value`: The boolean value to set for the option.
581    ///
582    /// # Returns
583    /// A new [`ArrowOptions`] with the updated setting.
584    ///
585    /// # Examples
586    /// ```rust,ignore
587    /// use clickhouse_arrow::arrow::ArrowOptions;
588    ///
589    /// let arrow_options = ArrowOptions::new()
590    ///     .with_setting("strings_as_strings", true)
591    ///     .with_setting("nullable_array_default_empty", false);
592    /// assert!(arrow_options.strings_as_strings);
593    /// assert!(!arrow_options.nullable_array_default_empty);
594    /// ```
595    #[must_use]
596    pub fn with_setting(self, name: &str, value: bool) -> Self {
597        match name {
598            "strings_as_strings" => self.with_strings_as_strings(value),
599            "use_date32_for_date" => self.with_use_date32_for_date(value),
600            "strict_schema" => self.with_strict_schema(value),
601            "disable_strict_schema_ddl" => self.with_disable_strict_schema_ddl(value),
602            "nullable_array_default_empty" => self.with_nullable_array_default_empty(value),
603            k => {
604                warn!("Unrecognized option for ArrowOptions: {k}");
605                self
606            }
607        }
608    }
609}
610
611impl<'a, S, I> From<I> for ArrowOptions
612where
613    S: AsRef<str> + 'a,
614    I: Iterator<Item = &'a (S, bool)> + 'a,
615{
616    /// Creates an `ArrowOptions` instance from an iterator of key-value pairs.
617    ///
618    /// This method constructs an [`ArrowOptions`] by applying settings from an iterator
619    /// of `(key, value)` pairs, where `key` is a string (e.g., `"strict_schema"`) and
620    /// `value` is a boolean. It uses [`ArrowOptions::with_setting`] to apply each
621    /// setting. Unrecognized keys trigger a warning but do not cause an error.
622    ///
623    /// See currently supported keys by inspecting [`ArrowOptions::with_setting`].
624    ///
625    /// # Parameters
626    /// - `value`: An iterator of `(key, value)` pairs, where `key` is a string-like type and
627    ///   `value` is a boolean.
628    ///
629    /// # Returns
630    /// An [`ArrowOptions`] instance with the applied settings.
631    ///
632    /// # Examples
633    /// ```rust,ignore
634    /// use clickhouse_arrow::arrow::ArrowOptions;
635    ///
636    /// let settings = vec![("strings_as_strings", true), ("nullable_array_default_empty", false)];
637    /// let arrow_options: ArrowOptions = settings.iter().collect();
638    /// assert!(arrow_options.strings_as_strings);
639    /// assert!(!arrow_options.nullable_array_default_empty);
640    /// ```
641    fn from(value: I) -> Self {
642        let mut options = ArrowOptions::default();
643        for (k, v) in value {
644            options = options.with_setting(k.as_ref(), *v);
645        }
646        options
647    }
648}
649
650impl<'a> FromIterator<(&'a str, bool)> for ArrowOptions {
651    /// Creates an `ArrowOptions` instance from an iterator of string-boolean pairs.
652    ///
653    /// This method constructs an [`ArrowOptions`] by applying settings from an iterator
654    /// of `(key, value)` pairs, where `key` is a string slice (e.g., `"strict_schema"`)
655    /// and `value` is a boolean. It uses [`ArrowOptions::with_setting`] to apply each
656    /// setting. Unrecognized keys trigger a warning but do not cause an error.
657    ///
658    /// See currently supported keys by inspecting [`ArrowOptions::with_setting`].
659    ///
660    /// # Parameters
661    /// - `iter`: An iterator of `(key, value)` pairs, where `key` is a string slice and `value` is
662    ///   a boolean.
663    ///
664    /// # Returns
665    /// An [`ArrowOptions`] instance with the applied settings.
666    ///
667    /// # Examples
668    /// ```rust,ignore
669    /// use clickhouse_arrow::arrow::ArrowOptions;
670    ///
671    /// let settings = vec![("strings_as_strings", true), ("nullable_array_default_empty", false)];
672    /// let arrow_options = ArrowOptions::from_iter(settings);
673    /// assert!(arrow_options.strings_as_strings);
674    /// assert!(!arrow_options.nullable_array_default_empty);
675    /// ```
676    fn from_iter<I: IntoIterator<Item = (&'a str, bool)>>(iter: I) -> Self {
677        let mut options = ArrowOptions::default();
678        for (k, v) in iter {
679            options = options.with_setting(k, v);
680        }
681        options
682    }
683}
684
685/// Configuration options for connecting to `ClickHouse` cloud instances.
686///
687/// The `CloudOptions` struct defines settings specific to `ClickHouse` cloud
688/// deployments, used within [`ClientOptions`]. These options control the behavior
689/// of cloud wakeup pings to ensure the instance is active before connecting.
690///
691/// # Fields
692/// - `timeout`: Optional timeout (in seconds) for the cloud wakeup ping; if `None`, uses a default
693///   timeout.
694/// - `wakeup`: If `true`, sends a wakeup ping before connecting; if `false`, skips the ping.
695///
696/// # Feature
697/// Requires the `cloud` feature to be enabled.
698///
699/// # Examples
700/// ```rust,ignore
701/// use clickhouse_arrow::prelude::*;
702///
703/// let cloud_options = CloudOptions {
704///     timeout: Some(10),
705///     wakeup: true,
706/// };
707/// let options = ClientOptions {
708///     cloud: cloud_options,
709///     ..ClientOptions::default()
710/// };
711/// ```
712#[derive(Debug, Clone, Copy, Default, PartialEq)]
713#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
714pub struct CloudOptions {
715    #[cfg_attr(feature = "serde", serde(default))]
716    pub timeout: Option<u64>,
717    #[cfg_attr(feature = "serde", serde(default))]
718    pub wakeup:  bool,
719}