violet_mail/
client.rs

1use std::{
2    sync::{Arc, RwLock},
3    thread,
4    time::Duration,
5};
6
7use chrono::Utc;
8use isahc::{config::Configurable, Request, RequestExt};
9
10use log::{set_logger, Metadata, Record};
11
12use crate::{VioletLog, VioletLogSeverity};
13pub type GenericError = Box<dyn std::error::Error + Send + Sync + 'static>;
14pub type GResult<T> = Result<T, GenericError>;
15
16lazy_static::lazy_static! {
17    static ref CLIENT: HttpVioletData = HttpVioletData::new();
18}
19
20#[derive(Clone)]
21#[non_exhaustive]
22pub struct HttpVioletData {
23    config: Arc<RwLock<Option<VioletBuilder>>>,
24}
25
26#[derive(Debug, Clone)]
27pub struct VioletBuilder {
28    indentifier: u64,
29    token: String,
30    send_err_async: bool,
31    default_title: String,
32    send_level: VioletLogSeverity,
33}
34
35impl VioletBuilder {
36    pub fn new(token: impl AsRef<str>, indentifier: u64) -> Self {
37        Self {
38            token: token.as_ref().to_string(),
39            indentifier,
40            default_title: env!("CARGO_PKG_NAME").into(),
41            send_err_async: false,
42            send_level: VioletLogSeverity::Error,
43        }
44    }
45
46    pub fn enable_async(mut self) -> Self {
47        self.send_err_async = true;
48        self
49    }
50
51    pub fn set_title(mut self, title: impl AsRef<str>) -> Self {
52        self.default_title = title.as_ref().to_string();
53        self
54    }
55
56    pub fn set_send_min_level(mut self, level: VioletLogSeverity) -> Self {
57        self.send_level = level;
58        self
59    }
60
61    pub fn init(self) -> GResult<()> {
62        if CLIENT
63            .config
64            .read()
65            .map_err(|err| format!("Poisoded mutex here: {:?}", &err))?
66            .is_some()
67        {
68            return Ok(());
69        }
70
71        CLIENT.set_config(self)?;
72        set_logger(&*CLIENT).unwrap();
73
74        Ok(())
75    }
76}
77
78impl HttpVioletData {
79    fn new() -> Self {
80        Self {
81            config: Arc::new(RwLock::new(None)),
82        }
83    }
84
85    pub fn get_http() -> &'static Self {
86        &CLIENT
87    }
88
89    fn set_config(&self, config: VioletBuilder) -> GResult<()> {
90        *self
91            .config
92            .write()
93            .map_err(|err| format!("Mutex is poisoned: {:?}", err))? = Some(config);
94        Ok(())
95    }
96
97    pub async fn send_data(
98        &self,
99        title: String,
100        severity: VioletLogSeverity,
101        message: String,
102    ) -> GResult<()> {
103        let log_vio = VioletLog::new(severity, title, message);
104        let log_vio_json = serde_json::to_string(&log_vio)?;
105        let config = self
106            .config
107            .clone()
108            .read()
109            .map_err(|err| format!("Poisoned mutex: {:?}", err))?
110            .as_ref()
111            .ok_or("Violet não foi inicializada")?
112            .clone();
113        println!("{:?}", &log_vio_json);
114        Request::post(format!(
115            "https://violet.zuraaa.com/api/apps/{}/events",
116            config.indentifier
117        ))
118        .header("Content-Type", "application/json")
119        .header("Authorization", config.token)
120        .timeout(Duration::from_secs(20))
121        .body(log_vio_json)?
122        .send_async()
123        .await?;
124        Ok(())
125    }
126}
127
128impl log::Log for HttpVioletData {
129    fn enabled(&self, _metadata: &Metadata) -> bool {
130        true
131    }
132
133    fn flush(&self) {
134        todo!()
135    }
136
137    fn log(&self, record: &Record) {
138        if self.config.read().unwrap().is_none() {
139            panic!("Violet não foi inicializado");
140        }
141
142        let config = self
143            .config
144            .read()
145            .as_ref()
146            .unwrap()
147            .as_ref()
148            .unwrap()
149            .clone();
150
151        let pointer_data = (record.level(), record.args().to_string());
152
153        {
154            let level = crate::convert_level_to_string(&pointer_data.0);
155            let data = Utc::now();
156            let data_formated = data.format("%d/%m/%Y %H:%M:%S").to_string();
157            println!("[({}) ({})]: {}", data_formated, level, &pointer_data.1)
158        }
159
160        {
161            let level_u8 = u8::from(config.send_level.clone());
162            let level_event_u8 = crate::convert_level_to_u8(&pointer_data.0);
163            if level_event_u8 > level_u8 {
164                return;
165            }
166        }
167
168        if config.send_err_async {
169            let cloned_self = self.clone();
170            thread::spawn(move || {
171                futures::executor::block_on(async {
172                    cloned_self
173                        .send_data(config.default_title, pointer_data.0.into(), pointer_data.1)
174                        .await
175                        .ok();
176                });
177            });
178        } else {
179            futures::executor::block_on(async {
180                self.send_data(config.default_title, pointer_data.0.into(), pointer_data.1)
181                    .await
182                    .ok();
183            })
184        }
185    }
186}