use std::fmt::Display;
use base64::prelude::*;
use derive_builder::Builder;
use url::Url;
use crate::{DEFAULT_LINK_COUNT, DEFAULT_MAX_IN_FLIGHT, DEFAULT_POISON_SOURCE};
#[derive(Builder)]
#[builder(build_fn(name = "unsafe_build", private))]
pub struct MiasmaConfig {
#[builder(default = DEFAULT_MAX_IN_FLIGHT)]
pub max_in_flight: u32,
#[builder(default = LinkPrefix::new("/"))]
#[builder(setter(custom))]
pub link_prefix: LinkPrefix,
#[builder(default = DEFAULT_LINK_COUNT)]
pub link_count: u8,
#[builder(default = None)]
#[builder(setter(strip_option))]
pub max_depth: Option<u32>,
#[builder(default = false)]
pub force_gzip: bool,
#[builder(default = false)]
pub unsafe_allow_html: bool,
#[builder(default = Url::parse(DEFAULT_POISON_SOURCE).unwrap())]
pub poison_source: Url,
#[builder(default = None)]
#[builder(setter(custom))]
pub metrics: Option<MetricsConfig>,
}
impl MiasmaConfig {
pub fn builder() -> MiasmaConfigBuilder {
MiasmaConfigBuilder::default()
}
}
impl Default for MiasmaConfig {
fn default() -> Self {
Self::builder().build()
}
}
impl MiasmaConfigBuilder {
pub fn metrics(
&mut self,
db_path: &str,
endpoint: &str,
username: &str,
password: &str,
) -> &mut Self {
self.metrics = Some(Some(MetricsConfig::new(
db_path, username, password, endpoint,
)));
self
}
pub fn link_prefix(&mut self, link_prefix: &str) -> &mut Self {
self.link_prefix = Some(LinkPrefix::new(link_prefix));
self
}
#[allow(clippy::missing_panics_doc)]
pub fn build(&self) -> MiasmaConfig {
self.unsafe_build()
.expect("build only errors for unset required fields - all config fields are optional")
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub struct MetricsConfig {
pub db_path: String,
pub endpoint: String,
username: String,
password: String,
}
impl MetricsConfig {
pub fn new(db_path: &str, username: &str, password: &str, endpoint: &str) -> Self {
MetricsConfig {
db_path: db_path.to_owned(),
endpoint: endpoint.to_owned(),
username: username.to_owned(),
password: password.to_owned(),
}
}
pub fn encoded_credentials(&self) -> String {
BASE64_STANDARD.encode(format!("{}:{}", self.username, self.password))
}
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct LinkPrefix(String);
impl LinkPrefix {
fn new(prefix: &str) -> Self {
let mut prefix = prefix.to_owned();
if !prefix.starts_with('/') {
prefix.insert(0, '/');
}
if !prefix.ends_with('/') {
prefix.push('/');
}
Self(prefix)
}
}
impl Display for LinkPrefix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn link_prefix_validation() {
let cases = [
("/", "/"),
("foo", "/foo/"),
("/foo", "/foo/"),
("foo/", "/foo/"),
("/foo/", "/foo/"),
];
for (input, expected) in cases {
let prefix = LinkPrefix::new(input);
assert_eq!(prefix.0, expected);
}
}
#[test]
fn builder_build_with_default_values() {
MiasmaConfig::builder().build();
}
}