1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use super::connect_params::ServerCerts;
use super::connect_params_builder::ConnectParamsBuilder;
use super::cp_url::UrlOpt;
use crate::{
    url::{HDBSQL, HDBSQLS},
    HdbError, HdbResult,
};
use url::Url;

/// A trait implemented by types that can be converted into a `ConnectParamsBuilder`.
///
/// # Example
/// ```rust
///     use hdbconnect::IntoConnectParamsBuilder;
///
///     let cp_builder = "hdbsql://MEIER:schLau@abcd123:2222"
///         .into_connect_params_builder()
///         .unwrap();
///
///     assert_eq!("abcd123", cp_builder.get_hostname().unwrap());
/// ```
pub trait IntoConnectParamsBuilder {
    /// Converts the value of `self` into a `ConnectParamsBuilder`.
    ///
    /// # Errors
    /// `HdbError::Usage` if wrong information was provided
    fn into_connect_params_builder(self) -> HdbResult<ConnectParamsBuilder>;
}

impl IntoConnectParamsBuilder for ConnectParamsBuilder {
    fn into_connect_params_builder(self) -> HdbResult<ConnectParamsBuilder> {
        Ok(self)
    }
}

impl<'a> IntoConnectParamsBuilder for &'a str {
    fn into_connect_params_builder(self) -> HdbResult<ConnectParamsBuilder> {
        Url::parse(self)
            .map_err(|e| HdbError::conn_params(Box::new(e)))?
            .into_connect_params_builder()
    }
}

impl IntoConnectParamsBuilder for String {
    fn into_connect_params_builder(self) -> HdbResult<ConnectParamsBuilder> {
        self.as_str().into_connect_params_builder()
    }
}

impl IntoConnectParamsBuilder for Url {
    fn into_connect_params_builder(self) -> HdbResult<ConnectParamsBuilder> {
        let mut builder = ConnectParamsBuilder::new();
        self.host_str().as_ref().map(|host| builder.hostname(host));
        self.port().as_ref().map(|port| builder.port(*port));

        let dbuser = self.username();
        if !dbuser.is_empty() {
            builder.dbuser(dbuser);
        }
        self.password().as_ref().map(|pw| builder.password(pw));

        // authoritative switch between protocols:
        let use_tls = match self.scheme() {
            HDBSQL => false,
            HDBSQLS => true,
            _ => {
                return Err(HdbError::Usage(
                    "Unknown protocol, only 'hdbsql' and 'hdbsqls' are supported",
                ));
            }
        };

        let mut insecure_option = false;
        let mut server_certs = Vec::<ServerCerts>::new();

        for (name, value) in self.query_pairs() {
            match UrlOpt::from(name.as_ref()) {
                Some(UrlOpt::ClientLocale) => {
                    builder.clientlocale(&value);
                }
                Some(UrlOpt::ClientLocaleFromEnv) => {
                    std::env::var(value.to_string())
                        .ok()
                        .map(|s| builder.clientlocale(s));
                }
                Some(UrlOpt::TlsCertificateDir) => {
                    server_certs.push(ServerCerts::Directory(value.to_string()));
                }
                Some(UrlOpt::TlsCertificateEnv) => {
                    server_certs.push(ServerCerts::Environment(value.to_string()));
                }
                Some(UrlOpt::TlsCertificateMozilla) => {
                    server_certs.push(ServerCerts::RootCertificates);
                }
                Some(UrlOpt::InsecureOmitServerCheck) => {
                    insecure_option = true;
                }
                Some(UrlOpt::Database) => {
                    builder.dbname(&value);
                }
                Some(UrlOpt::NetworkGroup) => {
                    builder.network_group(&value);
                }
                Some(UrlOpt::NoCompression) => {
                    builder.always_uncompressed(true);
                }
                None => {
                    return Err(HdbError::UsageDetailed(format!(
                        "option '{name}' not supported",
                    )));
                }
            }
        }

        if use_tls {
            if insecure_option {
                if !server_certs.is_empty() {
                    return Err(HdbError::Usage(
                        "Use the url-options 'tls_certificate_dir', 'tls_certificate_env', \
                        'tls_certificate_direct' and 'use_mozillas_root_certificates' \
                        to specify the access to the server certificate,\
                        or use 'insecure_omit_server_certificate_check' to not verify the server's \
                        identity, which is not recommended in most situations",
                    ));
                }
                builder.tls_without_server_verification();
            } else {
                if server_certs.is_empty() {
                    return Err(HdbError::Usage(
                        "Using 'hdbsqls' requires at least one of the url-options \
                        'tls_certificate_dir', 'tls_certificate_env', 'tls_certificate_direct', \
                        'use_mozillas_root_certificates', or 'insecure_omit_server_certificate_check'",
                    ));
                }
                for cert in server_certs {
                    builder.tls_with(cert);
                }
            }
        } else if insecure_option || !server_certs.is_empty() {
            return Err(HdbError::Usage(
                "Using 'hdbsql' is not possible with any of the url-options \
                    'tls_certificate_dir', 'tls_certificate_env', 'tls_certificate_direct', \
                    'use_mozillas_root_certificates', or 'insecure_omit_server_certificate_check'; \
                    consider using 'hdbsqls' instead",
            ));
        }

        Ok(builder)
    }
}