ray_rust/
lib.rs

1use rustc_version::version_meta;
2use serde::{Deserialize, Serialize};
3
4mod message;
5use message::*;
6
7#[cfg(test)]
8mod tests;
9
10#[macro_export]
11macro_rules! ray {
12    // If no arguments are passed, just create a new Ray instance
13    () => {{
14        let ray = Ray::new();
15
16        ray
17    }};
18    // If one or more arguments are passed, log them
19    ($($arg:expr),*) => {{
20        let mut ray = Ray::new();
21        let mut vec = Vec::new();
22
23        // Push each argument to the vector
24        $(vec.push(format!("{:#?}", $arg));)*
25
26        ray.log(vec);
27
28        ray
29    }};
30}
31
32#[macro_export]
33macro_rules! rd {
34    () => {{
35        let mut ray = Ray::new();
36
37        ray.die(1);
38
39        ray
40    }};
41    ($($arg:expr),*) => {{
42        let mut ray = Ray::new();
43        let mut vec = Vec::new();
44
45        $(vec.push(format!("{:#?}", $arg));)*
46
47        ray.log(vec);
48
49        ray.die(1);
50
51        ray
52    }};
53}
54
55#[derive(Debug, Serialize, Deserialize, Clone)]
56pub struct RayPayload {
57    uuid: String,
58    payloads: Vec<RayContent>,
59    meta: RayMeta,
60}
61
62#[derive(Debug, Serialize, Deserialize, Clone)]
63pub struct RayContent {
64    #[serde(rename = "type")] // rename the field to "type" since it's a reserved keyword
65    content_type: String,
66    content: RayMessage,
67    origin: RayOrigin,
68}
69
70#[derive(Debug, Serialize, Deserialize, Clone)]
71pub struct RayOrigin {
72    function_name: String,
73    file: String,
74    line_number: u32,
75    hostname: String,
76}
77
78impl RayOrigin {
79    pub fn new() -> Self {
80        // TODO: Get the function name, file, and line number from the macro
81        // I don't think we can get the function name, file, and line number from the macro, at
82        // least not easily. So we'll just set them to empty strings and 0 for now.
83        let function_name = "ray".to_string();
84        let file = "".to_string();
85        let line_number = 0;
86        let hostname = "localhost".to_string();
87
88        Self {
89            function_name,
90            file,
91            line_number,
92            hostname,
93        }
94    }
95}
96
97#[derive(Debug, Serialize, Deserialize, Clone)]
98pub struct RayMeta {
99    // TODO: See if we can get more useful information, edition? etc.
100    // I don't even what if this shows up on ray, but I'm going to leave it here for now
101    rustc_version: String,
102    package_version: String,
103}
104
105impl RayMeta {
106    pub fn new() -> Self {
107        let rustc_version = match version_meta() {
108            Ok(meta) => meta.short_version_string,
109            Err(_) => "🤷".to_string(),
110        };
111
112        Self {
113            rustc_version,
114            package_version: env!("CARGO_PKG_VERSION").to_string(),
115        }
116    }
117}
118
119pub struct Ray {
120    request: RayPayload,
121    is_enabled: bool,
122}
123
124impl Ray {
125    pub fn new() -> Self {
126        Self {
127            request: RayPayload {
128                uuid: uuid::Uuid::new_v4().to_string(),
129                payloads: vec![],
130                meta: RayMeta::new(),
131            },
132            is_enabled: true,
133        }
134    }
135
136    #[cfg(feature = "with_tokio")]
137    pub fn send(&mut self) {
138        if !self.is_enabled {
139            return ();
140        }
141
142        let request = self.request.clone();
143
144        let _ = tokio::task::spawn_blocking(move || {
145            let client = reqwest::blocking::Client::new();
146
147            let _ = client.post("http://localhost:23517").json(&request).send();
148        });
149    }
150
151    #[cfg(not(feature = "with_tokio"))]
152    pub fn send(&mut self) {
153        if !self.is_enabled {
154            return ();
155        }
156
157        let request = self.request.clone();
158
159        let client = reqwest::blocking::Client::new();
160
161        let _ = client.post("http://localhost:23517").json(&request).send();
162    }
163
164    pub fn die(&mut self, status: i32) {
165        panic!("exited with code {}", status);
166
167        // TODO: I think we need to use process::exit here to actually exit the process since this
168        // doesn't stop threaded work but I can't see a nice way to test this. I'm going to leave it
169        // std::process::exit(status);
170    }
171
172    pub fn clear_all(&mut self) -> &mut Self {
173        let message = RayMessage::ClearAll(RayClearAll {
174            label: RayMessageType::ClearAll,
175        });
176
177        let content = RayContent {
178            content_type: RayClearAll::get_type(),
179            origin: RayOrigin::new(),
180            content: message,
181        };
182
183        self.request.payloads.push(content);
184
185        self.send();
186        self
187    }
188
189    pub fn new_screen(&mut self, name: Option<&str>) -> &mut Self {
190        let message = RayMessage::NewScreen(RayNewScreen {
191            label: RayMessageType::NewScreen,
192            name: name.unwrap_or("").to_string(),
193        });
194
195        let content = RayContent {
196            content_type: RayNewScreen::get_type(),
197            origin: RayOrigin::new(),
198            content: message,
199        };
200
201        self.request.payloads.push(content);
202
203        self.send();
204        self
205    }
206
207    pub fn clear_screen(&mut self) -> &mut Self {
208        self.new_screen(None)
209    }
210
211    pub fn log(&mut self, values: Vec<String>) -> &mut Self {
212        let message = RayMessage::Log(RayLog {
213            label: RayMessageType::Log,
214            values,
215        });
216
217        let content = RayContent {
218            content_type: RayLog::get_type(),
219            origin: RayOrigin::new(),
220            content: message,
221        };
222
223        self.request.payloads.push(content);
224
225        self.send();
226        self
227    }
228
229    pub fn text(&mut self, value: &str) -> &mut Self {
230        let message = RayMessage::Text(RayText {
231            label: RayMessageType::Text,
232            content: value.to_string(),
233        });
234
235        let content = RayContent {
236            content_type: RayText::get_type(),
237            origin: RayOrigin::new(),
238            content: message,
239        };
240
241        self.request.payloads.push(content);
242
243        self.send();
244        self
245    }
246
247    pub fn color(&mut self, value: &str) -> &mut Self {
248        let message = RayMessage::Color(RayColor {
249            color: RayColors::from(value.to_string()),
250        });
251
252        let content = RayContent {
253            content_type: RayColor::get_type(),
254            origin: RayOrigin::new(),
255            content: message,
256        };
257
258        self.request.payloads.push(content);
259
260        self.send();
261        self
262    }
263
264    pub fn html(&mut self, value: &str) -> &mut Self {
265        let message = RayMessage::HTML(RayHtml {
266            label: RayMessageType::HTML,
267            content: value.to_string(),
268        });
269
270        let content = RayContent {
271            content_type: RayHtml::get_type(),
272            origin: RayOrigin::new(),
273            content: message,
274        };
275
276        self.request.payloads.push(content);
277
278        self.send();
279        self
280    }
281
282    pub fn confetti(&mut self) -> &mut Self {
283        let message = RayMessage::Confetti(RayConfetti {
284            label: RayMessageType::Confetti,
285        });
286
287        let content = RayContent {
288            content_type: RayConfetti::get_type(),
289            origin: RayOrigin::new(),
290            content: message,
291        };
292
293        self.request.payloads.push(content);
294
295        self.send();
296        self
297    }
298
299    pub fn charles(&mut self) -> &mut Self {
300        let message = RayMessage::Charles(RayCharles {
301            content: "🎶 🎹 🎷 🕺".to_string(),
302        });
303
304        let content = RayContent {
305            content_type: RayCharles::get_type(),
306            origin: RayOrigin::new(),
307            content: message,
308        };
309
310        self.request.payloads.push(content);
311
312        self.send();
313        self
314    }
315
316    pub fn count(&mut self, _name: &str) -> &mut Self {
317        // create a new counter hashmap with the name?
318        // increment the counter hashmap with the name?
319        // return a custom message with "called name x times"
320
321        unimplemented!();
322    }
323
324    pub fn counter_value(&mut self) -> &mut Self {
325        unimplemented!();
326    }
327
328    pub fn clear_counters(&mut self) -> &mut Self {
329        unimplemented!();
330    }
331
332    pub fn disable(&mut self) -> &mut Self {
333        self.is_enabled = false;
334
335        self
336    }
337
338    pub fn disabled(&mut self) -> bool {
339        !self.is_enabled
340    }
341
342    pub fn enable(&mut self) -> &mut Self {
343        self.is_enabled = true;
344
345        self
346    }
347
348    pub fn enabled(&mut self) -> bool {
349        self.is_enabled
350    }
351
352    pub fn file(&mut self) -> &mut Self {
353        unimplemented!();
354    }
355
356    pub fn gray(&mut self) -> &mut Self {
357        unimplemented!();
358    }
359
360    pub fn green(&mut self) -> &mut Self {
361        unimplemented!();
362    }
363
364    pub fn hide(&mut self) -> &mut Self {
365        unimplemented!();
366    }
367
368    pub fn hide_app(&mut self) -> &mut Self {
369        unimplemented!();
370    }
371
372    pub fn image(&mut self) -> &mut Self {
373        unimplemented!();
374    }
375
376    // I'm not sure if this is possible in Rust
377    pub fn r#if(&mut self) -> &mut Self {
378        unimplemented!();
379    }
380
381    pub fn json(&mut self) -> &mut Self {
382        unimplemented!();
383    }
384
385    pub fn label(&mut self) -> &mut Self {
386        unimplemented!();
387    }
388
389    pub fn large(&mut self) -> &mut Self {
390        unimplemented!();
391    }
392
393    pub fn limit(&mut self) -> &mut Self {
394        unimplemented!();
395    }
396
397    pub fn link(&mut self) -> &mut Self {
398        unimplemented!();
399    }
400
401    pub fn measure(&mut self) -> &mut Self {
402        unimplemented!();
403    }
404
405    pub fn notify(&mut self) -> &mut Self {
406        unimplemented!();
407    }
408
409    pub fn orange(&mut self) -> &mut Self {
410        unimplemented!();
411    }
412
413    pub fn pass(&mut self) -> &mut Self {
414        unimplemented!();
415    }
416
417    pub fn pause(&mut self) -> &mut Self {
418        unimplemented!();
419    }
420
421    pub fn info(&mut self) -> &mut Self {
422        unimplemented!();
423    }
424
425    pub fn purple(&mut self) -> &mut Self {
426        unimplemented!();
427    }
428
429    // TODO: This has 3 functions max, per_second and clear
430    pub fn rate_limiter(&mut self) -> &mut Self {
431        unimplemented!();
432    }
433
434    pub fn red(&mut self) -> &mut Self {
435        unimplemented!();
436    }
437
438    pub fn separator(&mut self) -> &mut Self {
439        unimplemented!();
440    }
441
442    pub fn show_app(&mut self) -> &mut Self {
443        unimplemented!();
444    }
445
446    pub fn small(&mut self) -> &mut Self {
447        unimplemented!();
448    }
449
450    pub fn table(&mut self) -> &mut Self {
451        unimplemented!();
452    }
453
454    pub fn to_json(&mut self) -> &mut Self {
455        unimplemented!();
456    }
457
458    pub fn trace(&mut self) -> &mut Self {
459        unimplemented!();
460    }
461
462    pub fn url(&mut self) -> &mut Self {
463        unimplemented!();
464    }
465
466    pub fn xml(&mut self) -> &mut Self {
467        unimplemented!();
468    }
469}