1use std::time::Duration;
4
5use mssql_auth::Credentials;
6#[cfg(feature = "tls")]
7use mssql_tls::TlsConfig;
8use tds_protocol::version::TdsVersion;
9
10#[derive(Debug, Clone)]
15pub struct RedirectConfig {
16 pub max_redirects: u8,
18 pub follow_redirects: bool,
20}
21
22impl Default for RedirectConfig {
23 fn default() -> Self {
24 Self {
25 max_redirects: 2,
26 follow_redirects: true,
27 }
28 }
29}
30
31impl RedirectConfig {
32 #[must_use]
34 pub fn new() -> Self {
35 Self::default()
36 }
37
38 #[must_use]
40 pub fn max_redirects(mut self, max: u8) -> Self {
41 self.max_redirects = max;
42 self
43 }
44
45 #[must_use]
47 pub fn follow_redirects(mut self, follow: bool) -> Self {
48 self.follow_redirects = follow;
49 self
50 }
51
52 #[must_use]
57 pub fn no_follow() -> Self {
58 Self {
59 max_redirects: 0,
60 follow_redirects: false,
61 }
62 }
63}
64
65#[derive(Debug, Clone)]
70pub struct TimeoutConfig {
71 pub connect_timeout: Duration,
73 pub tls_timeout: Duration,
75 pub login_timeout: Duration,
77 pub command_timeout: Duration,
79 pub idle_timeout: Duration,
81 pub keepalive_interval: Option<Duration>,
83}
84
85impl Default for TimeoutConfig {
86 fn default() -> Self {
87 Self {
88 connect_timeout: Duration::from_secs(15),
89 tls_timeout: Duration::from_secs(10),
90 login_timeout: Duration::from_secs(30),
91 command_timeout: Duration::from_secs(30),
92 idle_timeout: Duration::from_secs(300),
93 keepalive_interval: Some(Duration::from_secs(30)),
94 }
95 }
96}
97
98impl TimeoutConfig {
99 #[must_use]
101 pub fn new() -> Self {
102 Self::default()
103 }
104
105 #[must_use]
107 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
108 self.connect_timeout = timeout;
109 self
110 }
111
112 #[must_use]
114 pub fn tls_timeout(mut self, timeout: Duration) -> Self {
115 self.tls_timeout = timeout;
116 self
117 }
118
119 #[must_use]
121 pub fn login_timeout(mut self, timeout: Duration) -> Self {
122 self.login_timeout = timeout;
123 self
124 }
125
126 #[must_use]
128 pub fn command_timeout(mut self, timeout: Duration) -> Self {
129 self.command_timeout = timeout;
130 self
131 }
132
133 #[must_use]
135 pub fn idle_timeout(mut self, timeout: Duration) -> Self {
136 self.idle_timeout = timeout;
137 self
138 }
139
140 #[must_use]
142 pub fn keepalive_interval(mut self, interval: Option<Duration>) -> Self {
143 self.keepalive_interval = interval;
144 self
145 }
146
147 #[must_use]
149 pub fn no_keepalive(mut self) -> Self {
150 self.keepalive_interval = None;
151 self
152 }
153
154 #[must_use]
156 pub fn total_connect_timeout(&self) -> Duration {
157 self.connect_timeout + self.tls_timeout + self.login_timeout
158 }
159}
160
161#[derive(Debug, Clone)]
166pub struct RetryPolicy {
167 pub max_retries: u32,
169 pub initial_backoff: Duration,
171 pub max_backoff: Duration,
173 pub backoff_multiplier: f64,
175 pub jitter: bool,
177}
178
179impl Default for RetryPolicy {
180 fn default() -> Self {
181 Self {
182 max_retries: 3,
183 initial_backoff: Duration::from_millis(100),
184 max_backoff: Duration::from_secs(30),
185 backoff_multiplier: 2.0,
186 jitter: true,
187 }
188 }
189}
190
191impl RetryPolicy {
192 #[must_use]
194 pub fn new() -> Self {
195 Self::default()
196 }
197
198 #[must_use]
200 pub fn max_retries(mut self, max: u32) -> Self {
201 self.max_retries = max;
202 self
203 }
204
205 #[must_use]
207 pub fn initial_backoff(mut self, backoff: Duration) -> Self {
208 self.initial_backoff = backoff;
209 self
210 }
211
212 #[must_use]
214 pub fn max_backoff(mut self, backoff: Duration) -> Self {
215 self.max_backoff = backoff;
216 self
217 }
218
219 #[must_use]
221 pub fn backoff_multiplier(mut self, multiplier: f64) -> Self {
222 self.backoff_multiplier = multiplier;
223 self
224 }
225
226 #[must_use]
228 pub fn jitter(mut self, enabled: bool) -> Self {
229 self.jitter = enabled;
230 self
231 }
232
233 #[must_use]
235 pub fn no_retry() -> Self {
236 Self {
237 max_retries: 0,
238 ..Self::default()
239 }
240 }
241
242 #[must_use]
246 pub fn backoff_for_attempt(&self, attempt: u32) -> Duration {
247 if attempt == 0 {
248 return Duration::ZERO;
249 }
250
251 let base = self.initial_backoff.as_millis() as f64
252 * self
253 .backoff_multiplier
254 .powi(attempt.saturating_sub(1) as i32);
255 let capped = base.min(self.max_backoff.as_millis() as f64);
256
257 if self.jitter {
258 Duration::from_millis(capped as u64)
261 } else {
262 Duration::from_millis(capped as u64)
263 }
264 }
265
266 #[must_use]
268 pub fn should_retry(&self, attempt: u32) -> bool {
269 attempt < self.max_retries
270 }
271}
272
273#[derive(Debug, Clone)]
279#[non_exhaustive]
280pub struct Config {
281 pub host: String,
283
284 pub port: u16,
286
287 pub database: Option<String>,
289
290 pub credentials: Credentials,
292
293 #[cfg(feature = "tls")]
295 pub tls: TlsConfig,
296
297 pub application_name: String,
299
300 pub connect_timeout: Duration,
302
303 pub command_timeout: Duration,
305
306 pub packet_size: u16,
308
309 pub strict_mode: bool,
311
312 pub trust_server_certificate: bool,
314
315 pub instance: Option<String>,
317
318 pub mars: bool,
320
321 pub encrypt: bool,
325
326 pub no_tls: bool,
345
346 pub redirect: RedirectConfig,
348
349 pub retry: RetryPolicy,
351
352 pub timeouts: TimeoutConfig,
354
355 pub tds_version: TdsVersion,
368}
369
370impl Default for Config {
371 fn default() -> Self {
372 let timeouts = TimeoutConfig::default();
373 Self {
374 host: "localhost".to_string(),
375 port: 1433,
376 database: None,
377 credentials: Credentials::sql_server("", ""),
378 #[cfg(feature = "tls")]
379 tls: TlsConfig::default(),
380 application_name: "mssql-client".to_string(),
381 connect_timeout: timeouts.connect_timeout,
382 command_timeout: timeouts.command_timeout,
383 packet_size: 4096,
384 strict_mode: false,
385 trust_server_certificate: false,
386 instance: None,
387 mars: false,
388 encrypt: true, no_tls: false, redirect: RedirectConfig::default(),
391 retry: RetryPolicy::default(),
392 timeouts,
393 tds_version: TdsVersion::V7_4, }
395 }
396}
397
398impl Config {
399 #[must_use]
401 pub fn new() -> Self {
402 Self::default()
403 }
404
405 pub fn from_connection_string(conn_str: &str) -> Result<Self, crate::error::Error> {
412 let mut config = Self::default();
413
414 for part in conn_str.split(';') {
415 let part = part.trim();
416 if part.is_empty() {
417 continue;
418 }
419
420 let (key, value) = part
421 .split_once('=')
422 .ok_or_else(|| crate::error::Error::Config(format!("invalid key-value: {part}")))?;
423
424 let key = key.trim().to_lowercase();
425 let value = value.trim();
426
427 match key.as_str() {
428 "server" | "data source" | "host" => {
429 if let Some((host, port_or_instance)) = value.split_once(',') {
431 config.host = host.to_string();
432 config.port = port_or_instance.parse().map_err(|_| {
433 crate::error::Error::Config(format!("invalid port: {port_or_instance}"))
434 })?;
435 } else if let Some((host, instance)) = value.split_once('\\') {
436 config.host = host.to_string();
437 config.instance = Some(instance.to_string());
438 } else {
439 config.host = value.to_string();
440 }
441 }
442 "port" => {
443 config.port = value.parse().map_err(|_| {
444 crate::error::Error::Config(format!("invalid port: {value}"))
445 })?;
446 }
447 "database" | "initial catalog" => {
448 config.database = Some(value.to_string());
449 }
450 "user id" | "uid" | "user" => {
451 if let Credentials::SqlServer { password, .. } = &config.credentials {
453 config.credentials =
454 Credentials::sql_server(value.to_string(), password.clone());
455 }
456 }
457 "password" | "pwd" => {
458 if let Credentials::SqlServer { username, .. } = &config.credentials {
460 config.credentials =
461 Credentials::sql_server(username.clone(), value.to_string());
462 }
463 }
464 "application name" | "app" => {
465 config.application_name = value.to_string();
466 }
467 "connect timeout" | "connection timeout" => {
468 let secs: u64 = value.parse().map_err(|_| {
469 crate::error::Error::Config(format!("invalid timeout: {value}"))
470 })?;
471 config.connect_timeout = Duration::from_secs(secs);
472 }
473 "command timeout" => {
474 let secs: u64 = value.parse().map_err(|_| {
475 crate::error::Error::Config(format!("invalid timeout: {value}"))
476 })?;
477 config.command_timeout = Duration::from_secs(secs);
478 }
479 "trustservercertificate" | "trust server certificate" => {
480 config.trust_server_certificate = value.eq_ignore_ascii_case("true")
481 || value.eq_ignore_ascii_case("yes")
482 || value == "1";
483 }
484 "encrypt" => {
485 if value.eq_ignore_ascii_case("strict") {
487 config.strict_mode = true;
488 config.encrypt = true;
489 config.no_tls = false;
490 } else if value.eq_ignore_ascii_case("no_tls") {
491 config.no_tls = true;
494 config.encrypt = false;
495 } else if value.eq_ignore_ascii_case("true")
496 || value.eq_ignore_ascii_case("yes")
497 || value == "1"
498 {
499 config.encrypt = true;
500 config.no_tls = false;
501 } else if value.eq_ignore_ascii_case("false")
502 || value.eq_ignore_ascii_case("no")
503 || value == "0"
504 {
505 config.encrypt = false;
506 config.no_tls = false;
507 }
508 }
509 "multipleactiveresultsets" | "mars" => {
510 config.mars = value.eq_ignore_ascii_case("true")
511 || value.eq_ignore_ascii_case("yes")
512 || value == "1";
513 }
514 "packet size" => {
515 config.packet_size = value.parse().map_err(|_| {
516 crate::error::Error::Config(format!("invalid packet size: {value}"))
517 })?;
518 }
519 "tdsversion" | "tds version" | "protocolversion" | "protocol version" => {
520 config.tds_version = TdsVersion::parse(value).ok_or_else(|| {
523 crate::error::Error::Config(format!(
524 "invalid TDS version: {value}. Supported values: 7.3, 7.3A, 7.3B, 7.4, 8.0"
525 ))
526 })?;
527 if config.tds_version.is_tds_8() {
529 config.strict_mode = true;
530 }
531 }
532 _ => {
533 tracing::debug!(
535 key = key,
536 value = value,
537 "ignoring unknown connection string option"
538 );
539 }
540 }
541 }
542
543 Ok(config)
544 }
545
546 #[must_use]
548 pub fn host(mut self, host: impl Into<String>) -> Self {
549 self.host = host.into();
550 self
551 }
552
553 #[must_use]
555 pub fn port(mut self, port: u16) -> Self {
556 self.port = port;
557 self
558 }
559
560 #[must_use]
562 pub fn database(mut self, database: impl Into<String>) -> Self {
563 self.database = Some(database.into());
564 self
565 }
566
567 #[must_use]
569 pub fn credentials(mut self, credentials: Credentials) -> Self {
570 self.credentials = credentials;
571 self
572 }
573
574 #[must_use]
576 pub fn application_name(mut self, name: impl Into<String>) -> Self {
577 self.application_name = name.into();
578 self
579 }
580
581 #[must_use]
583 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
584 self.connect_timeout = timeout;
585 self
586 }
587
588 #[must_use]
590 pub fn trust_server_certificate(mut self, trust: bool) -> Self {
591 self.trust_server_certificate = trust;
592 #[cfg(feature = "tls")]
593 {
594 self.tls = self.tls.trust_server_certificate(trust);
595 }
596 self
597 }
598
599 #[must_use]
601 pub fn strict_mode(mut self, enabled: bool) -> Self {
602 self.strict_mode = enabled;
603 #[cfg(feature = "tls")]
604 {
605 self.tls = self.tls.strict_mode(enabled);
606 }
607 if enabled {
608 self.tds_version = TdsVersion::V8_0;
609 }
610 self
611 }
612
613 #[must_use]
637 pub fn tds_version(mut self, version: TdsVersion) -> Self {
638 self.tds_version = version;
639 if version.is_tds_8() {
641 self.strict_mode = true;
642 #[cfg(feature = "tls")]
643 {
644 self.tls = self.tls.strict_mode(true);
645 }
646 }
647 self
648 }
649
650 #[must_use]
658 pub fn encrypt(mut self, enabled: bool) -> Self {
659 self.encrypt = enabled;
660 self
661 }
662
663 #[must_use]
698 pub fn no_tls(mut self, enabled: bool) -> Self {
699 self.no_tls = enabled;
700 if enabled {
701 self.encrypt = false;
702 }
703 self
704 }
705
706 #[must_use]
708 pub fn with_host(mut self, host: &str) -> Self {
709 self.host = host.to_string();
710 self
711 }
712
713 #[must_use]
715 pub fn with_port(mut self, port: u16) -> Self {
716 self.port = port;
717 self
718 }
719
720 #[must_use]
722 pub fn redirect(mut self, redirect: RedirectConfig) -> Self {
723 self.redirect = redirect;
724 self
725 }
726
727 #[must_use]
729 pub fn max_redirects(mut self, max: u8) -> Self {
730 self.redirect.max_redirects = max;
731 self
732 }
733
734 #[must_use]
736 pub fn retry(mut self, retry: RetryPolicy) -> Self {
737 self.retry = retry;
738 self
739 }
740
741 #[must_use]
743 pub fn max_retries(mut self, max: u32) -> Self {
744 self.retry.max_retries = max;
745 self
746 }
747
748 #[must_use]
750 pub fn timeouts(mut self, timeouts: TimeoutConfig) -> Self {
751 self.connect_timeout = timeouts.connect_timeout;
753 self.command_timeout = timeouts.command_timeout;
754 self.timeouts = timeouts;
755 self
756 }
757}
758
759#[cfg(test)]
760#[allow(clippy::unwrap_used)]
761mod tests {
762 use super::*;
763
764 #[test]
765 fn test_connection_string_parsing() {
766 let config = Config::from_connection_string(
767 "Server=localhost;Database=test;User Id=sa;Password=secret;",
768 )
769 .unwrap();
770
771 assert_eq!(config.host, "localhost");
772 assert_eq!(config.database, Some("test".to_string()));
773 }
774
775 #[test]
776 fn test_connection_string_with_port() {
777 let config =
778 Config::from_connection_string("Server=localhost,1434;Database=test;").unwrap();
779
780 assert_eq!(config.host, "localhost");
781 assert_eq!(config.port, 1434);
782 }
783
784 #[test]
785 fn test_connection_string_with_instance() {
786 let config =
787 Config::from_connection_string("Server=localhost\\SQLEXPRESS;Database=test;").unwrap();
788
789 assert_eq!(config.host, "localhost");
790 assert_eq!(config.instance, Some("SQLEXPRESS".to_string()));
791 }
792
793 #[test]
794 fn test_redirect_config_defaults() {
795 let config = RedirectConfig::default();
796 assert_eq!(config.max_redirects, 2);
797 assert!(config.follow_redirects);
798 }
799
800 #[test]
801 fn test_redirect_config_builder() {
802 let config = RedirectConfig::new()
803 .max_redirects(5)
804 .follow_redirects(false);
805 assert_eq!(config.max_redirects, 5);
806 assert!(!config.follow_redirects);
807 }
808
809 #[test]
810 fn test_redirect_config_no_follow() {
811 let config = RedirectConfig::no_follow();
812 assert_eq!(config.max_redirects, 0);
813 assert!(!config.follow_redirects);
814 }
815
816 #[test]
817 fn test_config_redirect_builder() {
818 let config = Config::new().max_redirects(3);
819 assert_eq!(config.redirect.max_redirects, 3);
820
821 let config2 = Config::new().redirect(RedirectConfig::no_follow());
822 assert!(!config2.redirect.follow_redirects);
823 }
824
825 #[test]
826 fn test_retry_policy_defaults() {
827 let policy = RetryPolicy::default();
828 assert_eq!(policy.max_retries, 3);
829 assert_eq!(policy.initial_backoff, Duration::from_millis(100));
830 assert_eq!(policy.max_backoff, Duration::from_secs(30));
831 assert!((policy.backoff_multiplier - 2.0).abs() < f64::EPSILON);
832 assert!(policy.jitter);
833 }
834
835 #[test]
836 fn test_retry_policy_builder() {
837 let policy = RetryPolicy::new()
838 .max_retries(5)
839 .initial_backoff(Duration::from_millis(200))
840 .max_backoff(Duration::from_secs(60))
841 .backoff_multiplier(3.0)
842 .jitter(false);
843
844 assert_eq!(policy.max_retries, 5);
845 assert_eq!(policy.initial_backoff, Duration::from_millis(200));
846 assert_eq!(policy.max_backoff, Duration::from_secs(60));
847 assert!((policy.backoff_multiplier - 3.0).abs() < f64::EPSILON);
848 assert!(!policy.jitter);
849 }
850
851 #[test]
852 fn test_retry_policy_no_retry() {
853 let policy = RetryPolicy::no_retry();
854 assert_eq!(policy.max_retries, 0);
855 assert!(!policy.should_retry(0));
856 }
857
858 #[test]
859 fn test_retry_policy_should_retry() {
860 let policy = RetryPolicy::new().max_retries(3);
861 assert!(policy.should_retry(0));
862 assert!(policy.should_retry(1));
863 assert!(policy.should_retry(2));
864 assert!(!policy.should_retry(3));
865 assert!(!policy.should_retry(4));
866 }
867
868 #[test]
869 fn test_retry_policy_backoff_calculation() {
870 let policy = RetryPolicy::new()
871 .initial_backoff(Duration::from_millis(100))
872 .backoff_multiplier(2.0)
873 .max_backoff(Duration::from_secs(10))
874 .jitter(false);
875
876 assert_eq!(policy.backoff_for_attempt(0), Duration::ZERO);
877 assert_eq!(policy.backoff_for_attempt(1), Duration::from_millis(100));
878 assert_eq!(policy.backoff_for_attempt(2), Duration::from_millis(200));
879 assert_eq!(policy.backoff_for_attempt(3), Duration::from_millis(400));
880 }
881
882 #[test]
883 fn test_retry_policy_backoff_capped() {
884 let policy = RetryPolicy::new()
885 .initial_backoff(Duration::from_secs(1))
886 .backoff_multiplier(10.0)
887 .max_backoff(Duration::from_secs(5))
888 .jitter(false);
889
890 assert_eq!(policy.backoff_for_attempt(3), Duration::from_secs(5));
892 }
893
894 #[test]
895 fn test_config_retry_builder() {
896 let config = Config::new().max_retries(5);
897 assert_eq!(config.retry.max_retries, 5);
898
899 let config2 = Config::new().retry(RetryPolicy::no_retry());
900 assert_eq!(config2.retry.max_retries, 0);
901 }
902
903 #[test]
904 fn test_timeout_config_defaults() {
905 let config = TimeoutConfig::default();
906 assert_eq!(config.connect_timeout, Duration::from_secs(15));
907 assert_eq!(config.tls_timeout, Duration::from_secs(10));
908 assert_eq!(config.login_timeout, Duration::from_secs(30));
909 assert_eq!(config.command_timeout, Duration::from_secs(30));
910 assert_eq!(config.idle_timeout, Duration::from_secs(300));
911 assert_eq!(config.keepalive_interval, Some(Duration::from_secs(30)));
912 }
913
914 #[test]
915 fn test_timeout_config_builder() {
916 let config = TimeoutConfig::new()
917 .connect_timeout(Duration::from_secs(5))
918 .tls_timeout(Duration::from_secs(3))
919 .login_timeout(Duration::from_secs(10))
920 .command_timeout(Duration::from_secs(60))
921 .idle_timeout(Duration::from_secs(600))
922 .keepalive_interval(Some(Duration::from_secs(60)));
923
924 assert_eq!(config.connect_timeout, Duration::from_secs(5));
925 assert_eq!(config.tls_timeout, Duration::from_secs(3));
926 assert_eq!(config.login_timeout, Duration::from_secs(10));
927 assert_eq!(config.command_timeout, Duration::from_secs(60));
928 assert_eq!(config.idle_timeout, Duration::from_secs(600));
929 assert_eq!(config.keepalive_interval, Some(Duration::from_secs(60)));
930 }
931
932 #[test]
933 fn test_timeout_config_no_keepalive() {
934 let config = TimeoutConfig::new().no_keepalive();
935 assert_eq!(config.keepalive_interval, None);
936 }
937
938 #[test]
939 fn test_timeout_config_total_connect() {
940 let config = TimeoutConfig::new()
941 .connect_timeout(Duration::from_secs(5))
942 .tls_timeout(Duration::from_secs(3))
943 .login_timeout(Duration::from_secs(10));
944
945 assert_eq!(config.total_connect_timeout(), Duration::from_secs(18));
947 }
948
949 #[test]
950 fn test_config_timeouts_builder() {
951 let timeouts = TimeoutConfig::new()
952 .connect_timeout(Duration::from_secs(5))
953 .command_timeout(Duration::from_secs(60));
954
955 let config = Config::new().timeouts(timeouts);
956 assert_eq!(config.timeouts.connect_timeout, Duration::from_secs(5));
957 assert_eq!(config.timeouts.command_timeout, Duration::from_secs(60));
958 assert_eq!(config.connect_timeout, Duration::from_secs(5));
960 assert_eq!(config.command_timeout, Duration::from_secs(60));
961 }
962
963 #[test]
964 fn test_tds_version_default() {
965 let config = Config::default();
966 assert_eq!(config.tds_version, TdsVersion::V7_4);
967 assert!(!config.strict_mode);
968 }
969
970 #[test]
971 fn test_tds_version_builder() {
972 let config = Config::new().tds_version(TdsVersion::V7_3A);
973 assert_eq!(config.tds_version, TdsVersion::V7_3A);
974 assert!(!config.strict_mode);
975
976 let config = Config::new().tds_version(TdsVersion::V7_3B);
977 assert_eq!(config.tds_version, TdsVersion::V7_3B);
978 assert!(!config.strict_mode);
979
980 let config = Config::new().tds_version(TdsVersion::V8_0);
982 assert_eq!(config.tds_version, TdsVersion::V8_0);
983 assert!(config.strict_mode);
984 }
985
986 #[test]
987 fn test_strict_mode_sets_tds_8() {
988 let config = Config::new().strict_mode(true);
989 assert!(config.strict_mode);
990 assert_eq!(config.tds_version, TdsVersion::V8_0);
991 }
992
993 #[test]
994 fn test_connection_string_tds_version() {
995 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.3;").unwrap();
997 assert_eq!(config.tds_version, TdsVersion::V7_3A);
998
999 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.3A;").unwrap();
1001 assert_eq!(config.tds_version, TdsVersion::V7_3A);
1002
1003 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.3B;").unwrap();
1005 assert_eq!(config.tds_version, TdsVersion::V7_3B);
1006
1007 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.4;").unwrap();
1009 assert_eq!(config.tds_version, TdsVersion::V7_4);
1010
1011 let config = Config::from_connection_string("Server=localhost;TDSVersion=8.0;").unwrap();
1013 assert_eq!(config.tds_version, TdsVersion::V8_0);
1014 assert!(config.strict_mode);
1015
1016 let config =
1018 Config::from_connection_string("Server=localhost;ProtocolVersion=7.3;").unwrap();
1019 assert_eq!(config.tds_version, TdsVersion::V7_3A);
1020 }
1021
1022 #[test]
1023 fn test_connection_string_invalid_tds_version() {
1024 let result = Config::from_connection_string("Server=localhost;TDSVersion=invalid;");
1025 assert!(result.is_err());
1026
1027 let result = Config::from_connection_string("Server=localhost;TDSVersion=9.0;");
1028 assert!(result.is_err());
1029 }
1030
1031 #[test]
1032 fn test_connection_string_no_tls() {
1033 let config = Config::from_connection_string("Server=legacy;Encrypt=no_tls;").unwrap();
1035 assert!(config.no_tls);
1036 assert!(!config.encrypt);
1037 assert!(!config.strict_mode);
1038
1039 let config = Config::from_connection_string("Server=legacy;Encrypt=no_tls;").unwrap();
1041 assert!(config.no_tls);
1042
1043 let config = Config::from_connection_string("Server=localhost;Encrypt=true;").unwrap();
1045 assert!(!config.no_tls);
1046 assert!(config.encrypt);
1047
1048 let config = Config::from_connection_string("Server=localhost;Encrypt=strict;").unwrap();
1050 assert!(!config.no_tls);
1051 assert!(config.encrypt);
1052 assert!(config.strict_mode);
1053 }
1054
1055 #[test]
1056 fn test_no_tls_builder() {
1057 let config = Config::new().no_tls(true);
1059 assert!(config.no_tls);
1060 assert!(!config.encrypt);
1061
1062 let config = Config::new().no_tls(true).no_tls(false);
1064 assert!(!config.no_tls);
1065 }
1066}