Skip to main content

quo_rust/
lib.rs

1mod types;
2
3#[cfg(target_family = "wasm")]
4use js_sys::Date;
5#[cfg(not(target_family = "wasm"))]
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use crate::types::{QuoPayload, QuoPayloadLanguage, QuoPayloadMeta, QuoPayloadVariable};
9use std::fmt::Debug;
10use ureq::config::Config;
11
12/// This fn creates a QuoPayload. You might or might not question why this is a separate function: for testing.
13///
14/// # Example
15///
16/// let mut big_number: i128;
17///
18/// big_number = 170141183460469231731687303715884105727;
19///
20/// quo_create_payload(big_number, "big_number", line!(), file!());
21///
22#[cfg(debug_assertions)]
23#[doc(hidden)]
24fn quo_create_payload<T: Debug>(
25    value: T,
26    name: &str,
27    line: u32,
28    file: &str,
29    is_mutable: bool,
30) -> QuoPayload {
31    let var_type = std::any::type_name_of_val(&value).to_string();
32    let name = name;
33    let value = format!("{:?}", value);
34    let line = line;
35    let file = file;
36    let package_name = option_env!("CARGO_PKG_NAME").unwrap_or("Rust project");
37
38    #[cfg(target_family = "wasm")]
39    let (time_epoch_ms, uid) = {
40        let now = js_sys::Date::now();
41        (now as i64, now.to_string())
42    };
43
44    #[cfg(not(target_family = "wasm"))]
45    let (time_epoch_ms, uid) = {
46        let since_the_epoch = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
47        (
48            since_the_epoch.as_millis() as i64,
49            since_the_epoch.as_nanos().to_string(),
50        )
51    };
52
53    QuoPayload {
54        language: QuoPayloadLanguage::Rust,
55        meta: QuoPayloadMeta {
56            id: 0,
57            uid,
58            origin: package_name.to_string(),
59            sender_origin: format!("{}:{}", file, line),
60            time_epoch_ms,
61            variable: QuoPayloadVariable {
62                var_type: var_type.clone(),
63                name: name.to_string(),
64                value: value.clone(),
65                mutable: is_mutable,
66                // @TODO Correctly detect const.
67                is_constant: name == name.to_uppercase(),
68            },
69        },
70    }
71}
72
73/// This fn sends the provided variable to Quo.
74///
75/// # Example
76///
77/// let mut big_number: i128;
78///
79/// big_number = 170141183460469231731687303715884105727;
80///
81/// quo(big_number, "big_number", line!(), file!());
82///
83#[cfg(debug_assertions)]
84#[doc(hidden)]
85fn quo<T: Debug>(value: T, name: &str, line: u32, file: &str, is_mutable: bool) -> () {
86    #[cfg(debug_assertions)]
87    {
88        let env_host = option_env!("QUO_HOST").unwrap_or("http://127.0.0.1");
89        let env_port = option_env!("QUO_PORT").unwrap_or("7312");
90
91        let body = quo_create_payload(value, name, line, file, is_mutable);
92
93        let send_fn = move || {
94            let quo_server = format!("{}:{}", env_host, env_port);
95
96            let config = Config::builder().user_agent("Quo-Rust").build();
97
98            let agent = config.new_agent();
99
100            let _ = match agent.post(quo_server).send_json(body) {
101                Ok(_response) => {}
102                Err(ureq::Error::StatusCode(code)) => {
103                    eprintln!("[Quo] HTTP {} - is Quo running?", code)
104                }
105                Err(e) => {
106                    eprintln!("[Quo] error \"{}\" - is Quo running?", e.to_string())
107                }
108            };
109        };
110
111        #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
112        {
113            send_fn();
114        }
115
116        #[cfg(not(all(target_arch = "wasm32", not(target_feature = "atomics"))))]
117        {
118            std::thread::spawn(send_fn);
119        }
120    }
121}
122
123#[cfg(debug_assertions)]
124#[doc(hidden)]
125pub fn __private_quo<T: Debug>(
126    value: T,
127    name: &str,
128    line: u32,
129    file: &str,
130    is_mutable: bool,
131) -> () {
132    quo(value, name, line, file, is_mutable)
133}
134
135/// This macro sends the provided variable to Quo using the quo() fn.
136///
137/// # Example
138///
139/// let mut big_number: i128;
140///
141/// big_number = 170141183460469231731687303715884105727;
142///
143/// quo!(big_number);
144///
145#[macro_export]
146macro_rules! quo {
147    ($( mut $var:ident ),*) => {{
148        #[cfg(debug_assertions)]
149        $(
150            $crate::__private_quo(&$var, stringify!($var), line!(), file!(), true);
151        )*
152    }};
153    ($( $var:ident ),*) => {{
154        #[cfg(debug_assertions)]
155        $(
156            $crate::__private_quo(&$var, stringify!($var), line!(), file!(), false);
157        )*
158    }};
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn fn_test() {
167        let fn_test_var = 1234;
168
169        let payload = quo_create_payload(&fn_test_var, "fn_test_var", line!(), file!(), false);
170        assert_eq!(payload.meta.variable.name, "fn_test_var");
171        assert_eq!(payload.meta.variable.value, "1234");
172        assert_eq!(payload.meta.variable.var_type, "&i32");
173        assert!(!payload.meta.variable.mutable);
174        assert!(!payload.meta.variable.is_constant);
175    }
176
177    #[test]
178    fn macro_const_test() {
179        const VAR_I32: i32 = -1234;
180
181        quo!(VAR_I32, VAR_I32, VAR_I32);
182
183        let payload = quo_create_payload(&VAR_I32, "VAR_I32", line!(), file!(), false);
184        assert_eq!(payload.meta.variable.name, "VAR_I32");
185        assert_eq!(payload.meta.variable.value, "-1234");
186        assert_eq!(payload.meta.variable.var_type, "&i32");
187        assert!(!payload.meta.variable.mutable);
188        assert!(payload.meta.variable.is_constant);
189    }
190
191    #[test]
192    fn macro_i32_test() {
193        let var_i32: i32 = -1234;
194
195        let payload = quo_create_payload(&var_i32, "var_i32", line!(), file!(), false);
196        assert_eq!(payload.meta.variable.name, "var_i32");
197        assert_eq!(payload.meta.variable.value, "-1234");
198        assert_eq!(payload.meta.variable.var_type, "&i32");
199        assert!(!payload.meta.variable.mutable);
200        assert!(!payload.meta.variable.is_constant);
201    }
202
203    #[test]
204    fn macro_u32_test() {
205        let var_u32: u32 = 1234;
206
207        let payload = quo_create_payload(&var_u32, "var_u32", line!(), file!(), false);
208        assert_eq!(payload.meta.variable.name, "var_u32");
209        assert_eq!(payload.meta.variable.value, "1234");
210        assert_eq!(payload.meta.variable.var_type, "&u32");
211        assert!(!payload.meta.variable.mutable);
212        assert!(!payload.meta.variable.is_constant);
213    }
214
215    #[test]
216    fn macro_string_test() {
217        let var_string: &str = "string";
218
219        let payload = quo_create_payload(&var_string, "var_string", line!(), file!(), false);
220        assert_eq!(payload.meta.variable.name, "var_string");
221        assert_eq!(payload.meta.variable.value, "\"string\"");
222        assert_eq!(payload.meta.variable.var_type, "&&str");
223        assert!(!payload.meta.variable.mutable);
224        assert!(!payload.meta.variable.is_constant);
225    }
226
227    #[test]
228    fn macro_bool_test() {
229        let var_bool: bool = true;
230
231        let payload = quo_create_payload(&var_bool, "var_bool", line!(), file!(), false);
232        assert_eq!(payload.meta.variable.name, "var_bool");
233        assert_eq!(payload.meta.variable.value, "true");
234        assert_eq!(payload.meta.variable.var_type, "&bool");
235        assert!(!payload.meta.variable.mutable);
236        assert!(!payload.meta.variable.is_constant);
237    }
238
239    #[test]
240    fn macro_tupl_test() {
241        let var_tuple: (bool, String, String, String, u32) = (
242            false,
243            String::from("hope"),
244            String::from("this"),
245            String::from("works"),
246            23423,
247        );
248
249        let payload = quo_create_payload(&var_tuple, "var_tuple", line!(), file!(), false);
250        assert_eq!(payload.meta.variable.name, "var_tuple");
251        assert_eq!(
252            payload.meta.variable.value,
253            "(false, \"hope\", \"this\", \"works\", 23423)"
254        );
255        assert_eq!(
256            payload.meta.variable.var_type,
257            "&(bool, alloc::string::String, alloc::string::String, alloc::string::String, u32)"
258        );
259        assert!(!payload.meta.variable.mutable);
260        assert!(!payload.meta.variable.is_constant);
261    }
262
263    #[test]
264    fn macro_arr_test() {
265        let var_array: [String; 3] = [
266            String::from("hope"),
267            String::from("this"),
268            String::from("works"),
269        ];
270
271        let payload = quo_create_payload(&var_array, "var_array", line!(), file!(), false);
272        assert_eq!(payload.meta.variable.name, "var_array");
273        assert_eq!(
274            payload.meta.variable.value,
275            "[\"hope\", \"this\", \"works\"]"
276        );
277        assert_eq!(
278            payload.meta.variable.var_type,
279            "&[alloc::string::String; 3]"
280        );
281        assert!(!payload.meta.variable.mutable);
282        assert!(!payload.meta.variable.is_constant);
283    }
284
285    #[test]
286    fn macro_vec_test() {
287        let var_vector: Vec<String> = vec![
288            String::from("hope"),
289            String::from("this"),
290            String::from("works"),
291        ];
292
293        let payload = quo_create_payload(&var_vector, "var_vector", line!(), file!(), false);
294        assert_eq!(payload.meta.variable.name, "var_vector");
295        assert_eq!(
296            payload.meta.variable.value,
297            "[\"hope\", \"this\", \"works\"]"
298        );
299        assert_eq!(
300            payload.meta.variable.var_type,
301            "&alloc::vec::Vec<alloc::string::String>"
302        );
303        assert!(!payload.meta.variable.mutable);
304        assert!(!payload.meta.variable.is_constant);
305    }
306
307    #[test]
308    fn macro_mut_test() {
309        let mut var_mut = 1;
310        let payload1 = quo_create_payload(&var_mut, "var_mut", line!(), file!(), true);
311        assert_eq!(payload1.meta.variable.name, "var_mut");
312        assert_eq!(payload1.meta.variable.value, "1");
313        assert_eq!(payload1.meta.variable.var_type, "&i32");
314        assert!(payload1.meta.variable.mutable);
315        assert!(!payload1.meta.variable.is_constant);
316
317        var_mut = 2;
318        let payload2 = quo_create_payload(&var_mut, "var_mut", line!(), file!(), true);
319        assert_eq!(payload2.meta.variable.name, "var_mut");
320        assert_eq!(payload2.meta.variable.value, "2");
321        assert_eq!(payload2.meta.variable.var_type, "&i32");
322        assert!(payload2.meta.variable.mutable);
323        assert!(!payload2.meta.variable.is_constant);
324    }
325
326    #[test]
327    fn macro_immut_test() {
328        let var_immut = 1;
329        let payload = quo_create_payload(&var_immut, "var_immut", line!(), file!(), false);
330        assert_eq!(payload.meta.variable.name, "var_immut");
331        assert_eq!(payload.meta.variable.value, "1");
332        assert_eq!(payload.meta.variable.var_type, "&i32");
333        assert!(!payload.meta.variable.mutable);
334        assert!(!payload.meta.variable.is_constant);
335    }
336
337    #[test]
338    #[allow(dead_code)]
339    fn complex_type_test() {
340        #[derive(Debug)]
341        struct Complex {
342            a: i32,
343            b: String,
344            c: Vec<i32>,
345            d: bool,
346        }
347
348        let var_complex = Complex {
349            a: 1,
350            b: String::from("complex"),
351            c: vec![1, 2, 3],
352            d: true,
353        };
354
355        let payload = quo_create_payload(&var_complex, "var_complex", line!(), file!(), false);
356        assert_eq!(payload.meta.variable.name, "var_complex");
357        assert_eq!(
358            payload.meta.variable.var_type,
359            "&quo_rust::tests::complex_type_test::Complex"
360        );
361        assert!(payload.meta.variable.value.contains("a: 1"));
362        assert!(payload.meta.variable.value.contains("complex"));
363        assert!(!payload.meta.variable.mutable);
364        assert!(!payload.meta.variable.is_constant);
365    }
366
367    #[test]
368    #[allow(dead_code)]
369    fn large_var_test() {
370        #[derive(Debug)]
371        struct Large {
372            a: i32,
373            b: i32,
374            c: i32,
375            d: i32,
376            e: i32,
377            f: i32,
378            g: i32,
379            h: i32,
380            i: i32,
381            j: i32,
382        }
383
384        let var_large = Large {
385            a: 1,
386            b: 2,
387            c: 3,
388            d: 4,
389            e: 5,
390            f: 6,
391            g: 7,
392            h: 8,
393            i: 9,
394            j: 10,
395        };
396
397        let payload = quo_create_payload(&var_large, "var_large", line!(), file!(), false);
398        assert_eq!(payload.meta.variable.name, "var_large");
399        assert_eq!(
400            payload.meta.variable.var_type,
401            "&quo_rust::tests::large_var_test::Large"
402        );
403        assert!(payload.meta.variable.value.contains("j: 10"));
404        assert!(!payload.meta.variable.mutable);
405        assert!(!payload.meta.variable.is_constant);
406    }
407
408    #[test]
409    fn macro_mut_explicit_test() {
410        let mut var_mut = 1;
411        quo!(mut var_mut);
412
413        let payload = quo_create_payload(&var_mut, "var_mut", line!(), file!(), true);
414        assert!(payload.meta.variable.mutable);
415
416        var_mut = 2;
417        quo!(var_mut);
418    }
419}