diff --git a/examples/aad-auth.rs b/examples/aad-auth.rs index 8ef41c47..fdea4a50 100644 --- a/examples/aad-auth.rs +++ b/examples/aad-auth.rs @@ -36,14 +36,15 @@ async fn main() -> anyhow::Result<()> { ) .await?; - let mut config = Config::new(); let server = env::var("SERVER").expect("Missing SERVER environment variable."); - config.host(server); - config.port(1433); - config.authentication(AuthMethod::AADToken( - token.access_token().secret().to_string(), - )); - config.trust_cert(); + let config = Config::builder() + .host(server) + .port(1433) + .authentication(AuthMethod::AADToken( + token.access_token().secret().to_string(), + )) + .trust_cert() + .build(); let tcp = TcpStream::connect(config.get_addr()).await?; tcp.set_nodelay(true)?; diff --git a/src/client/config.rs b/src/client/config.rs index fff68bc1..a93e266e 100644 --- a/src/client/config.rs +++ b/src/client/config.rs @@ -3,6 +3,7 @@ mod jdbc; use std::collections::HashMap; use std::path::PathBuf; +use std::convert::From; use super::AuthMethod; use crate::EncryptionLevel; @@ -18,10 +19,16 @@ use jdbc::*; /// When using an [ADO.NET connection string], it can be /// constructed using the [`from_ado_string`] function. /// +/// Otherwise, first create a new [`ConfigBuilder`] via [`builder`], +/// set it up by calling its methods and then finalizing it by [`build`]. +/// /// [`Client`]: struct.Client.html /// [ADO.NET connection string]: https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings /// [`from_ado_string`]: struct.Config.html#method.from_ado_string /// [`get_addr`]: struct.Config.html#method.get_addr +/// [`ConfigBuilder`]: struct.ConfigBuilder.html +/// [`builder`]: struct.Config.html#method.builder +/// [`build`]: struct.ConfigBuilder.html#method.build pub struct Config { pub(crate) host: Option, pub(crate) port: Option, @@ -70,106 +77,8 @@ impl Default for Config { } impl Config { - /// Create a new `Config` with the default settings. - pub fn new() -> Self { - Self::default() - } - - /// A host or ip address to connect to. - /// - /// - Defaults to `localhost`. - pub fn host(&mut self, host: impl ToString) { - self.host = Some(host.to_string()); - } - - /// The server port. - /// - /// - Defaults to `1433`. - pub fn port(&mut self, port: u16) { - self.port = Some(port); - } - - /// The database to connect to. - /// - /// - Defaults to `master`. - pub fn database(&mut self, database: impl ToString) { - self.database = Some(database.to_string()) - } - - /// The instance name as defined in the SQL Browser. Only available on - /// Windows platforms. - /// - /// If specified, the port is replaced with the value returned from the - /// browser. - /// - /// - Defaults to no name specified. - pub fn instance_name(&mut self, name: impl ToString) { - self.instance_name = Some(name.to_string()); - } - - /// Sets the application name to the connection, queryable with the - /// `APP_NAME()` command. - /// - /// - Defaults to no name specified. - pub fn application_name(&mut self, name: impl ToString) { - self.application_name = Some(name.to_string()); - } - - /// Set the preferred encryption level. - /// - /// - With `tls` feature, defaults to `Required`. - /// - Without `tls` feature, defaults to `NotSupported`. - pub fn encryption(&mut self, encryption: EncryptionLevel) { - self.encryption = encryption; - } - - /// If set, the server certificate will not be validated and it is accepted - /// as-is. - /// - /// On production setting, the certificate should be added to the local key - /// storage (or use `trust_cert_ca` instead), using this setting is potentially dangerous. - /// - /// # Panics - /// Will panic in case `trust_cert_ca` was called before. - /// - /// - Defaults to `default`, meaning server certificate is validated against system-truststore. - pub fn trust_cert(&mut self) { - if let TrustConfig::CaCertificateLocation(_) = &self.trust { - panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.") - } - self.trust = TrustConfig::TrustAll; - } - - /// If set, the server certificate will be validated against the given CA certificate in - /// in addition to the system-truststore. - /// Useful when using self-signed certificates on the server without having to disable the - /// trust-chain. - /// - /// # Panics - /// Will panic in case `trust_cert` was called before. - /// - /// - Defaults to validating the server certificate is validated against system's certificate storage. - pub fn trust_cert_ca(&mut self, path: impl ToString) { - if let TrustConfig::TrustAll = &self.trust { - panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.") - } else { - self.trust = TrustConfig::CaCertificateLocation(PathBuf::from(path.to_string())) - } - } - - /// Sets the authentication method. - /// - /// - Defaults to `None`. - pub fn authentication(&mut self, auth: AuthMethod) { - self.auth = auth; - } - - /// Sets ApplicationIntent readonly. - /// - /// - Defaults to `false`. - pub fn readonly(&mut self, readnoly: bool) { - self.readonly = readnoly; - } + /// Create a new `ConfigBuilder` initialized with the default settings. + pub fn builder() -> ConfigBuilder { ConfigBuilder { inner: Self::default() } } pub(crate) fn get_host(&self) -> &str { self.host @@ -231,7 +140,7 @@ impl Config { } fn from_config_string(s: impl ConfigString) -> crate::Result { - let mut builder = Self::new(); + let mut builder = Self::builder(); let server = s.server()?; @@ -269,7 +178,131 @@ impl Config { builder.readonly(s.readonly()); - Ok(builder) + Ok(builder.build()) + } +} + +#[derive(Clone, Debug)] +pub struct ConfigBuilder { + inner: Config, +} + +impl<'a> ConfigBuilder { + /// A host or ip address to connect to. + /// + /// - Defaults to `localhost`. + pub fn host(&'a mut self, host: impl ToString) -> &'a mut Self { + self.inner.host = Some(host.to_string()); + self + } + + /// The server port. + /// + /// - Defaults to `1433`. + pub fn port(&'a mut self, port: u16) -> &'a mut Self { + self.inner.port = Some(port); + self + } + + /// The database to connect to. + /// + /// - Defaults to `master`. + pub fn database(&'a mut self, database: impl ToString) -> &'a mut Self { + self.inner.database = Some(database.to_string()); + self + } + + /// The instance name as defined in the SQL Browser. Only available on + /// Windows platforms. + /// + /// If specified, the port is replaced with the value returned from the + /// browser. + /// + /// - Defaults to no name specified. + pub fn instance_name(&'a mut self, name: impl ToString) -> &'a mut Self { + self.inner.instance_name = Some(name.to_string()); + self + } + + /// Sets the application name to the connection, queryable with the + /// `APP_NAME()` command. + /// + /// - Defaults to no name specified. + pub fn application_name(&'a mut self, name: impl ToString) -> &'a mut Self { + self.inner.application_name = Some(name.to_string()); + self + } + + /// Set the preferred encryption level. + /// + /// - With `tls` feature, defaults to `Required`. + /// - Without `tls` feature, defaults to `NotSupported`. + pub fn encryption(&'a mut self, encryption: EncryptionLevel) -> &'a mut Self { + self.inner.encryption = encryption; + self + } + + /// If set, the server certificate will not be validated and it is accepted + /// as-is. + /// + /// On production setting, the certificate should be added to the local key + /// storage (or use `trust_cert_ca` instead), using this setting is potentially dangerous. + /// + /// # Panics + /// Will panic in case `trust_cert_ca` was called before. + /// + /// - Defaults to `default`, meaning server certificate is validated against system-truststore. + pub fn trust_cert(&'a mut self) -> &'a mut Self { + if let TrustConfig::CaCertificateLocation(_) = &self.inner.trust { + panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.") + } + self.inner.trust = TrustConfig::TrustAll; + self + } + + /// If set, the server certificate will be validated against the given CA certificate in + /// in addition to the system-truststore. + /// Useful when using self-signed certificates on the server without having to disable the + /// trust-chain. + /// + /// # Panics + /// Will panic in case `trust_cert` was called before. + /// + /// - Defaults to validating the server certificate is validated against system's certificate storage. + pub fn trust_cert_ca(&'a mut self, path: impl ToString) -> &'a mut Self { + if let TrustConfig::TrustAll = &self.inner.trust { + panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.") + } else { + self.inner.trust = TrustConfig::CaCertificateLocation(PathBuf::from(path.to_string())) + } + self + } + + /// Sets the authentication method. + /// + /// - Defaults to `None`. + pub fn authentication(&'a mut self, auth: AuthMethod) -> &'a mut Self { + self.inner.auth = auth; + self + } + + /// Sets ApplicationIntent readonly. + /// + /// - Defaults to `false`. + pub fn readonly(&'a mut self, readnoly: bool) -> &'a mut Self { + self.inner.readonly = readnoly; + self + } + + /// Produces finalized `Config` from this `ConfigBuilder` instance + pub fn build(&self) -> Config { + self.inner.clone() + } +} + +impl From for ConfigBuilder { + fn from(config: Config) -> Self { + ConfigBuilder { inner: config } } }