1use sentry::ClientOptions;
2use sentry_types::Dsn;
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum DrCodeError {
7 #[error("Missing required configuration field: {0}")]
8 MissingField(String),
9 #[error("Failed to initialize Sentry: {0}")]
10 InitializationError(String),
11}
12
13pub struct Config {
14 pub public_key: String,
15 pub project_id: String,
16 pub traces_sample_rate: f32,
17 pub profiles_sample_rate: f32,
18}
19
20pub struct DrCode {
21 config: Config,
22 dsn: Dsn,
23}
24
25#[derive(Debug, Clone, Copy)]
26pub enum Level {
27 Debug,
28 Info,
29 Warning,
30 Error,
31 Fatal,
32}
33
34impl From<Level> for sentry::Level {
35 fn from(level: Level) -> Self {
36 match level {
37 Level::Debug => sentry::Level::Debug,
38 Level::Info => sentry::Level::Info,
39 Level::Warning => sentry::Level::Warning,
40 Level::Error => sentry::Level::Error,
41 Level::Fatal => sentry::Level::Fatal,
42 }
43 }
44}
45
46impl DrCode {
47 pub fn new(config: Config) -> Result<Self, DrCodeError> {
48 Self::validate_config(&config)?;
49 let dsn = Self::construct_dsn(&config)?;
50 Ok(Self { config, dsn })
51 }
52
53 fn validate_config(config: &Config) -> Result<(), DrCodeError> {
54 if config.public_key.is_empty() {
55 return Err(DrCodeError::MissingField("publicKey".to_string()));
56 }
57 if config.project_id.is_empty() {
58 return Err(DrCodeError::MissingField("projectId".to_string()));
59 }
60 Ok(())
61 }
62
63 fn construct_dsn(config: &Config) -> Result<Dsn, DrCodeError> {
64 let dsn_string = format!(
65 "https://{}@pulse.drcode.ai:443/{}",
66 config.public_key, config.project_id
67 );
68 dsn_string.parse::<Dsn>().map_err(|e| DrCodeError::InitializationError(e.to_string()))
69 }
70
71 pub fn init(&self) -> Result<(), DrCodeError> {
72 let options = ClientOptions {
73 dsn: Some(self.dsn.clone()),
74 traces_sample_rate: self.config.traces_sample_rate,
75 ..Default::default()
76 };
77
78 let _guard = sentry::init(options);
79 Ok(())
80 }
81
82 pub fn capture_message(&self, message: &str, level: Level) {
83 sentry::capture_message(message, level.into());
84 }
85
86 pub fn capture_exception<E>(&self, error: &E)
87 where
88 E: std::error::Error + Send + Sync + 'static,
89 {
90 sentry::capture_error(error);
91 }
92}
93
94pub fn error_handler<E>(err: E)
95where
96 E: std::error::Error + Send + Sync + 'static,
97{
98 sentry::capture_error(&err);
99}