rs_ray/
lib.rs

1use backtrace::Backtrace;
2use serde::Serialize;
3use std::{backtrace, collections::HashMap};
4
5mod payloads;
6
7use payloads::*;
8
9#[derive(Serialize, Debug, Clone)]
10struct RayPayload {
11    r#type: &'static str,
12    origin: RayOrigin,
13    content: Payload,
14}
15
16type RayMeta = HashMap<String, String>;
17
18#[derive(Serialize, Debug, Clone)]
19struct RayRequest {
20    uuid: String,
21    payloads: Vec<RayPayload>,
22    meta: RayMeta,
23}
24
25#[derive(Serialize, Debug, Clone)]
26struct RayOrigin {
27    function_name: String,
28    r#file: String,
29    line_number: String,
30    hostname: String,
31}
32impl RayOrigin {
33    pub fn new() -> Self {
34        Self {
35            function_name: "".to_string(),
36            file: "".to_string(),
37            line_number: "".to_string(),
38            hostname: "".to_string(),
39        }
40    }
41}
42
43pub struct Ray {
44    request: RayRequest,
45}
46
47impl Ray {
48    pub fn new() -> Self {
49        Self {
50            request: RayRequest {
51                uuid: uuid::Uuid::new_v4().to_string(),
52                payloads: vec![],
53                meta: HashMap::new(),
54            },
55        }
56    }
57
58    pub fn log(&mut self, values: Vec<String>) -> &mut Self {
59        let content = LogPayload {
60            values,
61            label: "Log".to_string(),
62        };
63
64        let payload = RayPayload {
65            r#type: "log",
66            content: Payload::from(content),
67            origin: RayOrigin::new(),
68        };
69
70        self.request.payloads.push(payload);
71        self.send();
72
73        self
74    }
75    pub fn text(&mut self, content: String) -> &mut Self {
76        let content = TextPayload {
77            content,
78            label: "Text".to_string(),
79        };
80
81        let payload = RayPayload {
82            r#type: "custom",
83            content: Payload::from(content),
84            origin: RayOrigin::new(),
85        };
86
87        self.request.payloads.push(payload);
88        self.send();
89
90        self
91    }
92
93    pub fn color(&mut self, color: &str) -> &mut Self {
94        let payload = RayPayload {
95            r#type: "color",
96            origin: RayOrigin::new(),
97            content: Payload::Color(ColorPayload {
98                color: color.to_string(),
99            }),
100        };
101
102        self.request.payloads.push(payload);
103        self.send();
104
105        self
106    }
107
108    pub fn confetti(&mut self) -> &mut Self {
109        let payload = RayPayload {
110            r#type: "confetti",
111            content: Payload::Confetti,
112            origin: RayOrigin::new(),
113        };
114
115        self.request.payloads.push(payload);
116        self.send();
117
118        self
119    }
120
121    pub fn clear_all(&mut self) -> &mut Self {
122        let payload = RayPayload {
123            r#type: "clear_all",
124            content: Payload::ClearAll,
125            origin: RayOrigin::new(),
126        };
127
128        self.request.payloads.push(payload);
129        self.send();
130
131        self
132    }
133
134    // Async version using tokio
135    #[cfg(feature = "with_tokio")]
136    fn send(&self) {
137        use tokio::task;
138
139        let request = self.request.clone();
140
141        let _ = task::spawn_blocking(move || {
142            let client = reqwest::blocking::Client::new();
143            let _ = client.post("http://localhost:23517/").json(&request).send();
144        });
145    }
146
147    // Blocking version without tokio
148    #[cfg(not(feature = "with_tokio"))]
149    fn send(&self) {
150        let client = reqwest::blocking::Client::new();
151        let _ = client
152            .post("http://localhost:23517/")
153            .json(&self.request)
154            .send();
155    }
156}
157
158#[macro_export]
159macro_rules! ray {
160    () => {{
161        Ray::new()
162    }};
163    ($($arg:expr),*) => {{
164        let mut ray = Ray::new();
165        let mut vec = Vec::new();
166
167       $(vec.push(format!("{:?}", $arg));)*
168
169        ray.log(vec);
170
171        ray
172    }};
173}
174
175#[cfg(test)]
176#[cfg(not(feature = "with_tokio"))]
177mod tests {
178    use super::*;
179
180    #[derive(Debug)]
181    struct TestStruct(&'static str);
182
183    #[test]
184    fn macro_no_args() {
185        let _ray = ray!();
186    }
187
188    #[test]
189    fn macro_one_arg() {
190        let _ray = ray!(TestStruct("One arg")).color("green");
191    }
192
193    #[test]
194    fn macro_multiple_args() {
195        let _ray = ray!("multiple", TestStruct("args"), vec![1, 2, 3]).color("green");
196    }
197
198    #[test]
199    fn log() {
200        ray!().log(vec!["log".to_string()]);
201    }
202
203    #[test]
204    fn text() {
205        ray!().text("text".to_string());
206    }
207
208    #[test]
209    fn color() {
210        ray!("red").color("red");
211
212        ray!("green").color("green");
213    }
214
215    #[test]
216    fn confetti() {
217        ray!().confetti();
218    }
219
220    #[test]
221    fn clear_all() {
222        ray!().clear_all();
223    }
224}
225
226#[cfg(feature = "with_tokio")]
227mod tests {
228    use super::*;
229
230    #[tokio::test]
231    async fn text() {
232        ray!("tokio").color("red").confetti();
233    }
234}