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