rumtk_core/
cli.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) 2025  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
21pub mod cli_utils {
22    use crate::core::RUMResult;
23    use crate::strings::{format_compact, RUMString, RUMStringConversions};
24    use clap::Parser;
25    use std::io::{stdin, Read};
26    use std::num::NonZeroU16;
27
28    ///
29    /// Example CLI parser that can be used to paste in your binary and adjust as needed.
30    ///
31    /// Note, this is only an example.
32    ///
33    #[derive(Parser, Debug)]
34    #[command(author, version, about, long_about = None)]
35    pub struct RUMTKArgs {
36        ///
37        /// For interface crate only. Specifies the ip address to connect to.
38        ///
39        /// In outbound mode, `--ip` and `--port` are required parameters.
40        ///
41        /// In inbound mode, you can omit either or both parameters.
42        ///
43        #[arg(short, long)]
44        ip: Option<RUMString>,
45        ///
46        /// For interface crate only. Specifies the port to connect to.
47        ///
48        /// In outbound mode, `--ip` and `--port` are required parameters.
49        ///
50        /// In inbound mode, you can omit either or both parameters.
51        ///
52        #[arg(short, long)]
53        port: Option<NonZeroU16>,
54        ///
55        /// For process crate only. Specifies command line script to execute on message.
56        ///
57        #[arg(short, long)]
58        x: Option<RUMString>,
59        ///
60        /// Number of processing threads to allocate for this program.
61        ///
62        #[arg(short, long, default_value_t = 1)]
63        threads: usize,
64        ///
65        /// For interface crate only. Specifies if the interface is in outbound mode.
66        ///
67        /// In outbound mode, `--ip` and `--port` are required parameters.
68        ///
69        /// In inbound mode, you can omit either or both parameters.
70        ///
71        #[arg(short, long)]
72        outbound: bool,
73        ///
74        /// Request program runs in debug mode and log more information.
75        ///
76        #[arg(short, long, default_value_t = false)]
77        debug: bool,
78        ///
79        /// Request program runs in dry run mode and simulate as many steps as possible but not commit
80        /// to a critical non-reversible step.
81        ///
82        /// For example, if it was meant to write contents to a file, stop before doing so.
83        ///
84        #[arg(short, long, default_value_t = false)]
85        dry_run: bool,
86    }
87
88    pub fn read_stdin() -> RUMResult<RUMString> {
89        let stdin_handle = stdin();
90        let mut locked_stdin_handle = stdin_handle.lock();
91        let mut message = String::new();
92        match locked_stdin_handle.read_to_string(&mut message) {
93            Ok(s) => s,
94            Err(e) => {
95                return Err(format_compact!("Error reading from STDIN: {}", e));
96            }
97        };
98        Ok(message.to_rumstring())
99    }
100}
101
102pub mod macros {
103
104    ///
105    /// Reads STDIN and unescapes the incoming message.
106    /// Return this unescaped message.
107    ///
108    /// # Example
109    /// ```
110    /// use rumtk_core::core::RUMResult;
111    /// use rumtk_core::strings::RUMString;
112    /// use crate::rumtk_core::rumtk_read_stdin;
113    ///
114    /// fn test_read_stdin() -> RUMResult<RUMString> {
115    ///     rumtk_read_stdin!()
116    /// }
117    ///
118    /// match test_read_stdin() {
119    ///     Ok(s) => (),
120    ///     Err(e) => panic!("Error reading stdin because => {}", e)
121    /// }
122    /// ```
123    ///
124    #[macro_export]
125    macro_rules! rumtk_read_stdin {
126        (  ) => {{
127            use $crate::cli::cli_utils::read_stdin;
128            use $crate::strings::unescape_string;
129            let raw_message = read_stdin().expect("Failed to read stdin");
130            unescape_string(&raw_message)
131        }};
132    }
133
134    ///
135    /// Escapes a message and writes it to stdout via the print! macro.
136    ///
137    /// # Example
138    /// ```
139    /// use rumtk_core::rumtk_write_stdout;
140    ///
141    /// rumtk_write_stdout!("I ❤ my wife!");
142    /// ```
143    ///
144    #[macro_export]
145    macro_rules! rumtk_write_stdout {
146        ( $message:expr ) => {{
147            use $crate::strings::escape;
148            let escaped_message = escape($message);
149            print!("{}", &escaped_message);
150        }};
151    }
152}