rumtk_core/
lib.rs

1/*
2 * rumtk attempts to implement HL7 and medical protocols for interoperability in medicine.
3 * This toolkit aims to be reliable, simple, performant, and standards compliant.
4 * Copyright (C) 2024  Luis M. Santos, M.D.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21//#![feature(unboxed_closures)]
22#![feature(inherent_associated_types)]
23#![feature(type_alias_impl_trait)]
24#![feature(unboxed_closures)]
25#![feature(buf_read_has_data_left)]
26
27pub mod cache;
28pub mod cli;
29pub mod core;
30pub mod json;
31pub mod log;
32pub mod maths;
33pub mod net;
34pub mod queue;
35pub mod search;
36pub mod strings;
37pub mod threading;
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::cache::RUMCache;
43    use crate::search::rumtk_search::*;
44    use crate::strings::{RUMArrayConversions, RUMString, RUMStringConversions, StringUtils};
45    use compact_str::{format_compact, CompactString};
46    use serde::Deserialize;
47    use std::future::IntoFuture;
48    use std::sync::Arc;
49    use tokio::sync::RwLock;
50
51    #[test]
52    fn test_is_escaped_str() {
53        let input = "\r\n\'\\\"";
54        let expected = false;
55        let result = strings::is_escaped_str(input);
56        println!("Input: {} Expected: {} Got: {}", input, expected, result);
57        assert_eq!(
58            expected, result,
59            "Incorrect detection of unescaped string as escaped!!"
60        );
61        println!("Passed!")
62    }
63
64    #[test]
65    fn test_escaping_control() {
66        let input = "\r\n\'\"\\";
67        let expected = "\\r\\n\\'\\\"\\\\";
68        let result = strings::escape(input);
69        println!(
70            "Input: {} Expected: {} Got: {}",
71            input,
72            expected,
73            result.as_str()
74        );
75        assert_eq!(expected, result, "Incorrect string escaping!");
76        println!("Passed!")
77    }
78
79    #[test]
80    fn test_escaping_unicode() {
81        let input = "❤";
82        let expected = "\\u2764";
83        let result = strings::escape(input);
84        println!(
85            "Input: {} Expected: {} Got: {}",
86            input,
87            expected,
88            result.as_str()
89        );
90        assert_eq!(expected, result, "Incorrect string escaping!");
91        println!("Passed!")
92    }
93
94    #[test]
95    fn test_unescaping_unicode() {
96        let input = "❤";
97        let escaped = strings::escape(input);
98        let expected = "❤";
99        let result = RUMString::from_utf8(strings::unescape(escaped.as_str()).unwrap()).unwrap();
100        println!(
101            "Input: {} Expected: {} Got: {}",
102            input,
103            expected,
104            result.as_str()
105        );
106        assert_eq!(expected, result.as_str(), "Incorrect string unescaping!");
107        println!("Passed!")
108    }
109
110    #[test]
111    fn test_unescaping_string() {
112        let input = "I \\u2764 my wife!";
113        let expected = "I ❤ my wife!";
114        let result = strings::unescape_string(input).unwrap();
115        println!(
116            "Input: {} Expected: {} Got: {}",
117            input,
118            expected,
119            result.as_str()
120        );
121        assert_eq!(expected, result.as_str(), "Incorrect string unescaping!");
122        println!("Passed!")
123    }
124
125    #[test]
126    fn test_is_escaped_string() {
127        let input = "I \\u2764 my wife!";
128        let expected = true;
129        let result = strings::is_escaped_str(input);
130        println!("Input: {} Expected: {} Got: {}", input, expected, result);
131        assert_eq!(
132            expected, result,
133            "Escaped string detected as unescaped string!"
134        );
135        println!("Passed!")
136    }
137
138    #[test]
139    fn test_is_unescaped_string() {
140        let input = "I ❤ my wife!";
141        let expected = false;
142        let result = strings::is_escaped_str(input);
143        println!("Input: {} Expected: {} Got: {}", input, expected, result);
144        assert_eq!(
145            expected, result,
146            "Unescaped string detected as escaped string!"
147        );
148        println!("Passed!")
149    }
150
151    #[test]
152    fn test_unique_string() {
153        let input = "I❤mywife!";
154        assert!(input.is_unique(), "String was not detected as unique.");
155    }
156
157    #[test]
158    fn test_non_unique_string() {
159        let input = "I❤❤mywife!";
160        assert!(!input.is_unique(), "String was detected as unique.");
161    }
162
163    #[test]
164    fn test_escaping_string() {
165        let input = "I ❤ my wife!";
166        let expected = "I \\u2764 my wife!";
167        let result = strings::escape(input);
168        println!(
169            "Input: {} Expected: {} Got: {}",
170            input,
171            expected,
172            result.as_str()
173        );
174        assert_eq!(expected, result.as_str(), "Incorrect string escaping!");
175        println!("Passed!")
176    }
177
178    #[test]
179    fn test_autodecode_utf8() {
180        let input = "I ❤ my wife!";
181        let result = strings::try_decode(input.as_bytes());
182        println!(
183            "Input: {} Expected: {} Got: {}",
184            input,
185            input,
186            result.as_str()
187        );
188        assert_eq!(input, result, "Incorrect string decoding!");
189        println!("Passed!")
190    }
191
192    #[test]
193    fn test_autodecode_other() {
194        //TODO: Need an example of other encoding texts.
195        let input = "I ❤ my wife!";
196        let result = input;
197        println!("Input: {} Expected: {} Got: {}", input, input, result);
198        assert_eq!(input, result, "Incorrect string decoding!");
199        println!("Passed!")
200    }
201
202    #[test]
203    fn test_decode() {
204        let input = "I ❤ my wife!";
205        let result = strings::try_decode_with(input.as_bytes(), "utf-8");
206        println!(
207            "Input: {} Expected: {} Got: {}",
208            input,
209            input,
210            result.as_str()
211        );
212        assert_eq!(input, result, "Incorrect string decoding!");
213        println!("Passed!")
214    }
215
216    #[test]
217    fn test_rumcache_insertion() {
218        let mut cache: RUMCache<&str, CompactString> = RUMCache::with_capacity(5);
219        cache.insert("❤", CompactString::from("I ❤ my wife!"));
220        println!("Contents: {:#?}", &cache);
221        assert_eq!(cache.len(), 1, "Incorrect number of items in cache!");
222        println!("Passed!")
223    }
224
225    #[test]
226    fn test_search_string_letters() {
227        let input = "Hello World!";
228        let expr = r"\w";
229        let result = string_search(input, expr, "");
230        let expected: RUMString = RUMString::from("HelloWorld");
231        println!(
232            "Input: {:?} Expected: {:?} Got: {:?}",
233            input, expected, result
234        );
235        assert_eq!(expected, result, "String search results mismatch");
236        println!("Passed!")
237    }
238
239    #[test]
240    fn test_search_string_words() {
241        let input = "Hello World!";
242        let expr = r"\w+";
243        let result = string_search(input, expr, " ");
244        let expected: RUMString = RUMString::from("Hello World");
245        println!(
246            "Input: {:?} Expected: {:?} Got: {:?}",
247            input, expected, result
248        );
249        assert_eq!(expected, result, "String search results mismatch");
250        println!("Passed!")
251    }
252
253    #[test]
254    fn test_search_string_named_groups() {
255        let input = "Hello World!";
256        let expr = r"(?<hello>\w{5}) (?<world>\w{5})";
257        let result = string_search_named_captures(input, expr, "");
258        let expected: RUMString = RUMString::from("World");
259        println!(
260            "Input: {:?} Expected: {:?} Got: {:?}",
261            input, expected, result
262        );
263        assert_eq!(expected, result["world"], "String search results mismatch");
264        println!("Passed!")
265    }
266
267    #[test]
268    fn test_search_string_all_groups() {
269        let input = "Hello World!";
270        let expr = r"(?<hello>\w{5}) (?<world>\w{5})";
271        let result = string_search_all_captures(input, expr, "");
272        let expected: Vec<&str> = vec!["Hello", "World"];
273        println!(
274            "Input: {:?} Expected: {:?} Got: {:?}",
275            input, expected, result
276        );
277        assert_eq!(expected, result, "String search results mismatch");
278        println!("Passed!")
279    }
280
281    ///////////////////////////////////Threading Tests/////////////////////////////////////////////////
282    #[test]
283    fn test_default_num_threads() {
284        use num_cpus;
285        let threads = threading::threading_functions::get_default_system_thread_count();
286        assert_eq!(
287            threads >= num_cpus::get(),
288            true,
289            "Default thread count is incorrect! We got {}, but expected {}!",
290            threads,
291            num_cpus::get()
292        );
293    }
294
295    #[test]
296    fn test_execute_job() {
297        let rt = rumtk_init_threads!();
298        let expected = vec![1, 2, 3];
299        let task_processor = async |args: &SafeTaskArgs<i32>| -> TaskResult<i32> {
300            let owned_args = Arc::clone(args);
301            let lock_future = owned_args.read();
302            let locked_args = lock_future.await;
303            let mut results = TaskItems::<i32>::with_capacity(locked_args.len());
304            print!("Contents: ");
305            for arg in locked_args.iter() {
306                results.push(arg.clone());
307                println!("{} ", &arg);
308            }
309            Ok(results)
310        };
311        let locked_args = RwLock::new(expected.clone());
312        let task_args = SafeTaskArgs::<i32>::new(locked_args);
313        let task_result = rumtk_wait_on_task!(rt, task_processor, &task_args);
314        let result = task_result.unwrap();
315        assert_eq!(&result, &expected, "{}", format_compact!("Task processing returned a different result than expected! Expected {:?} \nResults {:?}", &expected, &result));
316    }
317
318    #[test]
319    fn test_execute_job_macros() {
320        let rt = rumtk_init_threads!();
321        let expected = vec![1, 2, 3];
322        let task_processor = async |args: &SafeTaskArgs<i32>| -> TaskResult<i32> {
323            let owned_args = Arc::clone(args);
324            let lock_future = owned_args.read();
325            let locked_args = lock_future.await;
326            let mut results = TaskItems::<i32>::with_capacity(locked_args.len());
327            print!("Contents: ");
328            for arg in locked_args.iter() {
329                results.push(arg.clone());
330                println!("{} ", &arg);
331            }
332            Ok(results)
333        };
334        let task_args = rumtk_create_task_args!(1, 2, 3);
335        let task_result = rumtk_wait_on_task!(rt, task_processor, &task_args);
336        let result = task_result.unwrap();
337        assert_eq!(&result, &expected, "{}", format_compact!("Task processing returned a different result than expected! Expected {:?} \nResults {:?}", &expected, &result));
338    }
339
340    #[test]
341    fn test_execute_job_macros_one_line() {
342        let rt = rumtk_init_threads!();
343        let expected = vec![1, 2, 3];
344        let result = rumtk_exec_task!(
345            async |args: &SafeTaskArgs<i32>| -> TaskResult<i32> {
346                let owned_args = Arc::clone(args);
347                let lock_future = owned_args.read();
348                let locked_args = lock_future.await;
349                let mut results = TaskItems::<i32>::with_capacity(locked_args.len());
350                print!("Contents: ");
351                for arg in locked_args.iter() {
352                    results.push(arg.clone());
353                    println!("{} ", &arg);
354                }
355                Ok(results)
356            },
357            vec![1, 2, 3]
358        )
359        .unwrap();
360        assert_eq!(&result, &expected, "{}", format_compact!("Task processing returned a different result than expected! Expected {:?} \nResults {:?}", &expected, &result));
361    }
362
363    #[test]
364    fn test_clamp_index_positive_index() {
365        let values = vec![1, 2, 3, 4];
366        let given_index = 3isize;
367        let max_size = values.len() as isize;
368        let index = clamp_index(&given_index, &max_size).unwrap();
369        assert_eq!(
370            index, 3,
371            "Index mismatch! Requested index {} but got {}",
372            &given_index, &index
373        );
374        assert_eq!(
375            values[index], 4,
376            "Value mismatch! Expected {} but got {}",
377            &values[3], &values[index]
378        );
379    }
380
381    #[test]
382    fn test_clamp_index_reverse_index() {
383        let values = vec![1, 2, 3, 4];
384        let given_index = -1isize;
385        let max_size = values.len() as isize;
386        let index = clamp_index(&given_index, &max_size).unwrap();
387        assert_eq!(
388            index, 4,
389            "Index mismatch! Requested index {} but got {}",
390            &given_index, &index
391        );
392        assert_eq!(
393            values[index - 1],
394            4,
395            "Value mismatch! Expected {} but got {}",
396            &values[3],
397            &values[index]
398        );
399    }
400
401    ///////////////////////////////////Queue Tests/////////////////////////////////////////////////
402    use crate::cli::cli_utils::print_license_notice;
403    use crate::core::clamp_index;
404    use crate::json::serialization::Serialize;
405    use crate::net::tcp::LOCALHOST;
406    use crate::threading::thread_primitives::{SafeTaskArgs, TaskItems, TaskResult};
407    use crate::threading::threading_functions::sleep;
408    use queue::queue::*;
409
410    #[test]
411    fn test_queue_data() {
412        let expected = vec![
413            RUMString::from("Hello"),
414            RUMString::from("World!"),
415            RUMString::from("Overcast"),
416            RUMString::from("and"),
417            RUMString::from("Sad"),
418        ];
419        let mut queue = TaskQueue::<RUMString>::new(&5).unwrap();
420        let locked_args = RwLock::new(expected.clone());
421        let task_args = SafeTaskArgs::<RUMString>::new(locked_args);
422        let processor = rumtk_create_task!(
423            async |args: &SafeTaskArgs<RUMString>| -> TaskResult<RUMString> {
424                let owned_args = Arc::clone(args);
425                let lock_future = owned_args.read();
426                let locked_args = lock_future.await;
427                let mut results = TaskItems::<RUMString>::with_capacity(locked_args.len());
428                print!("Contents: ");
429                for arg in locked_args.iter() {
430                    print!("{} ", &arg);
431                    results.push(RUMString::new(arg));
432                }
433                Ok(results)
434            },
435            task_args
436        );
437        queue.add_task::<_>(processor);
438        let results = queue.wait();
439        let mut result_data = Vec::<RUMString>::with_capacity(5);
440        for r in results {
441            for v in r.unwrap().iter() {
442                result_data.push(v.clone());
443            }
444        }
445        assert_eq!(result_data, expected, "Results do not match expected!");
446    }
447
448    ///////////////////////////////////Net Tests/////////////////////////////////////////////////
449    #[test]
450    fn test_server_start() {
451        let mut server = match rumtk_create_server!("localhost", 0) {
452            Ok(server) => server,
453            Err(e) => panic!("Failed to create server because {}", e),
454        };
455        match server.start(false) {
456            Ok(_) => (),
457            Err(e) => panic!("Failed to start server because {}", e),
458        }
459    }
460
461    #[test]
462    fn test_server_send() {
463        let msg = RUMString::from("Hello World!");
464        let mut server = match rumtk_create_server!(LOCALHOST, 0, 1) {
465            Ok(server) => server,
466            Err(e) => panic!("Failed to create server because {}", e),
467        };
468        match server.start(false) {
469            Ok(_) => (),
470            Err(e) => panic!("Failed to start server because {}", e),
471        };
472        let address_info = server.get_address_info().unwrap();
473        let (ip, port) = rumtk_get_ip_port!(address_info);
474        println!("Sleeping");
475        rumtk_sleep!(1);
476        let mut client = match rumtk_connect!(port) {
477            Ok(client) => client,
478            Err(e) => panic!("Failed to create server because {}", e),
479        };
480        let client_id = client.get_address().unwrap();
481        rumtk_sleep!(1);
482        match server.send(&client_id, &msg.to_raw()) {
483            Ok(_) => (),
484            Err(e) => panic!("Server failed to send message because {}", e),
485        };
486        rumtk_sleep!(1);
487        let received_message = client.receive().unwrap();
488        assert_eq!(
489            &msg.to_raw(),
490            &received_message,
491            "{}",
492            format_compact!(
493                "Received message does not match sent message by server {:?}",
494                &received_message
495            )
496        );
497    }
498
499    #[test]
500    fn test_server_receive() {
501        let msg = RUMString::from("Hello World!");
502        let mut server = match rumtk_create_server!(LOCALHOST, 0) {
503            Ok(server) => server,
504            Err(e) => panic!("Failed to create server because {}", e),
505        };
506        match server.start(false) {
507            Ok(_) => (),
508            Err(e) => panic!("Failed to start server because {}", e),
509        };
510        let address_info = server.get_address_info().unwrap();
511        let (ip, port) = rumtk_get_ip_port!(address_info);
512        println!("Sleeping");
513        rumtk_sleep!(1);
514        let mut client = match rumtk_connect!(port) {
515            Ok(client) => client,
516            Err(e) => panic!("Failed to create server because {}", e),
517        };
518        match client.send(&msg.to_raw()) {
519            Ok(_) => (),
520            Err(e) => panic!("Failed to send message because {}", e),
521        };
522        rumtk_sleep!(1);
523        let client_id = client.get_address().expect("Failed to get client id");
524        let incoming_message = server.receive(&client_id).unwrap().to_rumstring();
525        println!("Received message => {:?}", &incoming_message);
526        assert_eq!(&incoming_message, msg, "Received message corruption!");
527    }
528
529    #[test]
530    fn test_server_get_clients() {
531        let mut server = match rumtk_create_server!(LOCALHOST, 0) {
532            Ok(server) => server,
533            Err(e) => panic!("Failed to create server because {}", e),
534        };
535        match server.start(false) {
536            Ok(_) => (),
537            Err(e) => panic!("Failed to start server because {}", e),
538        };
539        let address_info = server.get_address_info().unwrap();
540        let (ip, port) = rumtk_get_ip_port!(address_info);
541        println!("Sleeping");
542        rumtk_sleep!(1);
543        let mut client = match rumtk_connect!(port) {
544            Ok(client) => client,
545            Err(e) => panic!("Failed to create server because {}", e),
546        };
547        rumtk_sleep!(1);
548        let expected_client_id = client.get_address().expect("Failed to get client id");
549        let clients = server.get_client_ids();
550        let incoming_client_id = clients.get(0).expect("Expected client to have connected!");
551        println!("Connected client id => {}", &incoming_client_id);
552        assert_eq!(
553            &incoming_client_id, &expected_client_id,
554            "Connected client does not match the connecting client! Client id => {}",
555            &incoming_client_id
556        );
557    }
558
559    #[test]
560    fn test_server_stop() {
561        let msg = RUMString::from("Hello World!");
562        let mut server = match rumtk_create_server!("localhost", 0) {
563            Ok(server) => server,
564            Err(e) => panic!("Failed to create server because {}", e),
565        };
566        match server.start(false) {
567            Ok(_) => (),
568            Err(e) => panic!("Failed to start server because {}", e),
569        };
570        println!("Sleeping");
571        rumtk_sleep!(1);
572        match server.stop() {
573            Ok(_) => (),
574            Err(e) => panic!("Failed to stop server because {}", e),
575        };
576    }
577
578    #[test]
579    fn test_server_get_address_info() {
580        let msg = RUMString::from("Hello World!");
581        let mut server = match rumtk_create_server!("localhost", 0) {
582            Ok(server) => server,
583            Err(e) => panic!("Failed to create server because {}", e),
584        };
585        match server.start(false) {
586            Ok(_) => (),
587            Err(e) => panic!("Failed to start server because {}", e),
588        };
589        println!("Sleeping");
590        rumtk_sleep!(1);
591        match server.get_address_info() {
592            Some(addr) => println!("Server address info => {}", addr),
593            None => panic!("No address. Perhaps the server was never initialized?"),
594        };
595    }
596
597    #[test]
598    fn test_client_send() {
599        let msg = RUMString::from("Hello World!");
600        let mut server = match rumtk_create_server!(LOCALHOST, 0) {
601            Ok(server) => server,
602            Err(e) => panic!("Failed to create server because {}", e),
603        };
604        match server.start(false) {
605            Ok(_) => (),
606            Err(e) => panic!("Failed to start server because {}", e),
607        };
608        let address_info = server.get_address_info().unwrap();
609        let (ip, port) = rumtk_get_ip_port!(address_info);
610        println!("Sleeping");
611        rumtk_sleep!(1);
612        let mut client = match rumtk_connect!(port) {
613            Ok(client) => client,
614            Err(e) => panic!("Failed to create server because {}", e),
615        };
616        rumtk_sleep!(2);
617        match client.send(&msg.to_raw()) {
618            Ok(_) => (),
619            Err(e) => panic!("Failed to send message because {}", e),
620        };
621        rumtk_sleep!(1);
622        let clients = server.get_client_ids();
623        let incoming_client_id = clients.first().expect("Expected client to have connected!");
624        let mut received_message = server.receive(incoming_client_id).unwrap();
625        if received_message.is_empty() {
626            rumtk_sleep!(1);
627            received_message = server.receive(incoming_client_id).unwrap();
628        }
629        assert_eq!(
630            &msg.to_raw(),
631            &received_message,
632            "{}",
633            format_compact!(
634                "Received message does not match sent message by client {:?}",
635                &received_message
636            )
637        );
638    }
639
640    ////////////////////////////JSON Tests/////////////////////////////////
641
642    #[test]
643    fn test_serialize_json() {
644        #[derive(Serialize)]
645        struct MyStruct {
646            hello: RUMString,
647        }
648
649        let hw = MyStruct {
650            hello: RUMString::from("World"),
651        };
652        let hw_str = rumtk_serialize!(&hw, true).unwrap();
653
654        assert!(
655            !hw_str.is_empty(),
656            "Empty JSON string generated from the test struct!"
657        );
658    }
659
660    #[test]
661    fn test_deserialize_json() {
662        #[derive(Serialize, Deserialize, PartialEq)]
663        struct MyStruct {
664            hello: RUMString,
665        }
666
667        let hw = MyStruct {
668            hello: RUMString::from("World"),
669        };
670        let hw_str = rumtk_serialize!(&hw, true).unwrap();
671        let new_hw: MyStruct = rumtk_deserialize!(&hw_str).unwrap();
672
673        assert!(
674            new_hw == hw,
675            "Deserialized JSON does not match the expected value!"
676        );
677    }
678
679    /*
680    #[test]
681    fn test_escape_unescape_json() {
682        #[derive(Serialize, Deserialize, PartialEq)]
683        struct MyStruct {
684            hello: RUMString,
685        }
686
687        let hw = MyStruct {
688            hello: RUMString::from("World"),
689        };
690
691        let hw_str = rumtk_serialize!(&hw, true).unwrap();
692        let hw_escaped_str = strings::basic_escape(&hw_str);
693        println!("Escaped => {}", hw_escaped_str);
694
695        let hw_unescaped_str = strings::unescape_string(&hw_escaped_str).unwrap();
696        println!("Unescaped => {}", hw_unescaped_str);
697        assert_eq!(
698            hw_str.to_rumstring(),
699            hw_unescaped_str.to_rumstring(),
700            "Unescaped serialized JSON mismatch!"
701        );
702
703        let new_hw: MyStruct = rumtk_deserialize!(&hw_unescaped_str).unwrap();
704
705        assert!(
706            new_hw == hw,
707            "Deserialized JSON does not match the expected value!"
708        );
709    }
710    */
711
712    ////////////////////////////CLI Tests/////////////////////////////////
713
714    #[test]
715    fn test_print_license_notice() {
716        print_license_notice("RUMTK", "2025", &vec!["Luis M. Santos, M.D."]);
717    }
718
719    //////////////////////////////////////////////////////////////////////////////////////////////
720}