use crate::tag::Tag;
use crate::value::{Props, Value};
use std::fmt;
use std::str::FromStr;
pub const DEFAULT_SCRIPT_HOST: &str = "https://privatusstats.com";
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Spa {
#[default]
Auto,
History,
Hash,
Off,
}
impl Spa {
pub fn as_str(self) -> &'static str {
match self {
Spa::Auto => "auto",
Spa::History => "history",
Spa::Hash => "hash",
Spa::Off => "off",
}
}
}
impl fmt::Display for Spa {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnknownSpaMode(pub String);
impl fmt::Display for UnknownSpaMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unknown spa mode {:?}: use auto, history, hash or off",
self.0
)
}
}
impl std::error::Error for UnknownSpaMode {}
impl FromStr for Spa {
type Err = UnknownSpaMode;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().to_ascii_lowercase().as_str() {
"auto" => Ok(Spa::Auto),
"history" => Ok(Spa::History),
"hash" => Ok(Spa::Hash),
"off" => Ok(Spa::Off),
_ => Err(UnknownSpaMode(s.to_owned())),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CspNonce(pub String);
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Config {
site_id: Option<String>,
script_host: Option<String>,
modules: Option<Vec<String>>,
excluded_paths: Vec<String>,
domains: Option<Vec<String>>,
spa: Spa,
manual: bool,
enabled: Option<bool>,
auto_inject: Option<bool>,
extra_attributes: Vec<(String, String)>,
props: Props,
queue_stub: bool,
}
impl Config {
pub fn new() -> Self {
Config::default()
}
pub fn site_id(mut self, site_id: impl Into<String>) -> Self {
self.site_id = Some(site_id.into());
self
}
pub fn script_host(mut self, host: impl Into<String>) -> Self {
self.script_host = Some(host.into());
self
}
pub fn modules<I, S>(mut self, modules: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.modules = Some(modules.into_iter().map(Into::into).collect());
self
}
pub fn default_modules(mut self) -> Self {
self.modules = None;
self
}
pub fn excluded_paths<I, S>(mut self, paths: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.excluded_paths = paths.into_iter().map(Into::into).collect();
self
}
pub fn domains<I, S>(mut self, domains: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.domains = Some(domains.into_iter().map(Into::into).collect());
self
}
pub fn spa(mut self, spa: Spa) -> Self {
self.spa = spa;
self
}
pub fn manual(mut self, manual: bool) -> Self {
self.manual = manual;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = Some(enabled);
self
}
pub fn auto_inject(mut self, auto_inject: bool) -> Self {
self.auto_inject = Some(auto_inject);
self
}
pub fn extra_attribute(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
set_pair(&mut self.extra_attributes, name.into(), value.into());
self
}
pub fn props<I, K, V>(mut self, props: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<Value>,
{
self.props = props.into_iter().collect();
self
}
pub fn queue_stub(mut self, queue_stub: bool) -> Self {
self.queue_stub = queue_stub;
self
}
pub fn tag(&self) -> Tag<'_> {
Tag::new(self)
}
pub fn resolved_site_id(&self) -> Option<String> {
let configured = self.site_id.as_deref().unwrap_or("").trim();
if !configured.is_empty() {
return Some(configured.to_owned());
}
let env = std::env::var("PRIVATUS_SITE_ID").unwrap_or_default();
let env = env.trim();
(!env.is_empty()).then(|| env.to_owned())
}
pub fn resolved_script_host(&self) -> String {
let mut host = self.script_host.as_deref().unwrap_or("").trim().to_owned();
if host.is_empty() {
host = std::env::var("PRIVATUS_SCRIPT_HOST")
.unwrap_or_default()
.trim()
.to_owned();
}
if host.is_empty() {
host = DEFAULT_SCRIPT_HOST.to_owned();
}
if host.ends_with('/') {
host.pop();
}
host
}
pub fn is_enabled(&self) -> bool {
self.enabled.unwrap_or(cfg!(not(debug_assertions)))
}
pub fn is_auto_inject(&self) -> bool {
self.auto_inject.unwrap_or(true)
}
pub(crate) fn modules_list(&self) -> Option<&[String]> {
self.modules.as_deref()
}
pub(crate) fn excluded_paths_list(&self) -> &[String] {
&self.excluded_paths
}
pub(crate) fn domains_list(&self) -> Option<&[String]> {
self.domains.as_deref()
}
pub(crate) fn spa_mode(&self) -> Spa {
self.spa
}
pub(crate) fn is_manual(&self) -> bool {
self.manual
}
pub(crate) fn extra_attributes_list(&self) -> &[(String, String)] {
&self.extra_attributes
}
pub(crate) fn configured_props(&self) -> &Props {
&self.props
}
pub(crate) fn wants_queue_stub(&self) -> bool {
self.queue_stub
}
}
pub(crate) fn set_pair(pairs: &mut Vec<(String, String)>, name: String, value: String) {
match pairs.iter_mut().find(|(k, _)| *k == name) {
Some(slot) => slot.1 = value,
None => pairs.push((name, value)),
}
}