use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Credential {
name: String,
username: String,
password: String,
url: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CredentialBuilder {
name: Option<String>,
username: Option<String>,
password: Option<String>,
url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CredentialBuilderError {
MissingName,
MissingUsername,
MissingPassword,
}
impl Credential {
pub fn new<N: Into<String>, U: Into<String>, P: Into<String>>(
name: N,
username: U,
password: P,
url: Option<String>,
) -> Self {
Credential {
name: name.into(),
username: username.into(),
password: password.into(),
url,
}
}
pub fn builder() -> CredentialBuilder {
CredentialBuilder::default()
}
pub fn name(&self) -> &str {
&self.name
}
pub fn set_name<T: Into<String>>(&mut self, name: T) -> &mut Self {
self.name = name.into();
self
}
pub fn username(&self) -> &str {
&self.username
}
pub fn set_username<T: Into<String>>(&mut self, username: T) -> &mut Self {
self.username = username.into();
self
}
pub fn password(&self) -> &str {
&self.password
}
pub fn set_password<T: Into<String>>(&mut self, password: T) -> &mut Self {
self.password = password.into();
self
}
pub fn url(&self) -> Option<&str> {
self.url.as_deref()
}
pub fn set_url<T: Into<String>>(&mut self, url: Option<T>) -> &mut Self {
self.url = url.map(Into::into);
self
}
}
impl CredentialBuilder {
pub fn name<T: Into<String>>(mut self, name: T) -> Self {
self.name = Some(name.into());
self
}
pub fn username<T: Into<String>>(mut self, username: T) -> Self {
self.username = Some(username.into());
self
}
pub fn password<T: Into<String>>(mut self, password: T) -> Self {
self.password = Some(password.into());
self
}
pub fn url<T: Into<String>>(mut self, url: T) -> Self {
self.url = Some(url.into());
self
}
pub fn build(self) -> Result<Credential, CredentialBuilderError> {
let name = self.name.ok_or(CredentialBuilderError::MissingName)?;
let username = self
.username
.ok_or(CredentialBuilderError::MissingUsername)?;
let password = self
.password
.ok_or(CredentialBuilderError::MissingPassword)?;
Ok(Credential {
name,
username,
password,
url: self.url,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let credential = Credential::new(
"My Credential".to_string(),
"username".to_string(),
"password".to_string(),
Some("https://example.com".to_string()),
);
assert_eq!(credential.name(), "My Credential");
assert_eq!(credential.username(), "username");
assert_eq!(credential.password(), "password");
assert_eq!(credential.url(), Some("https://example.com"));
}
#[test]
fn builder_with_optional_url() {
let credential = Credential::builder()
.name("My Credential".to_string())
.username("username".to_string())
.password("password".to_string())
.url("https://example.com".to_string())
.build()
.unwrap();
assert_eq!(credential.name(), "My Credential");
assert_eq!(credential.username(), "username");
assert_eq!(credential.password(), "password");
assert_eq!(credential.url(), Some("https://example.com"));
}
#[test]
fn builder_without_optional_url() {
let credential = Credential::builder()
.name("My Credential".to_string())
.username("username".to_string())
.password("password".to_string())
.build()
.unwrap();
assert_eq!(credential.url(), None);
}
#[test]
fn builder_missing_required_field() {
let result = Credential::builder()
.name("My Credential".to_string())
.password("password".to_string())
.build();
assert_eq!(result, Err(CredentialBuilderError::MissingUsername));
}
}