1use std::time::Duration;
4
5use mssql_auth::Credentials;
6use mssql_tls::TlsConfig;
7use tds_protocol::version::TdsVersion;
8
9#[derive(Debug, Clone)]
14pub struct RedirectConfig {
15 pub max_redirects: u8,
17 pub follow_redirects: bool,
19}
20
21impl Default for RedirectConfig {
22 fn default() -> Self {
23 Self {
24 max_redirects: 2,
25 follow_redirects: true,
26 }
27 }
28}
29
30impl RedirectConfig {
31 #[must_use]
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 #[must_use]
39 pub fn max_redirects(mut self, max: u8) -> Self {
40 self.max_redirects = max;
41 self
42 }
43
44 #[must_use]
46 pub fn follow_redirects(mut self, follow: bool) -> Self {
47 self.follow_redirects = follow;
48 self
49 }
50
51 #[must_use]
56 pub fn no_follow() -> Self {
57 Self {
58 max_redirects: 0,
59 follow_redirects: false,
60 }
61 }
62}
63
64#[derive(Debug, Clone)]
69pub struct TimeoutConfig {
70 pub connect_timeout: Duration,
72 pub tls_timeout: Duration,
74 pub login_timeout: Duration,
76 pub command_timeout: Duration,
78 pub idle_timeout: Duration,
80 pub keepalive_interval: Option<Duration>,
82}
83
84impl Default for TimeoutConfig {
85 fn default() -> Self {
86 Self {
87 connect_timeout: Duration::from_secs(15),
88 tls_timeout: Duration::from_secs(10),
89 login_timeout: Duration::from_secs(30),
90 command_timeout: Duration::from_secs(30),
91 idle_timeout: Duration::from_secs(300),
92 keepalive_interval: Some(Duration::from_secs(30)),
93 }
94 }
95}
96
97impl TimeoutConfig {
98 #[must_use]
100 pub fn new() -> Self {
101 Self::default()
102 }
103
104 #[must_use]
106 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
107 self.connect_timeout = timeout;
108 self
109 }
110
111 #[must_use]
113 pub fn tls_timeout(mut self, timeout: Duration) -> Self {
114 self.tls_timeout = timeout;
115 self
116 }
117
118 #[must_use]
120 pub fn login_timeout(mut self, timeout: Duration) -> Self {
121 self.login_timeout = timeout;
122 self
123 }
124
125 #[must_use]
127 pub fn command_timeout(mut self, timeout: Duration) -> Self {
128 self.command_timeout = timeout;
129 self
130 }
131
132 #[must_use]
134 pub fn idle_timeout(mut self, timeout: Duration) -> Self {
135 self.idle_timeout = timeout;
136 self
137 }
138
139 #[must_use]
141 pub fn keepalive_interval(mut self, interval: Option<Duration>) -> Self {
142 self.keepalive_interval = interval;
143 self
144 }
145
146 #[must_use]
148 pub fn no_keepalive(mut self) -> Self {
149 self.keepalive_interval = None;
150 self
151 }
152
153 #[must_use]
155 pub fn total_connect_timeout(&self) -> Duration {
156 self.connect_timeout + self.tls_timeout + self.login_timeout
157 }
158}
159
160#[derive(Debug, Clone)]
165pub struct RetryPolicy {
166 pub max_retries: u32,
168 pub initial_backoff: Duration,
170 pub max_backoff: Duration,
172 pub backoff_multiplier: f64,
174 pub jitter: bool,
176}
177
178impl Default for RetryPolicy {
179 fn default() -> Self {
180 Self {
181 max_retries: 3,
182 initial_backoff: Duration::from_millis(100),
183 max_backoff: Duration::from_secs(30),
184 backoff_multiplier: 2.0,
185 jitter: true,
186 }
187 }
188}
189
190impl RetryPolicy {
191 #[must_use]
193 pub fn new() -> Self {
194 Self::default()
195 }
196
197 #[must_use]
199 pub fn max_retries(mut self, max: u32) -> Self {
200 self.max_retries = max;
201 self
202 }
203
204 #[must_use]
206 pub fn initial_backoff(mut self, backoff: Duration) -> Self {
207 self.initial_backoff = backoff;
208 self
209 }
210
211 #[must_use]
213 pub fn max_backoff(mut self, backoff: Duration) -> Self {
214 self.max_backoff = backoff;
215 self
216 }
217
218 #[must_use]
220 pub fn backoff_multiplier(mut self, multiplier: f64) -> Self {
221 self.backoff_multiplier = multiplier;
222 self
223 }
224
225 #[must_use]
227 pub fn jitter(mut self, enabled: bool) -> Self {
228 self.jitter = enabled;
229 self
230 }
231
232 #[must_use]
234 pub fn no_retry() -> Self {
235 Self {
236 max_retries: 0,
237 ..Self::default()
238 }
239 }
240
241 #[must_use]
245 pub fn backoff_for_attempt(&self, attempt: u32) -> Duration {
246 if attempt == 0 {
247 return Duration::ZERO;
248 }
249
250 let base = self.initial_backoff.as_millis() as f64
251 * self
252 .backoff_multiplier
253 .powi(attempt.saturating_sub(1) as i32);
254 let capped = base.min(self.max_backoff.as_millis() as f64);
255
256 if self.jitter {
257 Duration::from_millis(capped as u64)
260 } else {
261 Duration::from_millis(capped as u64)
262 }
263 }
264
265 #[must_use]
267 pub fn should_retry(&self, attempt: u32) -> bool {
268 attempt < self.max_retries
269 }
270}
271
272#[derive(Debug, Clone)]
278#[non_exhaustive]
279pub struct Config {
280 pub host: String,
282
283 pub port: u16,
285
286 pub database: Option<String>,
288
289 pub credentials: Credentials,
291
292 pub tls: TlsConfig,
294
295 pub application_name: String,
297
298 pub connect_timeout: Duration,
300
301 pub command_timeout: Duration,
303
304 pub packet_size: u16,
306
307 pub strict_mode: bool,
309
310 pub trust_server_certificate: bool,
312
313 pub instance: Option<String>,
315
316 pub mars: bool,
318
319 pub encrypt: bool,
323
324 pub no_tls: bool,
343
344 pub redirect: RedirectConfig,
346
347 pub retry: RetryPolicy,
349
350 pub timeouts: TimeoutConfig,
352
353 pub tds_version: TdsVersion,
366}
367
368impl Default for Config {
369 fn default() -> Self {
370 let timeouts = TimeoutConfig::default();
371 Self {
372 host: "localhost".to_string(),
373 port: 1433,
374 database: None,
375 credentials: Credentials::sql_server("", ""),
376 tls: TlsConfig::default(),
377 application_name: "mssql-client".to_string(),
378 connect_timeout: timeouts.connect_timeout,
379 command_timeout: timeouts.command_timeout,
380 packet_size: 4096,
381 strict_mode: false,
382 trust_server_certificate: false,
383 instance: None,
384 mars: false,
385 encrypt: true, no_tls: false, redirect: RedirectConfig::default(),
388 retry: RetryPolicy::default(),
389 timeouts,
390 tds_version: TdsVersion::V7_4, }
392 }
393}
394
395impl Config {
396 #[must_use]
398 pub fn new() -> Self {
399 Self::default()
400 }
401
402 pub fn from_connection_string(conn_str: &str) -> Result<Self, crate::error::Error> {
409 let mut config = Self::default();
410
411 for part in conn_str.split(';') {
412 let part = part.trim();
413 if part.is_empty() {
414 continue;
415 }
416
417 let (key, value) = part
418 .split_once('=')
419 .ok_or_else(|| crate::error::Error::Config(format!("invalid key-value: {part}")))?;
420
421 let key = key.trim().to_lowercase();
422 let value = value.trim();
423
424 match key.as_str() {
425 "server" | "data source" | "host" => {
426 if let Some((host, port_or_instance)) = value.split_once(',') {
428 config.host = host.to_string();
429 config.port = port_or_instance.parse().map_err(|_| {
430 crate::error::Error::Config(format!("invalid port: {port_or_instance}"))
431 })?;
432 } else if let Some((host, instance)) = value.split_once('\\') {
433 config.host = host.to_string();
434 config.instance = Some(instance.to_string());
435 } else {
436 config.host = value.to_string();
437 }
438 }
439 "port" => {
440 config.port = value.parse().map_err(|_| {
441 crate::error::Error::Config(format!("invalid port: {value}"))
442 })?;
443 }
444 "database" | "initial catalog" => {
445 config.database = Some(value.to_string());
446 }
447 "user id" | "uid" | "user" => {
448 if let Credentials::SqlServer { password, .. } = &config.credentials {
450 config.credentials =
451 Credentials::sql_server(value.to_string(), password.clone());
452 }
453 }
454 "password" | "pwd" => {
455 if let Credentials::SqlServer { username, .. } = &config.credentials {
457 config.credentials =
458 Credentials::sql_server(username.clone(), value.to_string());
459 }
460 }
461 "application name" | "app" => {
462 config.application_name = value.to_string();
463 }
464 "connect timeout" | "connection timeout" => {
465 let secs: u64 = value.parse().map_err(|_| {
466 crate::error::Error::Config(format!("invalid timeout: {value}"))
467 })?;
468 config.connect_timeout = Duration::from_secs(secs);
469 }
470 "command timeout" => {
471 let secs: u64 = value.parse().map_err(|_| {
472 crate::error::Error::Config(format!("invalid timeout: {value}"))
473 })?;
474 config.command_timeout = Duration::from_secs(secs);
475 }
476 "trustservercertificate" | "trust server certificate" => {
477 config.trust_server_certificate = value.eq_ignore_ascii_case("true")
478 || value.eq_ignore_ascii_case("yes")
479 || value == "1";
480 }
481 "encrypt" => {
482 if value.eq_ignore_ascii_case("strict") {
484 config.strict_mode = true;
485 config.encrypt = true;
486 config.no_tls = false;
487 } else if value.eq_ignore_ascii_case("no_tls") {
488 config.no_tls = true;
491 config.encrypt = false;
492 } else if value.eq_ignore_ascii_case("true")
493 || value.eq_ignore_ascii_case("yes")
494 || value == "1"
495 {
496 config.encrypt = true;
497 config.no_tls = false;
498 } else if value.eq_ignore_ascii_case("false")
499 || value.eq_ignore_ascii_case("no")
500 || value == "0"
501 {
502 config.encrypt = false;
503 config.no_tls = false;
504 }
505 }
506 "multipleactiveresultsets" | "mars" => {
507 config.mars = value.eq_ignore_ascii_case("true")
508 || value.eq_ignore_ascii_case("yes")
509 || value == "1";
510 }
511 "packet size" => {
512 config.packet_size = value.parse().map_err(|_| {
513 crate::error::Error::Config(format!("invalid packet size: {value}"))
514 })?;
515 }
516 "tdsversion" | "tds version" | "protocolversion" | "protocol version" => {
517 config.tds_version = TdsVersion::parse(value).ok_or_else(|| {
520 crate::error::Error::Config(format!(
521 "invalid TDS version: {value}. Supported values: 7.3, 7.3A, 7.3B, 7.4, 8.0"
522 ))
523 })?;
524 if config.tds_version.is_tds_8() {
526 config.strict_mode = true;
527 }
528 }
529 _ => {
530 tracing::debug!(
532 key = key,
533 value = value,
534 "ignoring unknown connection string option"
535 );
536 }
537 }
538 }
539
540 Ok(config)
541 }
542
543 #[must_use]
545 pub fn host(mut self, host: impl Into<String>) -> Self {
546 self.host = host.into();
547 self
548 }
549
550 #[must_use]
552 pub fn port(mut self, port: u16) -> Self {
553 self.port = port;
554 self
555 }
556
557 #[must_use]
559 pub fn database(mut self, database: impl Into<String>) -> Self {
560 self.database = Some(database.into());
561 self
562 }
563
564 #[must_use]
566 pub fn credentials(mut self, credentials: Credentials) -> Self {
567 self.credentials = credentials;
568 self
569 }
570
571 #[must_use]
573 pub fn application_name(mut self, name: impl Into<String>) -> Self {
574 self.application_name = name.into();
575 self
576 }
577
578 #[must_use]
580 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
581 self.connect_timeout = timeout;
582 self
583 }
584
585 #[must_use]
587 pub fn trust_server_certificate(mut self, trust: bool) -> Self {
588 self.trust_server_certificate = trust;
589 self.tls = self.tls.trust_server_certificate(trust);
590 self
591 }
592
593 #[must_use]
595 pub fn strict_mode(mut self, enabled: bool) -> Self {
596 self.strict_mode = enabled;
597 self.tls = self.tls.strict_mode(enabled);
598 if enabled {
599 self.tds_version = TdsVersion::V8_0;
600 }
601 self
602 }
603
604 #[must_use]
628 pub fn tds_version(mut self, version: TdsVersion) -> Self {
629 self.tds_version = version;
630 if version.is_tds_8() {
632 self.strict_mode = true;
633 self.tls = self.tls.strict_mode(true);
634 }
635 self
636 }
637
638 #[must_use]
646 pub fn encrypt(mut self, enabled: bool) -> Self {
647 self.encrypt = enabled;
648 self
649 }
650
651 #[must_use]
686 pub fn no_tls(mut self, enabled: bool) -> Self {
687 self.no_tls = enabled;
688 if enabled {
689 self.encrypt = false;
690 }
691 self
692 }
693
694 #[must_use]
696 pub fn with_host(mut self, host: &str) -> Self {
697 self.host = host.to_string();
698 self
699 }
700
701 #[must_use]
703 pub fn with_port(mut self, port: u16) -> Self {
704 self.port = port;
705 self
706 }
707
708 #[must_use]
710 pub fn redirect(mut self, redirect: RedirectConfig) -> Self {
711 self.redirect = redirect;
712 self
713 }
714
715 #[must_use]
717 pub fn max_redirects(mut self, max: u8) -> Self {
718 self.redirect.max_redirects = max;
719 self
720 }
721
722 #[must_use]
724 pub fn retry(mut self, retry: RetryPolicy) -> Self {
725 self.retry = retry;
726 self
727 }
728
729 #[must_use]
731 pub fn max_retries(mut self, max: u32) -> Self {
732 self.retry.max_retries = max;
733 self
734 }
735
736 #[must_use]
738 pub fn timeouts(mut self, timeouts: TimeoutConfig) -> Self {
739 self.connect_timeout = timeouts.connect_timeout;
741 self.command_timeout = timeouts.command_timeout;
742 self.timeouts = timeouts;
743 self
744 }
745}
746
747#[cfg(test)]
748#[allow(clippy::unwrap_used)]
749mod tests {
750 use super::*;
751
752 #[test]
753 fn test_connection_string_parsing() {
754 let config = Config::from_connection_string(
755 "Server=localhost;Database=test;User Id=sa;Password=secret;",
756 )
757 .unwrap();
758
759 assert_eq!(config.host, "localhost");
760 assert_eq!(config.database, Some("test".to_string()));
761 }
762
763 #[test]
764 fn test_connection_string_with_port() {
765 let config =
766 Config::from_connection_string("Server=localhost,1434;Database=test;").unwrap();
767
768 assert_eq!(config.host, "localhost");
769 assert_eq!(config.port, 1434);
770 }
771
772 #[test]
773 fn test_connection_string_with_instance() {
774 let config =
775 Config::from_connection_string("Server=localhost\\SQLEXPRESS;Database=test;").unwrap();
776
777 assert_eq!(config.host, "localhost");
778 assert_eq!(config.instance, Some("SQLEXPRESS".to_string()));
779 }
780
781 #[test]
782 fn test_redirect_config_defaults() {
783 let config = RedirectConfig::default();
784 assert_eq!(config.max_redirects, 2);
785 assert!(config.follow_redirects);
786 }
787
788 #[test]
789 fn test_redirect_config_builder() {
790 let config = RedirectConfig::new()
791 .max_redirects(5)
792 .follow_redirects(false);
793 assert_eq!(config.max_redirects, 5);
794 assert!(!config.follow_redirects);
795 }
796
797 #[test]
798 fn test_redirect_config_no_follow() {
799 let config = RedirectConfig::no_follow();
800 assert_eq!(config.max_redirects, 0);
801 assert!(!config.follow_redirects);
802 }
803
804 #[test]
805 fn test_config_redirect_builder() {
806 let config = Config::new().max_redirects(3);
807 assert_eq!(config.redirect.max_redirects, 3);
808
809 let config2 = Config::new().redirect(RedirectConfig::no_follow());
810 assert!(!config2.redirect.follow_redirects);
811 }
812
813 #[test]
814 fn test_retry_policy_defaults() {
815 let policy = RetryPolicy::default();
816 assert_eq!(policy.max_retries, 3);
817 assert_eq!(policy.initial_backoff, Duration::from_millis(100));
818 assert_eq!(policy.max_backoff, Duration::from_secs(30));
819 assert!((policy.backoff_multiplier - 2.0).abs() < f64::EPSILON);
820 assert!(policy.jitter);
821 }
822
823 #[test]
824 fn test_retry_policy_builder() {
825 let policy = RetryPolicy::new()
826 .max_retries(5)
827 .initial_backoff(Duration::from_millis(200))
828 .max_backoff(Duration::from_secs(60))
829 .backoff_multiplier(3.0)
830 .jitter(false);
831
832 assert_eq!(policy.max_retries, 5);
833 assert_eq!(policy.initial_backoff, Duration::from_millis(200));
834 assert_eq!(policy.max_backoff, Duration::from_secs(60));
835 assert!((policy.backoff_multiplier - 3.0).abs() < f64::EPSILON);
836 assert!(!policy.jitter);
837 }
838
839 #[test]
840 fn test_retry_policy_no_retry() {
841 let policy = RetryPolicy::no_retry();
842 assert_eq!(policy.max_retries, 0);
843 assert!(!policy.should_retry(0));
844 }
845
846 #[test]
847 fn test_retry_policy_should_retry() {
848 let policy = RetryPolicy::new().max_retries(3);
849 assert!(policy.should_retry(0));
850 assert!(policy.should_retry(1));
851 assert!(policy.should_retry(2));
852 assert!(!policy.should_retry(3));
853 assert!(!policy.should_retry(4));
854 }
855
856 #[test]
857 fn test_retry_policy_backoff_calculation() {
858 let policy = RetryPolicy::new()
859 .initial_backoff(Duration::from_millis(100))
860 .backoff_multiplier(2.0)
861 .max_backoff(Duration::from_secs(10))
862 .jitter(false);
863
864 assert_eq!(policy.backoff_for_attempt(0), Duration::ZERO);
865 assert_eq!(policy.backoff_for_attempt(1), Duration::from_millis(100));
866 assert_eq!(policy.backoff_for_attempt(2), Duration::from_millis(200));
867 assert_eq!(policy.backoff_for_attempt(3), Duration::from_millis(400));
868 }
869
870 #[test]
871 fn test_retry_policy_backoff_capped() {
872 let policy = RetryPolicy::new()
873 .initial_backoff(Duration::from_secs(1))
874 .backoff_multiplier(10.0)
875 .max_backoff(Duration::from_secs(5))
876 .jitter(false);
877
878 assert_eq!(policy.backoff_for_attempt(3), Duration::from_secs(5));
880 }
881
882 #[test]
883 fn test_config_retry_builder() {
884 let config = Config::new().max_retries(5);
885 assert_eq!(config.retry.max_retries, 5);
886
887 let config2 = Config::new().retry(RetryPolicy::no_retry());
888 assert_eq!(config2.retry.max_retries, 0);
889 }
890
891 #[test]
892 fn test_timeout_config_defaults() {
893 let config = TimeoutConfig::default();
894 assert_eq!(config.connect_timeout, Duration::from_secs(15));
895 assert_eq!(config.tls_timeout, Duration::from_secs(10));
896 assert_eq!(config.login_timeout, Duration::from_secs(30));
897 assert_eq!(config.command_timeout, Duration::from_secs(30));
898 assert_eq!(config.idle_timeout, Duration::from_secs(300));
899 assert_eq!(config.keepalive_interval, Some(Duration::from_secs(30)));
900 }
901
902 #[test]
903 fn test_timeout_config_builder() {
904 let config = TimeoutConfig::new()
905 .connect_timeout(Duration::from_secs(5))
906 .tls_timeout(Duration::from_secs(3))
907 .login_timeout(Duration::from_secs(10))
908 .command_timeout(Duration::from_secs(60))
909 .idle_timeout(Duration::from_secs(600))
910 .keepalive_interval(Some(Duration::from_secs(60)));
911
912 assert_eq!(config.connect_timeout, Duration::from_secs(5));
913 assert_eq!(config.tls_timeout, Duration::from_secs(3));
914 assert_eq!(config.login_timeout, Duration::from_secs(10));
915 assert_eq!(config.command_timeout, Duration::from_secs(60));
916 assert_eq!(config.idle_timeout, Duration::from_secs(600));
917 assert_eq!(config.keepalive_interval, Some(Duration::from_secs(60)));
918 }
919
920 #[test]
921 fn test_timeout_config_no_keepalive() {
922 let config = TimeoutConfig::new().no_keepalive();
923 assert_eq!(config.keepalive_interval, None);
924 }
925
926 #[test]
927 fn test_timeout_config_total_connect() {
928 let config = TimeoutConfig::new()
929 .connect_timeout(Duration::from_secs(5))
930 .tls_timeout(Duration::from_secs(3))
931 .login_timeout(Duration::from_secs(10));
932
933 assert_eq!(config.total_connect_timeout(), Duration::from_secs(18));
935 }
936
937 #[test]
938 fn test_config_timeouts_builder() {
939 let timeouts = TimeoutConfig::new()
940 .connect_timeout(Duration::from_secs(5))
941 .command_timeout(Duration::from_secs(60));
942
943 let config = Config::new().timeouts(timeouts);
944 assert_eq!(config.timeouts.connect_timeout, Duration::from_secs(5));
945 assert_eq!(config.timeouts.command_timeout, Duration::from_secs(60));
946 assert_eq!(config.connect_timeout, Duration::from_secs(5));
948 assert_eq!(config.command_timeout, Duration::from_secs(60));
949 }
950
951 #[test]
952 fn test_tds_version_default() {
953 let config = Config::default();
954 assert_eq!(config.tds_version, TdsVersion::V7_4);
955 assert!(!config.strict_mode);
956 }
957
958 #[test]
959 fn test_tds_version_builder() {
960 let config = Config::new().tds_version(TdsVersion::V7_3A);
961 assert_eq!(config.tds_version, TdsVersion::V7_3A);
962 assert!(!config.strict_mode);
963
964 let config = Config::new().tds_version(TdsVersion::V7_3B);
965 assert_eq!(config.tds_version, TdsVersion::V7_3B);
966 assert!(!config.strict_mode);
967
968 let config = Config::new().tds_version(TdsVersion::V8_0);
970 assert_eq!(config.tds_version, TdsVersion::V8_0);
971 assert!(config.strict_mode);
972 }
973
974 #[test]
975 fn test_strict_mode_sets_tds_8() {
976 let config = Config::new().strict_mode(true);
977 assert!(config.strict_mode);
978 assert_eq!(config.tds_version, TdsVersion::V8_0);
979 }
980
981 #[test]
982 fn test_connection_string_tds_version() {
983 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.3;").unwrap();
985 assert_eq!(config.tds_version, TdsVersion::V7_3A);
986
987 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.3A;").unwrap();
989 assert_eq!(config.tds_version, TdsVersion::V7_3A);
990
991 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.3B;").unwrap();
993 assert_eq!(config.tds_version, TdsVersion::V7_3B);
994
995 let config = Config::from_connection_string("Server=localhost;TDSVersion=7.4;").unwrap();
997 assert_eq!(config.tds_version, TdsVersion::V7_4);
998
999 let config = Config::from_connection_string("Server=localhost;TDSVersion=8.0;").unwrap();
1001 assert_eq!(config.tds_version, TdsVersion::V8_0);
1002 assert!(config.strict_mode);
1003
1004 let config =
1006 Config::from_connection_string("Server=localhost;ProtocolVersion=7.3;").unwrap();
1007 assert_eq!(config.tds_version, TdsVersion::V7_3A);
1008 }
1009
1010 #[test]
1011 fn test_connection_string_invalid_tds_version() {
1012 let result = Config::from_connection_string("Server=localhost;TDSVersion=invalid;");
1013 assert!(result.is_err());
1014
1015 let result = Config::from_connection_string("Server=localhost;TDSVersion=9.0;");
1016 assert!(result.is_err());
1017 }
1018
1019 #[test]
1020 fn test_connection_string_no_tls() {
1021 let config = Config::from_connection_string("Server=legacy;Encrypt=no_tls;").unwrap();
1023 assert!(config.no_tls);
1024 assert!(!config.encrypt);
1025 assert!(!config.strict_mode);
1026
1027 let config = Config::from_connection_string("Server=legacy;Encrypt=no_tls;").unwrap();
1029 assert!(config.no_tls);
1030
1031 let config = Config::from_connection_string("Server=localhost;Encrypt=true;").unwrap();
1033 assert!(!config.no_tls);
1034 assert!(config.encrypt);
1035
1036 let config = Config::from_connection_string("Server=localhost;Encrypt=strict;").unwrap();
1038 assert!(!config.no_tls);
1039 assert!(config.encrypt);
1040 assert!(config.strict_mode);
1041 }
1042
1043 #[test]
1044 fn test_no_tls_builder() {
1045 let config = Config::new().no_tls(true);
1047 assert!(config.no_tls);
1048 assert!(!config.encrypt);
1049
1050 let config = Config::new().no_tls(true).no_tls(false);
1052 assert!(!config.no_tls);
1053 }
1054}