perfect-postcode/server-rs/src/bugsink.rs
2026-05-17 10:16:30 +01:00

80 lines
2.1 KiB
Rust

use std::borrow::Cow;
use serde::Serialize;
#[derive(Clone, Debug)]
pub struct BackendConfig {
pub dsn: Option<String>,
pub environment: Option<String>,
pub release: Option<String>,
pub send_default_pii: bool,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FrontendConfig {
pub dsn: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub release: Option<String>,
pub send_default_pii: bool,
}
pub fn env_nonempty(name: &str) -> Option<String> {
std::env::var(name).ok().and_then(nonempty)
}
pub fn nonempty(value: String) -> Option<String> {
let trimmed = value.trim();
(!trimmed.is_empty()).then(|| trimmed.to_owned())
}
pub fn default_release() -> String {
format!("{}@{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
}
pub fn init_backend(config: &BackendConfig) -> Option<sentry::ClientInitGuard> {
let dsn = config.dsn.clone().and_then(nonempty)?;
let dsn = match dsn.parse::<sentry::types::Dsn>() {
Ok(dsn) => dsn,
Err(err) => {
eprintln!("Ignoring invalid BUGSINK_DSN: {err}");
return None;
}
};
Some(sentry::init(sentry::ClientOptions {
dsn: Some(dsn),
environment: config
.environment
.clone()
.and_then(nonempty)
.map(Cow::Owned),
release: Some(Cow::Owned(
config
.release
.clone()
.and_then(nonempty)
.unwrap_or_else(default_release),
)),
send_default_pii: config.send_default_pii,
traces_sample_rate: 0.0,
..Default::default()
}))
}
pub fn frontend_config(
dsn: Option<String>,
environment: Option<String>,
release: Option<String>,
send_default_pii: bool,
) -> Option<FrontendConfig> {
dsn.and_then(nonempty).map(|dsn| FrontendConfig {
dsn,
environment: environment.and_then(nonempty),
release: release.and_then(nonempty),
send_default_pii,
})
}