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 #[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 #[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}