├── .gitignore ├── Cargo.toml ├── hl7-query ├── src │ ├── print │ │ ├── mod.rs │ │ ├── query.rs │ │ ├── hl7.rs │ │ ├── table.rs │ │ └── json.rs │ ├── map.rs │ ├── cli.rs │ └── main.rs ├── Cargo.toml ├── README.md ├── tests │ └── cli.rs └── assets │ ├── JSON.sublime-syntax │ └── ansi.tmTheme ├── hl7-send-receive ├── README.md ├── Cargo.toml └── src │ ├── print.rs │ ├── ack.rs │ ├── cli.rs │ └── main.rs ├── .github └── workflows │ └── rust.yml ├── README.md ├── assets └── sample_adt_a01.hl7 ├── LICENSE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "hl7-query", 4 | "hl7-send-receive", 5 | ] 6 | resolver = "2" 7 | -------------------------------------------------------------------------------- /hl7-query/src/print/mod.rs: -------------------------------------------------------------------------------- 1 | mod hl7; 2 | pub use hl7::print_message_hl7; 3 | mod json; 4 | pub use json::print_message_json; 5 | mod table; 6 | pub use table::print_message_table; 7 | mod query; 8 | pub use query::print_query_results; 9 | -------------------------------------------------------------------------------- /hl7-send-receive/README.md: -------------------------------------------------------------------------------- 1 | # HS 2 | 3 | HS is a tool for sending and receiving HL7 messages over the MLLP protocol. 4 | 5 | ## Features 6 | 7 | - [X] Send a HL7 message over MLLP (message sourced from a file or stdin). 8 | - [X] Open a server to receive HL7 messages over MLLP and print them to stdout. 9 | - [X] Generate and return reasonably formed HL7 ACKs 10 | - [X] Limit the number of received messages before stopping the server (or run indefinitely). 11 | 12 | ## Non-Goals 13 | 14 | * Send more than one message at a time (TBD, maybe will become a feature) 15 | * Provide a TUI 16 | * Anything fancy 17 | 18 | -------------------------------------------------------------------------------- /hl7-query/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hl7-query" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Kenton Hamaluik "] 6 | description = "A command-line tool for parsing and manipulating HL7 messages" 7 | 8 | [[bin]] 9 | name = "hq" 10 | path = "src/main.rs" 11 | 12 | [dependencies] 13 | chrono = "0.4.31" 14 | clap = { version = "4.4.14", features = ["derive", "cargo", "wrap_help"] } 15 | color-eyre = "0.6.2" 16 | hl7-parser = "0.1" 17 | nom = "7.1.3" 18 | rand = "0.8.5" 19 | termcolor = "1.4.0" 20 | syntect = { version = "5.1", default-features = false, features = ["default-fancy"] } 21 | serde = "1.0.195" 22 | serde_json = "1.0.111" 23 | strip-ansi-escapes = "0.2.0" 24 | 25 | [dev-dependencies] 26 | assert_cmd = "2" 27 | predicates = "3" 28 | 29 | -------------------------------------------------------------------------------- /hl7-send-receive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hl7-send-receive" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Kenton Hamaluik "] 6 | description = "Send and receive HL7v2 messages over MLLP" 7 | keywords = ["hl7", "mllp", "tcp", "cli"] 8 | categories = ["network-programming", "cli", "science"] 9 | 10 | [[bin]] 11 | name = "hs" 12 | path = "src/main.rs" 13 | 14 | [dependencies] 15 | bytes = "1.5.0" 16 | clap = { version = "4.4.14", features = ["derive", "cargo", "wrap_help"] } 17 | color-eyre = "0.6.2" 18 | futures = "0.3.30" 19 | hl7-mllp-codec = "0.4.0" 20 | strip-ansi-escapes = "0.2.0" 21 | termcolor = "1.4.1" 22 | tokio = { version = "1.35.1", features = ["full"] } 23 | tokio-util = "0.7.10" 24 | hl7-parser = "0.1" 25 | rand = "0.8.5" 26 | chrono = "0.4.31" 27 | -------------------------------------------------------------------------------- /hl7-query/README.md: -------------------------------------------------------------------------------- 1 | # HQ 2 | 3 | HQ is a an HL7 parser for the command line with some basic features to enable 4 | using and manipulating HL7 messages via a composable command-line workflow. 5 | 6 | ## Features 7 | 8 | - [X] Parse HL7 messages 9 | - [ ] Validate HL7 message structure 10 | - [X] Map field values to new values (ex: set `MSH.10` to "1234") 11 | - [X] Query field values (ex: "what is the value of `PID.5`?) 12 | - [X] Read from a file or stdin 13 | - [X] Map newlines to HL7 `\r` segment separators 14 | - [X] Print a (minimally) syntax-highlighted version of the message to stdout 15 | - [X] Print an easily greppable table of the message to stdout 16 | - [X] Print a JSON version of the message to stdout 17 | 18 | ## Non-Goals 19 | 20 | * Send or receive HL7 messages 21 | * Process more than one message at a time (TBD, maybe will become a feature) 22 | * Provide a TUI 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | # Allows you to run this workflow manually from the Actions tab 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | branches: 11 | - main 12 | merge_group: 13 | 14 | # ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel 15 | # and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency 16 | concurrency: 17 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | test: 22 | strategy: 23 | fail-fast: false 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | - name: Install Rust Stable 29 | uses: dtolnay/rust-toolchain@master 30 | with: 31 | toolchain: stable 32 | - name: Run tests 33 | run: cargo test --verbose --all-features 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HL7-Tools 2 | 3 | ## HQ 4 | 5 | HQ is a an HL7 parser for the command line with some basic features to enable 6 | using and manipulating HL7 messages via a composable command-line workflow. 7 | 8 | ```bash 9 | # Read sample_adt_a01.hl and before printing it, set MSH.10 (control ID) of the 10 | # message to `1234` 11 | hq -m MSH.10=1234 assets/sample_adt_a01.hl7 12 | ``` 13 | 14 | ```bash 15 | # Query PID.5 (patient name) from assets/sample_adt_a01.hl7 and print it 16 | hq -q PID.5 assets/sample_adt_a01.hl7 17 | ``` 18 | 19 | ```bash 20 | # Parse the message from stdin and print a JSON version to stdout 21 | cat assets/sample_adt_a01.hl7 | hq -o json 22 | ``` 23 | 24 | ## HS 25 | 26 | HS is a tool for sending and receiving HL7 messages over the MLLP protocol. 27 | 28 | ```bash 29 | # Listen on localhost port 10500 for 3 messages and print them as they come in, 30 | # returning error ACKs for each 31 | hs listen --message-count 3 --ack-mode error --bind localhost:10500 32 | ``` 33 | 34 | ```bash 35 | # Send a message to localhost port 10500 36 | cat assets/sample_adt_a01.hl7 | hs send localhost:10500 37 | ``` 38 | 39 | ```bash 40 | # Send a message, automatically generating the control id 41 | cat assets/sample_adt_a01.hl7 | hq -m 'MSH.10=' | hs send localhost:10500 42 | ``` 43 | -------------------------------------------------------------------------------- /assets/sample_adt_a01.hl7: -------------------------------------------------------------------------------- 1 | MSH|^~\&|AccMgr|1|||20050110045504||ADT^A01|599102|P|2.3|||AL 2 | EVN|A01|20050110045502||||| 3 | PID|1||10006579^^^1^MRN^1||DUCK^DONALD^D||19241010|M||1|111 DUCK ST^^FOWL^CA^999990000^^M|1|8885551212|8885551212|1|2||40007716^^^AccMgr^VN^1|123121234|||||||||||NO NK1|1|DUCK^HUEY|SO|3583 DUCK RD^^FOWL^CA^999990000|8885552222||Y|||||||||||||| 4 | PV1|1|I|PREOP^101^1^1^^^S|3|||37^DISNEY^WALT^^^^^^AccMgr^^^^CI|||01||||1|||37^DISNEY^WALT^^^^^^AccMgr^^^^CI|2|40007716^^^AccMgr^VN|4|||||||||||||||||||1||G|||20050110045253|||||| 5 | GT1|1|8291|DUCK^DONALD^D||111^DUCKST^^FOWL^CA^999990000|8885551212||19241010|M||1|123121234||||#Cartoon Ducks Inc|111^DUCK ST^^FOWL^CA^999990000|8885551212||PT| 6 | DG1|1|I9|71596^OSTEOARTHROS NOS-L/LEG ^I9|OSTEOARTHROS NOS-L/LEG ||A| 7 | IN1|1|MEDICARE|3|MEDICARE|||||||Cartoon Ducks Inc|19891001|||4|DUCK^DONALD^D|1|19241010|111^DUCK ST^^FOWL^CA^999990000|||||||||||||||||123121234A||||||PT|M|111 DUCK ST^^FOWL^CA^999990000|||||8291 8 | IN2|1||123121234|Cartoon Ducks Inc|||123121234A|||||||||||||||||||||||||||||||||||||||||||||||||||||||||8885551212 9 | IN1|2|NON-PRIMARY|9|MEDICAL MUTUAL CALIF.|PO BOX 94776^^HOLLYWOOD^CA^441414776||8003621279|PUBSUMB|||Cartoon Ducks Inc||||7|DUCK^DONALD^D|1|19241010|111 DUCK ST^^FOWL^CA^999990000|||||||||||||||||056269770||||||PT|M|111^DUCK ST^^FOWL^CA^999990000|||||8291 10 | IN2|2||123121234|Cartoon Ducks Inc||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||8885551212 11 | IN1|3|SELF PAY|1|SELF PAY|||||||||||5||1 12 | -------------------------------------------------------------------------------- /hl7-query/src/print/query.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::Cli; 2 | use color_eyre::eyre::{Context, Result}; 3 | use hl7_parser::ParsedMessageOwned; 4 | use termcolor::{Color, ColorSpec, WriteColor}; 5 | use crate::open_stdout; 6 | use std::io::Write; 7 | 8 | pub fn print_query_results(message: ParsedMessageOwned, cli: &Cli) -> Result<()> { 9 | let mut stdout = open_stdout(cli); 10 | for query in cli.query.iter() { 11 | let value = message.query_value(query).wrap_err_with(|| { 12 | format!( 13 | "Failed to query message for {:?} (query: {:?})", 14 | query, query 15 | ) 16 | })?; 17 | if let Some(value) = value { 18 | let value = message.separators.decode(value); 19 | 20 | let mut hl_special_char = ColorSpec::new(); 21 | let mut hl_value = ColorSpec::new(); 22 | hl_special_char.set_fg(Some(Color::Black)).set_intense(true); 23 | hl_value.set_fg(Some(Color::White)).set_intense(true); 24 | 25 | for c in value.chars() { 26 | if message.separators.is_special_char(c) { 27 | stdout 28 | .set_color(&hl_special_char) 29 | .wrap_err_with(|| "Failed to set stdout colour")?; 30 | } 31 | else { 32 | stdout 33 | .set_color(&hl_value) 34 | .wrap_err_with(|| "Failed to set stdout colour")?; 35 | } 36 | write!(stdout, "{c}").wrap_err_with(|| "Failed to write to stdout")?; 37 | } 38 | stdout.reset().wrap_err_with(|| "Failed to reset stdout colour")?; 39 | writeln!(stdout) 40 | .wrap_err_with(|| "Failed to write to stdout")?; 41 | } else { 42 | writeln!(stdout).wrap_err_with(|| "Failed to write to stdout")?; 43 | } 44 | } 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /hl7-query/tests/cli.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::cmd::Command; 2 | use predicates::prelude::*; 3 | 4 | #[test] 5 | fn file_doesnt_exist() { 6 | let mut cmd = Command::cargo_bin("hq").expect("binary exists"); 7 | 8 | cmd.arg("test/file/doesnt/exist"); 9 | cmd.assert() 10 | .failure() 11 | .stderr(predicate::str::contains("No such file or directory")); 12 | } 13 | 14 | #[test] 15 | fn parse_valid_file() { 16 | let mut cmd = Command::cargo_bin("hq").expect("binary exists"); 17 | 18 | cmd.arg("--colour").arg("never").arg(concat!( 19 | env!("CARGO_MANIFEST_DIR"), 20 | "/../assets/sample_adt_a01.hl7" 21 | )); 22 | cmd.assert().success().stdout(predicate::str::contains( 23 | r"MSH|^~\&|AccMgr|1|||20050110045504||ADT^A01|599102|P|2.3|||AL", 24 | )); 25 | } 26 | 27 | #[test] 28 | fn parse_valid_stdin() { 29 | let mut cmd = Command::cargo_bin("hq").expect("binary exists"); 30 | 31 | cmd.arg("--colour").arg("never"); 32 | cmd.pipe_stdin(concat!( 33 | env!("CARGO_MANIFEST_DIR"), 34 | "/../assets/sample_adt_a01.hl7" 35 | )) 36 | .expect("test file exists"); 37 | cmd.assert().success().stdout(predicate::str::contains( 38 | r"MSH|^~\&|AccMgr|1|||20050110045504||ADT^A01|599102|P|2.3|||AL", 39 | )); 40 | } 41 | 42 | #[test] 43 | fn should_not_parse_invalid_hl7() { 44 | let mut cmd = Command::cargo_bin("hq").expect("binary exists"); 45 | 46 | cmd.arg("--colour").arg("never"); 47 | cmd.write_stdin("Hello world"); 48 | cmd.assert() 49 | .failure() 50 | .stderr(predicate::str::contains("Failed to parse input as HL7 message")); 51 | } 52 | 53 | #[test] 54 | fn should_output_tabular_data() { 55 | let mut cmd = Command::cargo_bin("hq").expect("binary exists"); 56 | 57 | cmd.arg("--colour").arg("never").arg("-o").arg("table").arg(concat!( 58 | env!("CARGO_MANIFEST_DIR"), 59 | "/../assets/sample_adt_a01.hl7" 60 | )); 61 | cmd.assert().success().stdout(predicate::str::contains( 62 | "MSH.10\t599102", 63 | )); 64 | } 65 | 66 | #[test] 67 | fn should_print_help() { 68 | let mut cmd = Command::cargo_bin("hq").expect("binary exists"); 69 | 70 | cmd.arg("--help"); 71 | cmd.assert() 72 | .success() 73 | .stdout(predicate::str::contains(r"Usage: hq [OPTIONS] [INPUT]")); 74 | } 75 | -------------------------------------------------------------------------------- /hl7-query/src/map.rs: -------------------------------------------------------------------------------- 1 | use chrono::Local; 2 | use hl7_parser::LocationQuery; 3 | use std::str::FromStr; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct ValueMapFrom(pub LocationQuery); 7 | 8 | impl std::ops::Deref for ValueMapFrom { 9 | type Target = LocationQuery; 10 | 11 | fn deref(&self) -> &Self::Target { 12 | &self.0 13 | } 14 | } 15 | 16 | #[derive(Debug, Clone)] 17 | pub enum ValueMapTo { 18 | Auto, 19 | Now, 20 | Explicit(String), 21 | } 22 | 23 | #[derive(Debug, Clone)] 24 | pub struct ValueMap { 25 | pub from: ValueMapFrom, 26 | pub to: ValueMapTo, 27 | } 28 | 29 | impl FromStr for ValueMap { 30 | type Err = String; 31 | 32 | fn from_str(s: &str) -> Result { 33 | let mut parts = s.splitn(2, '='); 34 | let from = parts.next().ok_or_else(|| "missing from".to_string())?; 35 | let to = parts.next().ok_or_else(|| "missing to".to_string())?; 36 | let from = LocationQuery::from_str(from)?; 37 | let to = match to { 38 | "" => ValueMapTo::Auto, 39 | "" => ValueMapTo::Now, 40 | s => ValueMapTo::Explicit(s.to_string()), 41 | }; 42 | Ok(ValueMap { 43 | from: ValueMapFrom(from), 44 | to, 45 | }) 46 | } 47 | } 48 | 49 | impl ValueMapTo { 50 | pub fn reify(&self, location: &LocationQuery) -> String { 51 | match self { 52 | ValueMapTo::Auto => { 53 | // TODO: generate based on defintion of field 54 | if *location == LocationQuery::new_field_repeat("MSH", 7, 1).unwrap() { 55 | // message time 56 | let now = Local::now(); 57 | now.format("%Y%m%d%H%M%S").to_string() 58 | } else if *location == LocationQuery::new_field_repeat("MSH", 10, 1).unwrap() { 59 | // control ID 60 | use rand::distributions::{Alphanumeric, DistString}; 61 | Alphanumeric.sample_string(&mut rand::thread_rng(), 20) 62 | } else { 63 | use rand::distributions::{Alphanumeric, DistString}; 64 | Alphanumeric.sample_string(&mut rand::thread_rng(), 8) 65 | } 66 | } 67 | ValueMapTo::Now => { 68 | let now = Local::now(); 69 | now.format("%Y%m%d%H%M%S").to_string() 70 | } 71 | ValueMapTo::Explicit(s) => s.clone(), 72 | } 73 | } 74 | } 75 | 76 | impl std::fmt::Display for ValueMapFrom { 77 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 78 | write!(f, "{}", self.0) 79 | } 80 | } 81 | 82 | impl std::fmt::Display for ValueMapTo { 83 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 84 | match self { 85 | ValueMapTo::Auto => write!(f, ""), 86 | ValueMapTo::Now => write!(f, ""), 87 | ValueMapTo::Explicit(s) => write!(f, "{}", s), 88 | } 89 | } 90 | } 91 | 92 | impl std::fmt::Display for ValueMap { 93 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 94 | write!(f, "{from}={to}", from = self.from, to = self.to) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /hl7-query/src/print/hl7.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::Cli; 2 | use color_eyre::eyre::{Context, Result}; 3 | use hl7_parser::ParsedMessageOwned; 4 | use termcolor::{Color, ColorSpec, WriteColor}; 5 | use crate::open_stdout; 6 | use std::ops::Range; 7 | use std::io::Write; 8 | 9 | pub fn print_message_hl7(message: ParsedMessageOwned, cli: &Cli) -> Result<()> { 10 | let mut hl_segment = ColorSpec::new(); 11 | let mut hl_special_char = ColorSpec::new(); 12 | let mut hl_number = ColorSpec::new(); 13 | let mut hl_value = ColorSpec::new(); 14 | 15 | hl_segment.set_fg(Some(Color::Cyan)); 16 | hl_special_char.set_fg(Some(Color::Black)).set_intense(true); 17 | hl_number.set_fg(Some(Color::White)); 18 | hl_value.set_fg(Some(Color::White)).set_intense(true); 19 | 20 | let mut stdout = open_stdout(cli); 21 | 22 | // this is awful but it basically works 23 | let mut value_ranges: Vec> = Vec::new(); 24 | let mut number_ranges: Vec> = Vec::new(); 25 | let mut segment_identifier_ranges: Vec> = Vec::new(); 26 | for segments in message.segments.values() { 27 | for segment in segments.iter() { 28 | let segment_id_range = segment.range.start..segment.range.start + 3; 29 | segment_identifier_ranges.push(segment_id_range); 30 | for field in segment.fields.iter() { 31 | if field.repeats.is_empty() { 32 | value_ranges.push(field.range.clone()); 33 | if field.source(&message.source).parse::().is_ok() { 34 | number_ranges.push(field.range.clone()); 35 | } 36 | } 37 | for repeat in field.repeats.iter() { 38 | if repeat.components.is_empty() { 39 | value_ranges.push(repeat.range.clone()); 40 | if repeat.source(&message.source).parse::().is_ok() { 41 | number_ranges.push(repeat.range.clone()); 42 | } 43 | } 44 | for component in repeat.components.iter() { 45 | if component.sub_components.is_empty() { 46 | value_ranges.push(component.range.clone()); 47 | if component.source(&message.source).parse::().is_ok() { 48 | number_ranges.push(component.range.clone()); 49 | } 50 | } 51 | for sub_component in component.sub_components.iter() { 52 | value_ranges.push(sub_component.range.clone()); 53 | if sub_component.source(&message.source).parse::().is_ok() { 54 | number_ranges.push(sub_component.range.clone()); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | for (i, c) in message.source.chars().enumerate() { 64 | let mut hl = None; 65 | if segment_identifier_ranges.iter().any(|r| r.contains(&i)) { 66 | hl = Some(&hl_segment); 67 | } else if number_ranges.iter().any(|r| r.contains(&i)) { 68 | hl = Some(&hl_number); 69 | } else if value_ranges.iter().any(|r| r.contains(&i)) { 70 | hl = Some(&hl_value); 71 | } else if message.separators.is_special_char(c) { 72 | hl = Some(&hl_special_char); 73 | } 74 | 75 | if let Some(hl) = hl { 76 | stdout 77 | .set_color(hl) 78 | .wrap_err_with(|| "Failed to set stdout colour")?; 79 | } 80 | if c == '\r' { 81 | writeln!(stdout).wrap_err_with(|| "Failed to write new line to stdout")?; 82 | } else { 83 | write!(stdout, "{c}").wrap_err_with(|| "Failed to write character to stdout")?; 84 | } 85 | } 86 | stdout 87 | .reset() 88 | .wrap_err_with(|| "Failed to reset stdout colour")?; 89 | println!(); 90 | 91 | Ok(()) 92 | } 93 | 94 | -------------------------------------------------------------------------------- /hl7-send-receive/src/print.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::{Context, Result}; 2 | use hl7_parser::ParsedMessage; 3 | use std::io::Write; 4 | use std::ops::Range; 5 | use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; 6 | 7 | pub fn print_message_nohl(message: S) -> Result<()> { 8 | let message = message.to_string().replace('\r', "\n"); 9 | println!("{}", message); 10 | Ok(()) 11 | } 12 | 13 | pub fn print_message_hl(stdout: &mut StandardStream, message: ParsedMessage) -> Result<()> { 14 | let mut hl_segment = ColorSpec::new(); 15 | let mut hl_special_char = ColorSpec::new(); 16 | let mut hl_number = ColorSpec::new(); 17 | let mut hl_value = ColorSpec::new(); 18 | 19 | hl_segment.set_fg(Some(Color::Cyan)); 20 | hl_special_char.set_fg(Some(Color::Black)).set_intense(true); 21 | hl_number.set_fg(Some(Color::White)); 22 | hl_value.set_fg(Some(Color::White)).set_intense(true); 23 | 24 | // this is awful but it basically works 25 | let mut value_ranges: Vec> = Vec::new(); 26 | let mut number_ranges: Vec> = Vec::new(); 27 | let mut segment_identifier_ranges: Vec> = Vec::new(); 28 | for segments in message.segments.values() { 29 | for segment in segments.iter() { 30 | let segment_id_range = segment.range.start..segment.range.start + 3; 31 | segment_identifier_ranges.push(segment_id_range); 32 | for field in segment.fields.iter() { 33 | if field.repeats.is_empty() { 34 | value_ranges.push(field.range.clone()); 35 | if field.source(message.source).parse::().is_ok() { 36 | number_ranges.push(field.range.clone()); 37 | } 38 | } 39 | for repeat in field.repeats.iter() { 40 | if repeat.components.is_empty() { 41 | value_ranges.push(repeat.range.clone()); 42 | if repeat.source(message.source).parse::().is_ok() { 43 | number_ranges.push(repeat.range.clone()); 44 | } 45 | } 46 | for component in repeat.components.iter() { 47 | if component.sub_components.is_empty() { 48 | value_ranges.push(component.range.clone()); 49 | if component.source(message.source).parse::().is_ok() { 50 | number_ranges.push(component.range.clone()); 51 | } 52 | } 53 | for sub_component in component.sub_components.iter() { 54 | value_ranges.push(sub_component.range.clone()); 55 | if sub_component.source(message.source).parse::().is_ok() { 56 | number_ranges.push(sub_component.range.clone()); 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | for (i, c) in message.source.chars().enumerate() { 66 | let mut hl = None; 67 | if segment_identifier_ranges.iter().any(|r| r.contains(&i)) { 68 | hl = Some(&hl_segment); 69 | } else if number_ranges.iter().any(|r| r.contains(&i)) { 70 | hl = Some(&hl_number); 71 | } else if value_ranges.iter().any(|r| r.contains(&i)) { 72 | hl = Some(&hl_value); 73 | } else if message.separators.is_special_char(c) { 74 | hl = Some(&hl_special_char); 75 | } 76 | 77 | if let Some(hl) = hl { 78 | stdout 79 | .set_color(hl) 80 | .wrap_err_with(|| "Failed to set stdout colour")?; 81 | } 82 | if c == '\r' { 83 | writeln!(stdout).wrap_err_with(|| "Failed to write new line to stdout")?; 84 | } else { 85 | write!(stdout, "{c}").wrap_err_with(|| "Failed to write character to stdout")?; 86 | } 87 | } 88 | stdout 89 | .reset() 90 | .wrap_err_with(|| "Failed to reset stdout colour")?; 91 | println!(); 92 | 93 | Ok(()) 94 | } 95 | -------------------------------------------------------------------------------- /hl7-query/src/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::map::ValueMap; 2 | use clap::{ColorChoice, Parser, ValueEnum}; 3 | use hl7_parser::LocationQuery; 4 | use std::{path::PathBuf, str::FromStr}; 5 | 6 | #[derive(Parser, Debug)] 7 | #[command(author = clap::crate_authors!(), version, about, long_about = None, help_template = "\ 8 | {before-help}{name} {version} 9 | by {author-with-newline}{about-with-newline} 10 | {usage-heading} {usage} 11 | 12 | {all-args}{after-help} 13 | ")] 14 | #[command(propagate_version = true)] 15 | pub struct Cli { 16 | #[arg( 17 | short, 18 | long, 19 | value_parser = clap::value_parser!(ValueMap), 20 | )] 21 | /// Map HL7 fields to values (or auto-generate values when using the `` keyword) 22 | /// 23 | /// Format: `hl7_field=||value` 24 | /// 25 | /// Example: `MSH-10=` 26 | /// 27 | /// Example: `MSH-10=1234` 28 | /// 29 | /// The `hl7_field` is a location query, see https://docs.rs/hl7-parser/0.1.0/hl7_parser/struct.LocationQuery.html 30 | /// 31 | /// The `value` is a string value to use for the fields 32 | /// 33 | /// The `auto` keyword will attempt to generate an appropriate value for the field 34 | /// (for example, a date for a date field, a control ID for MSH-10, etc.) 35 | pub map: Vec, 36 | 37 | #[arg(short, long)] 38 | /// Extract fields from the HL7 message and print the Result 39 | /// 40 | /// Query the HL7 message (after any mappings have been applied) and print the result 41 | /// Multiple queries can be specified and will be reported on separate lines 42 | pub query: Vec, 43 | 44 | #[arg(short, long, default_value_t = false)] 45 | /// Don't correct newlines in the HL7 message 46 | /// 47 | /// By default, \r\n and \n will be converted to \r to separate segments 48 | pub no_correct_newlines: bool, 49 | 50 | #[arg(short, long, default_value_t = ColorChoice::Auto)] 51 | /// Colorize output 52 | pub colour: ColorChoice, 53 | 54 | #[arg(short, long, default_value_t = OutputMode::HL7)] 55 | /// How to output the HL7 message 56 | pub output: OutputMode, 57 | 58 | /// The input file to read an HL7 message from 59 | /// 60 | /// If not specified, the message will be read from stdin 61 | pub input: Option, 62 | } 63 | 64 | pub fn cli() -> Cli { 65 | Cli::parse() 66 | } 67 | 68 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] 69 | #[derive(ValueEnum)] 70 | pub enum OutputMode { 71 | #[default] 72 | /// Print the HL7 message as HL7 (the default) 73 | /// 74 | /// Note: this will change the newlines in the HL7 message to match the current platform 75 | /// 76 | /// Example: 77 | /// ```text 78 | /// MSH|^~\&|EPICADT|DH|LABADT|DH|201301011226||ADT^A01|HL7MSG00001|P|2.5 79 | /// EVN|A01|201301011223 80 | /// ``` 81 | HL7, 82 | /// Print the HL7 message as JSON 83 | /// 84 | /// Example: 85 | /// ```json 86 | /// { 87 | /// "MSH": { 88 | /// "1": "|", 89 | /// "2": "^~\\&", 90 | /// "3": "EPICADT", 91 | /// } 92 | /// } 93 | /// ``` 94 | Json, 95 | 96 | /// Print the HL7 message as a list of rows specifying the field name and value 97 | /// 98 | /// Example: 99 | /// 100 | /// ```text 101 | /// MSH.10 1234 102 | /// MSH.15 AL 103 | /// ``` 104 | Table, 105 | } 106 | 107 | impl std::fmt::Display for OutputMode { 108 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 109 | match self { 110 | OutputMode::HL7 => write!(f, "hl7"), 111 | OutputMode::Json => write!(f, "json"), 112 | OutputMode::Table => write!(f, "table"), 113 | } 114 | } 115 | } 116 | 117 | impl FromStr for OutputMode { 118 | type Err = String; 119 | 120 | fn from_str(s: &str) -> Result { 121 | match s.to_lowercase().as_str() { 122 | "hl7" => Ok(OutputMode::HL7), 123 | "json" => Ok(OutputMode::Json), 124 | "table" => Ok(OutputMode::Table), 125 | _ => Err(format!("invalid output mode: {}", s)), 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /hl7-send-receive/src/ack.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::AckMode; 2 | use chrono::Utc; 3 | use color_eyre::{eyre::Context, Result}; 4 | use hl7_parser::ParsedMessage; 5 | 6 | pub fn generate_ack(message: &str, ack_mode: AckMode) -> Result<(String, ParsedMessage)> { 7 | match ack_mode { 8 | AckMode::Success => { 9 | let message = 10 | ParsedMessage::parse(message, false).wrap_err_with(|| "Failed to parse message")?; 11 | Ok(( 12 | compose_ack(&message, true).wrap_err_with(|| "Failed to compose ACK")?, 13 | message, 14 | )) 15 | } 16 | AckMode::Error => { 17 | let message = 18 | ParsedMessage::parse(message, false).wrap_err_with(|| "Failed to parse message")?; 19 | Ok(( 20 | compose_ack(&message, false).wrap_err_with(|| "Failed to compose ACK")?, 21 | message, 22 | )) 23 | } 24 | AckMode::Ignore => Err(color_eyre::eyre::eyre!("ACK mode is set to Ignore")), 25 | } 26 | } 27 | 28 | fn compose_ack(message: &ParsedMessage, success: bool) -> Result { 29 | let accept_ack = message 30 | .query_value("MSH.15") 31 | .expect("valid query") 32 | .unwrap_or_default(); 33 | let application_ack = message 34 | .query_value("MSH.16") 35 | .expect("valid query") 36 | .unwrap_or_default(); 37 | 38 | let accept_ack: Option = 39 | AckRequest::from_str(accept_ack).wrap_err_with(|| "Failed to parse accept ACK")?; 40 | let application_ack: Option = AckRequest::from_str(application_ack) 41 | .wrap_err_with(|| "Failed to parse application ACK")?; 42 | 43 | let is_enhanced_mode = accept_ack.is_some() || application_ack.is_some(); 44 | let ack_level = if is_enhanced_mode { 'C' } else { 'A' }; 45 | 46 | let control_id = message 47 | .query_value("MSH.10") 48 | .expect("valid query") 49 | .unwrap_or_default(); 50 | 51 | let sapp = message 52 | .query_value("MSH.3") 53 | .expect("valid query") 54 | .unwrap_or_default(); 55 | let sfac = message 56 | .query_value("MSH.4") 57 | .expect("valid query") 58 | .unwrap_or_default(); 59 | let rapp = message 60 | .query_value("MSH.5") 61 | .expect("valid query") 62 | .unwrap_or_default(); 63 | let rfac = message 64 | .query_value("MSH.6") 65 | .expect("valid query") 66 | .unwrap_or_default(); 67 | let processing_id = message 68 | .query_value("MSH.11") 69 | .expect("valid query") 70 | .unwrap_or_default(); 71 | let version = message 72 | .query_value("MSH.12") 73 | .expect("valid query") 74 | .unwrap_or_default(); 75 | let trigger = message 76 | .query_value("MSH.9.2") 77 | .expect("valid query") 78 | .unwrap_or_default(); 79 | 80 | use rand::distributions::{Alphanumeric, DistString}; 81 | let new_control_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 20); 82 | 83 | let now = Utc::now(); 84 | let now = now.format("%Y%m%d%H%M%S").to_string(); 85 | 86 | let msh = format!( 87 | "MSH|^~\\&|{rapp}|{rfac}|{sapp}|{sfac}|{now}||ACK^{trigger}^ACK|{new_control_id}|{processing_id}|{version}", 88 | ); 89 | 90 | let msa = format!( 91 | "MSA|{ack_level}{success}|{control_id}|{error_message}", 92 | success = if success { 'A' } else { 'E' }, 93 | error_message = if success { 94 | "Message accepted" 95 | } else { 96 | "Message rejected" 97 | }, 98 | ); 99 | 100 | Ok(format!("{}\r{}", msh, msa)) 101 | } 102 | 103 | #[derive(Debug, Copy, Clone)] 104 | enum AckRequest { 105 | Always, 106 | Never, 107 | Success, 108 | Error, 109 | } 110 | 111 | impl AckRequest { 112 | fn from_str(s: &str) -> Result> { 113 | match s { 114 | "AL" => Ok(Some(AckRequest::Always)), 115 | "NE" => Ok(Some(AckRequest::Never)), 116 | "SU" => Ok(Some(AckRequest::Success)), 117 | "ER" => Ok(Some(AckRequest::Error)), 118 | "" => Ok(None), 119 | _ => Err(color_eyre::eyre::eyre!("Invalid ACK request")), 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /hl7-send-receive/src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::{ColorChoice, Parser, Subcommand, ValueEnum}; 2 | use std::{ 3 | net::{SocketAddr, ToSocketAddrs}, 4 | path::PathBuf, 5 | }; 6 | 7 | #[derive(Parser, Debug)] 8 | #[command(author = clap::crate_authors!(), version, about, long_about = None, help_template = "\ 9 | {before-help}{name} {version} 10 | by {author-with-newline}{about-with-newline} 11 | {usage-heading} {usage} 12 | 13 | {all-args}{after-help} 14 | ")] 15 | #[command(propagate_version = true)] 16 | pub struct Cli { 17 | #[arg(short, long, action = clap::ArgAction::Count)] 18 | /// Increase the level of verbosity 19 | /// 20 | /// Use -v to print log information, -vv to print debug information 21 | pub verbose: u8, 22 | 23 | #[arg(short, long, default_value_t = ColorChoice::Auto)] 24 | /// Colorize output 25 | pub colour: ColorChoice, 26 | 27 | #[arg(short, long, default_value_t = false)] 28 | /// Don't correct newlines in HL7 messages 29 | /// 30 | /// By default, \r\n and \n will be converted to \r to separate segments 31 | pub no_correct_newlines: bool, 32 | 33 | #[command(subcommand)] 34 | pub command: Command, 35 | } 36 | 37 | #[derive(Subcommand, Debug)] 38 | pub enum Command { 39 | /// Send an HL7 message to a destination via MLLP transport 40 | /// 41 | /// The HL7 message will be read from stdin or from a file 42 | Send { 43 | #[arg(short, long, default_value_t = 10.0)] 44 | /// The number of seconds to wait for an ACK response before timing out 45 | /// 46 | /// If set to 0, quit immediately after sending the message without 47 | /// waiting for a response 48 | /// 49 | /// If an ACK response is received before the wait time has elapsed, it 50 | /// will be written to stdout. 51 | wait_time: f64, 52 | 53 | #[arg(short('p'), long, default_value_t = false)] 54 | /// Don't parse the input message or ACK response 55 | /// 56 | /// By default, both the input message and ACK response (if any) will be 57 | /// parsed. If either message fails to parse as HL7, the program will 58 | /// exit with an error. 59 | no_parse: bool, 60 | 61 | #[arg(value_parser = parse_socket_addr)] 62 | /// The destination to send the HL7 message to in the form of : 63 | destination: SocketAddr, 64 | 65 | /// The input file to read an HL7 message from 66 | /// 67 | /// If not specified, the message will be read from stdin 68 | input: Option, 69 | }, 70 | 71 | /// Listen for HL7 messages via MLLP transport 72 | /// 73 | /// The received HL7 messages will be written to stdout 74 | Listen { 75 | #[arg(short, long)] 76 | /// The number of messages to receive before exiting 77 | /// 78 | /// If not specified, the server will run until killed 79 | message_count: Option, 80 | 81 | #[arg(short, long, default_value_t = AckMode::Success)] 82 | /// The mode to use for sending ACKs 83 | ack_mode: AckMode, 84 | 85 | #[arg(short, long, default_value = "127.0.0.1:2575", value_parser = parse_socket_addr)] 86 | /// The address to bind to in the form of : 87 | bind: SocketAddr, 88 | }, 89 | } 90 | 91 | pub fn cli() -> Cli { 92 | Cli::parse() 93 | } 94 | 95 | fn parse_socket_addr(s: &str) -> Result { 96 | s.to_socket_addrs() 97 | .map_err(|e| e.to_string())? 98 | .next() 99 | .ok_or_else(|| format!("{}: no addresses found", s)) 100 | } 101 | 102 | #[derive(Debug, ValueEnum, Default, Copy, Clone)] 103 | pub enum AckMode { 104 | /// Don't parse the received messages and don't send ACKs 105 | Ignore, 106 | #[default] 107 | /// Parse the received messages and send ACKs as if the message was processed successfully 108 | Success, 109 | /// Parse the received messages and send ACKs as if the message failed to process 110 | Error, 111 | } 112 | 113 | impl std::fmt::Display for AckMode { 114 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 115 | match self { 116 | AckMode::Ignore => write!(f, "ignore"), 117 | AckMode::Success => write!(f, "success"), 118 | AckMode::Error => write!(f, "error"), 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /hl7-query/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::IsTerminal; 2 | 3 | use cli::Cli; 4 | use color_eyre::eyre::{Context, Result}; 5 | use hl7_parser::ParsedMessageOwned; 6 | use termcolor::StandardStream; 7 | 8 | mod cli; 9 | mod map; 10 | mod print; 11 | pub use print::*; 12 | 13 | fn main() -> Result<()> { 14 | color_eyre::install()?; 15 | 16 | let cli = cli::cli(); 17 | 18 | let input = if let Some(input) = &cli.input { 19 | std::fs::read_to_string(input) 20 | .wrap_err_with(|| format!("Failed to read input file: {:?}", input.display()))? 21 | } else { 22 | use std::io::Read; 23 | let mut input = String::new(); 24 | std::io::stdin() 25 | .read_to_string(&mut input) 26 | .wrap_err_with(|| "Failed to read from stdin")?; 27 | input 28 | }; 29 | let input = strip_ansi_escapes::strip_str(input); 30 | 31 | let input = if cli.no_correct_newlines { 32 | input 33 | } else { 34 | input.replace("\r\n", "\r").replace('\n', "\r") 35 | }; 36 | let input = input.trim_end_matches('\r').to_string(); 37 | 38 | let message = ParsedMessageOwned::parse(input, false) 39 | .wrap_err_with(|| "Failed to parse input as HL7 message")?; 40 | 41 | let message = apply_maps(message, &cli).wrap_err_with(|| "Failed to apply value mappings")?; 42 | 43 | if cli.query.is_empty() { 44 | match cli.output { 45 | cli::OutputMode::HL7 => { 46 | print_message_hl7(message, &cli).wrap_err_with(|| "Failed to print message") 47 | } 48 | cli::OutputMode::Json => { 49 | print_message_json(message, &cli).wrap_err_with(|| "Failed to print queries") 50 | } 51 | cli::OutputMode::Table => { 52 | print_message_table(message, &cli).wrap_err_with(|| "Failed to print queries") 53 | } 54 | } 55 | } else { 56 | print_query_results(message, &cli).wrap_err_with(|| "Failed to print queries") 57 | } 58 | } 59 | 60 | fn apply_maps(mut message: ParsedMessageOwned, cli: &Cli) -> Result { 61 | 'maps: for map in cli.map.iter() { 62 | let query = &*map.from; 63 | 64 | if !message.has_segment(&query.segment) { 65 | continue 'maps; 66 | } 67 | 68 | let range = message.query(query).wrap_err_with(|| { 69 | format!( 70 | "Failed to query message for {:?} (map: {:?})", 71 | query, map.from 72 | ) 73 | })?; 74 | 75 | if let Some(range) = range { 76 | let value = map.to.reify(query); 77 | message.source.replace_range(range, &value); 78 | message = ParsedMessageOwned::parse(&message.source, false) 79 | .wrap_err_with(|| format!("Failed to re-parse message after applying map {map}"))?; 80 | } 81 | } 82 | Ok(message) 83 | } 84 | 85 | fn open_stdout(cli: &Cli) -> StandardStream { 86 | let colour = match cli.colour { 87 | clap::ColorChoice::Auto => termcolor::ColorChoice::Auto, 88 | clap::ColorChoice::Always => termcolor::ColorChoice::Always, 89 | clap::ColorChoice::Never => termcolor::ColorChoice::Never, 90 | }; 91 | let colour = if !std::io::stdout().is_terminal() { 92 | termcolor::ColorChoice::Never 93 | } else { 94 | colour 95 | }; 96 | StandardStream::stdout(colour) 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | use super::*; 102 | use crate::{cli::OutputMode, map::*}; 103 | use clap::ColorChoice; 104 | use hl7_parser::LocationQuery; 105 | 106 | #[test] 107 | fn can_map() { 108 | let input = "MSH|^~\\&|AccMgr|1|||20050110045504||ADT^A01|599102|P|2.3|||\rPID|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16\r"; 109 | let cli = Cli { 110 | map: vec![ 111 | ValueMap { 112 | from: ValueMapFrom(LocationQuery::new_field_repeat("MSH", 10, 1).unwrap()), 113 | to: ValueMapTo::Explicit("XXX".to_string()), 114 | }, 115 | ValueMap { 116 | from: ValueMapFrom(LocationQuery::new_field_repeat("MSH", 7, 1).unwrap()), 117 | to: ValueMapTo::Explicit("123".to_string()), 118 | }, 119 | ], 120 | no_correct_newlines: false, 121 | colour: ColorChoice::Never, 122 | input: None, 123 | output: OutputMode::HL7, 124 | query: vec![], 125 | }; 126 | let message = ParsedMessageOwned::parse(input, false).unwrap(); 127 | let message = apply_maps(message, &cli).unwrap(); 128 | assert_eq!( 129 | message.source, 130 | "MSH|^~\\&|AccMgr|1|||123||ADT^A01|XXX|P|2.3|||\rPID|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16\r" 131 | ); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /hl7-query/assets/JSON.sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | name: JSON 4 | scope: source.json 5 | version: 2 6 | 7 | file_extensions: 8 | - json 9 | 10 | hidden_file_extensions: [] 11 | 12 | first_line_match: |- 13 | (?xi: 14 | ^ \s* // .*? -\*- .*? \bjsonc?\b .*? -\*- # editorconfig 15 | ) 16 | 17 | contexts: 18 | 19 | prototype: 20 | - include: comments 21 | 22 | main: 23 | - include: value 24 | 25 | value: 26 | - include: constant 27 | - include: number 28 | - include: string 29 | - include: array 30 | - include: object 31 | 32 | array: 33 | - match: \[ 34 | scope: punctuation.section.sequence.begin.json 35 | push: 36 | - meta_scope: meta.sequence.json 37 | - match: \] 38 | scope: punctuation.section.sequence.end.json 39 | pop: 1 40 | - include: value 41 | - match: ',' 42 | scope: punctuation.separator.sequence.json 43 | - match: '[^\s\]]' 44 | scope: invalid.illegal.expected-sequence-separator.json 45 | 46 | comments: 47 | - include: block-comments 48 | - include: line-comments 49 | 50 | block-comments: 51 | # empty block comments 52 | - match: /\*\*+/ 53 | scope: comment.block.empty.json punctuation.definition.comment.json 54 | # documentation block comments 55 | - match: /\*\*+ 56 | scope: punctuation.definition.comment.begin.json 57 | push: block-comment-documentation-body 58 | # normal block comments 59 | - match: /\* 60 | scope: punctuation.definition.comment.begin.json 61 | push: block-comment-body 62 | 63 | block-comment-documentation-body: 64 | - meta_include_prototype: false 65 | - meta_scope: comment.block.documentation.json 66 | - match: \*+/ 67 | scope: punctuation.definition.comment.end.json 68 | pop: 1 69 | - match: ^\s*(\*)(?![*/]) 70 | captures: 71 | 1: punctuation.definition.comment.json 72 | 73 | block-comment-body: 74 | - meta_include_prototype: false 75 | - meta_scope: comment.block.json 76 | - match: \*/ 77 | scope: punctuation.definition.comment.end.json 78 | pop: 1 79 | - match: ^\s*(\*)(?![*/]) 80 | captures: 81 | 1: punctuation.definition.comment.json 82 | 83 | line-comments: 84 | - match: // 85 | scope: punctuation.definition.comment.json 86 | push: line-comment-body 87 | 88 | line-comment-body: 89 | - meta_include_prototype: false 90 | - meta_scope: comment.line.double-slash.json 91 | - match: $\n? 92 | pop: 1 93 | 94 | constant: 95 | - match: \b(?:false|true)\b 96 | scope: constant.language.boolean.json 97 | - match: \bnull\b 98 | scope: constant.language.null.json 99 | 100 | number: 101 | # handles integer and decimal numbers 102 | - match: (-?)((?:0|[1-9]\d*)(?:(?:(\.)\d+)(?:[eE][-+]?\d+)?|(?:[eE][-+]?\d+))) 103 | scope: meta.number.float.decimal.json 104 | captures: 105 | 1: keyword.operator.arithmetic.json 106 | 2: constant.numeric.value.json 107 | 3: punctuation.separator.decimal.json 108 | - match: (-?)(0|[1-9]\d*) 109 | scope: meta.number.integer.decimal.json 110 | captures: 111 | 1: keyword.operator.arithmetic.json 112 | 2: constant.numeric.value.json 113 | 114 | object: 115 | # a JSON object 116 | - match: \{ 117 | scope: punctuation.section.mapping.begin.json 118 | push: 119 | - meta_scope: meta.mapping.json 120 | - match: \} 121 | scope: punctuation.section.mapping.end.json 122 | pop: 1 123 | - match: \" 124 | scope: punctuation.definition.string.begin.json 125 | push: 126 | - clear_scopes: 1 127 | - meta_scope: meta.mapping.key.json string.quoted.double.json 128 | - meta_include_prototype: false 129 | - include: inside-string 130 | - match: ':' 131 | scope: punctuation.separator.key-value.json 132 | push: 133 | - match: ',|\s?(?=\})' 134 | scope: invalid.illegal.expected-mapping-value.json 135 | pop: 1 136 | - match: (?=\S) 137 | set: 138 | - clear_scopes: 1 139 | - meta_scope: meta.mapping.value.json 140 | - include: value 141 | - match: '' 142 | set: 143 | - match: ',' 144 | scope: punctuation.separator.sequence.json 145 | pop: 1 146 | - match: \s*(?=\}) 147 | pop: 1 148 | - match: \s(?!/[/*])(?=[^\s,])|[^\s,] 149 | scope: invalid.illegal.expected-mapping-separator.json 150 | pop: 1 151 | - match: '[^\s\}]' 152 | scope: invalid.illegal.expected-mapping-key.json 153 | 154 | string: 155 | - match: \" 156 | scope: punctuation.definition.string.begin.json 157 | push: inside-string 158 | 159 | inside-string: 160 | - meta_scope: string.quoted.double.json 161 | - meta_include_prototype: false 162 | - match: \" 163 | scope: punctuation.definition.string.end.json 164 | pop: 1 165 | - include: string-escape 166 | - match: \n 167 | scope: invalid.illegal.unclosed-string.json 168 | pop: 1 169 | 170 | string-escape: 171 | - match: |- 172 | (?x: # turn on extended mode 173 | \\ # a literal backslash 174 | (?: # ...followed by... 175 | ["\\/bfnrt] # one of these characters 176 | | # ...or... 177 | u # a u 178 | [0-9a-fA-F]{4} # and four hex digits 179 | ) 180 | ) 181 | scope: constant.character.escape.json 182 | - match: \\. 183 | scope: invalid.illegal.unrecognized-string-escape.json 184 | 185 | -------------------------------------------------------------------------------- /hl7-query/src/print/table.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::Cli; 2 | use color_eyre::eyre::{Context, Result}; 3 | use hl7_parser::ParsedMessageOwned; 4 | use std::io::Write; 5 | use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; 6 | 7 | fn write_path(stdout: &mut StandardStream, path: &[String]) -> Result<()> { 8 | let mut hl_segment = ColorSpec::new(); 9 | let mut hl_special_char = ColorSpec::new(); 10 | let mut hl_number = ColorSpec::new(); 11 | let mut hl_value = ColorSpec::new(); 12 | 13 | hl_segment.set_fg(Some(Color::Cyan)); 14 | hl_special_char.set_fg(Some(Color::Black)).set_intense(true); 15 | hl_number.set_fg(Some(Color::Yellow)); 16 | hl_value.set_fg(Some(Color::White)).set_intense(true); 17 | 18 | for (i, segment) in path.iter().enumerate() { 19 | if i > 0 { 20 | stdout 21 | .set_color(&hl_special_char) 22 | .wrap_err_with(|| "Failed to set stdout colour")?; 23 | write!(stdout, ".").wrap_err_with(|| "Failed to write to stdout")?; 24 | } 25 | if i == 0 { 26 | stdout 27 | .set_color(&hl_segment) 28 | .wrap_err_with(|| "Failed to set stdout colour")?; 29 | } else { 30 | stdout 31 | .set_color(&hl_number) 32 | .wrap_err_with(|| "Failed to set stdout colour")?; 33 | } 34 | write!(stdout, "{segment}").wrap_err_with(|| "Failed to write to stdout")?; 35 | } 36 | stdout 37 | .reset() 38 | .wrap_err_with(|| "Failed to reset stdout colour")?; 39 | Ok(()) 40 | } 41 | 42 | fn write_value(stdout: &mut StandardStream, value: &str) -> Result<()> { 43 | let mut hl_value = ColorSpec::new(); 44 | hl_value.set_fg(Some(Color::White)).set_intense(true); 45 | 46 | stdout 47 | .set_color(&hl_value) 48 | .wrap_err_with(|| "Failed to set stdout colour")?; 49 | write!(stdout, "{value}").wrap_err_with(|| "Failed to write to stdout")?; 50 | stdout 51 | .reset() 52 | .wrap_err_with(|| "Failed to reset stdout colour")?; 53 | Ok(()) 54 | } 55 | 56 | fn write_path_value(stdout: &mut StandardStream, path: &[String], value: &str) -> Result<()> { 57 | write_path(stdout, path)?; 58 | write!(stdout, "\t")?; 59 | write_value(stdout, value)?; 60 | writeln!(stdout)?; 61 | Ok(()) 62 | } 63 | 64 | pub fn print_message_table(message: ParsedMessageOwned, cli: &Cli) -> Result<()> { 65 | let mut stdout = crate::open_stdout(cli); 66 | 67 | let mut current_path: Vec = Vec::new(); 68 | for (segment_name, segments) in message.segments.iter() { 69 | for (segment_i, segment) in segments.iter().enumerate() { 70 | let segment_name = if segments.len() > 1 { 71 | format!("{}[{}]", segment_name, segment_i) 72 | } else { 73 | segment_name.to_string() 74 | }; 75 | current_path.push(segment_name); 76 | for (field_i, field) in segment.fields.iter().enumerate() { 77 | let field_name = format!("{}", field_i + 1); 78 | current_path.push(field_name); 79 | if field.repeats.is_empty() { 80 | let value = field.source(&message.source); 81 | if !value.is_empty() { 82 | write_path_value(&mut stdout, ¤t_path, value)?; 83 | } 84 | } else { 85 | for (repeat_i, repeat) in field.repeats.iter().enumerate() { 86 | if field.repeats.len() > 1 { 87 | current_path.push(format!("[{}]", repeat_i + 1)); 88 | } 89 | if repeat.components.is_empty() || repeat.components.len() == 1 { 90 | let value = repeat.source(&message.source); 91 | if !value.is_empty() { 92 | write_path_value(&mut stdout, ¤t_path, value)?; 93 | } 94 | } else { 95 | for (component_i, component) in repeat.components.iter().enumerate() { 96 | current_path.push(format!("{}", component_i + 1)); 97 | if component.sub_components.is_empty() 98 | || component.sub_components.len() == 1 99 | { 100 | let value = component.source(&message.source); 101 | if !value.is_empty() { 102 | write_path_value(&mut stdout, ¤t_path, value)?; 103 | } 104 | } else { 105 | for (sub_component_i, sub_component) in 106 | component.sub_components.iter().enumerate() 107 | { 108 | current_path.push(format!("{}", sub_component_i + 1)); 109 | let value = sub_component.source(&message.source); 110 | if !value.is_empty() { 111 | write_path_value(&mut stdout, ¤t_path, value)?; 112 | } 113 | current_path.pop(); 114 | } 115 | } 116 | current_path.pop(); 117 | } 118 | } 119 | if field.repeats.len() > 1 { 120 | current_path.pop(); 121 | } 122 | } 123 | } 124 | current_path.pop(); 125 | } 126 | current_path.pop(); 127 | } 128 | } 129 | 130 | Ok(()) 131 | } 132 | -------------------------------------------------------------------------------- /hl7-query/src/print/json.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::Cli; 2 | use clap::ColorChoice; 3 | use color_eyre::eyre::{Context, Result}; 4 | use hl7_parser::ParsedMessageOwned; 5 | use serde_json::{Map, Value}; 6 | use std::io::Write; 7 | use syntect::{ 8 | highlighting::{self, Style, ThemeSet}, 9 | parsing::{SyntaxDefinition, SyntaxSetBuilder}, 10 | util::LinesWithEndings, 11 | }; 12 | use termcolor::{Color, ColorSpec, WriteColor}; 13 | 14 | const JSON_SYNTAX: &str = include_str!("../../assets/JSON.sublime-syntax"); 15 | const THEME: &[u8] = include_bytes!("../../assets/ansi.tmTheme"); 16 | 17 | fn sub_components_to_json( 18 | sub_components: &[hl7_parser::SubComponent], 19 | source: &str, 20 | ) -> Option { 21 | if sub_components.len() == 1 { 22 | let value = sub_components[0].source(source); 23 | if value.is_empty() { 24 | None 25 | } else { 26 | Some(Value::String(value.to_string())) 27 | } 28 | } else { 29 | Some(Value::Object( 30 | sub_components 31 | .iter() 32 | .enumerate() 33 | .filter_map(|(i, sub_component)| { 34 | let value = sub_component.source(source); 35 | if value.is_empty() { 36 | None 37 | } else { 38 | Some((format!("{}", i + 1), Value::String(value.to_string()))) 39 | } 40 | }) 41 | .collect(), 42 | )) 43 | } 44 | } 45 | 46 | fn components_to_json(components: &[hl7_parser::Component], source: &str) -> Option { 47 | if components.len() == 1 { 48 | sub_components_to_json(&components[0].sub_components, source) 49 | } else { 50 | Some(Value::Object( 51 | components 52 | .iter() 53 | .enumerate() 54 | .filter_map(|(i, component)| { 55 | sub_components_to_json(&component.sub_components, source) 56 | .map(|value| (format!("{}", i + 1), value)) 57 | }) 58 | .collect(), 59 | )) 60 | } 61 | } 62 | 63 | fn repeats_to_json(repeats: &[hl7_parser::Repeat], source: &str) -> Option { 64 | if repeats.len() == 1 { 65 | components_to_json(&repeats[0].components, source) 66 | } else { 67 | Some(Value::Object( 68 | repeats 69 | .iter() 70 | .enumerate() 71 | .filter_map(|(i, repeat)| { 72 | components_to_json(&repeat.components, source) 73 | .map(|value| (format!("{}", i + 1), value)) 74 | }) 75 | .collect(), 76 | )) 77 | } 78 | } 79 | 80 | fn fields_to_json(fields: &[hl7_parser::Field], source: &str) -> Option { 81 | if fields.len() == 1 { 82 | repeats_to_json(&fields[0].repeats, source) 83 | } else { 84 | Some(Value::Object( 85 | fields 86 | .iter() 87 | .enumerate() 88 | .filter_map(|(i, field)| { 89 | repeats_to_json(&field.repeats, source) 90 | .map(|value| (format!("{}", i + 1), value)) 91 | }) 92 | .collect(), 93 | )) 94 | } 95 | } 96 | 97 | fn segments_to_json(segments: &[hl7_parser::Segment], source: &str) -> Value { 98 | if segments.len() == 1 { 99 | fields_to_json(&segments[0].fields, source).unwrap_or(Value::Null) 100 | } else { 101 | Value::Object( 102 | segments 103 | .iter() 104 | .enumerate() 105 | .filter_map(|(i, segment)| { 106 | fields_to_json(&segment.fields, source) 107 | .map(|value| (format!("{}", i + 1), value)) 108 | }) 109 | .collect(), 110 | ) 111 | } 112 | } 113 | 114 | fn message_to_json(message: &ParsedMessageOwned) -> Value { 115 | let tree: Map = message 116 | .segments 117 | .iter() 118 | .map(|(segment_name, segments)| { 119 | ( 120 | segment_name.to_string(), 121 | segments_to_json(segments, &message.source), 122 | ) 123 | }) 124 | .collect(); 125 | 126 | Value::Object(tree) 127 | } 128 | 129 | pub fn print_message_json(message: ParsedMessageOwned, cli: &Cli) -> Result<()> { 130 | let json = message_to_json(&message); 131 | let json = serde_json::to_string_pretty(&json).wrap_err_with(|| "Can't serialize JSON")?; 132 | 133 | if cli.colour == ColorChoice::Never { 134 | print!("{json}"); 135 | } else { 136 | // TODO: Do this at build time and figure out why my attempts segfault 137 | let mut theme = std::io::Cursor::new(THEME); 138 | let theme = ThemeSet::load_from_reader(&mut theme).wrap_err_with(|| "Can't load themes")?; 139 | let syntax = SyntaxDefinition::load_from_str(JSON_SYNTAX, false, None) 140 | .wrap_err_with(|| "Can't load syntax")?; 141 | let mut syntaxes = SyntaxSetBuilder::new(); 142 | syntaxes.add(syntax); 143 | let syntaxes = syntaxes.build(); 144 | 145 | let syntax = syntaxes 146 | .find_syntax_by_name("JSON") 147 | .expect("Can find JSON syntax"); 148 | let mut highlighter = syntect::easy::HighlightLines::new(syntax, &theme); 149 | 150 | let mut stdout = crate::open_stdout(cli); 151 | 152 | for line in LinesWithEndings::from(&json) { 153 | let spans: Vec<(Style, &str)> = highlighter 154 | .highlight_line(line, &syntaxes) 155 | .wrap_err_with(|| "Can't highlight line")?; 156 | 157 | for (style, text) in spans.into_iter() { 158 | let fg = to_ansi_color(style.foreground); 159 | let mut spec = ColorSpec::new(); 160 | spec.set_fg(fg); 161 | 162 | stdout 163 | .set_color(&spec) 164 | .wrap_err_with(|| "Can't set color")?; 165 | write!(stdout, "{text}").wrap_err_with(|| "Can't write to buffer")?; 166 | } 167 | } 168 | 169 | stdout.reset().wrap_err_with(|| "Can't reset stdout")?; 170 | } 171 | println!(); 172 | 173 | Ok(()) 174 | } 175 | 176 | /// source: https://github.com/sharkdp/bat/blob/cd81c7fa6bf0d061f455f67aae72dc5537f7851d/src/terminal.rs#L6 177 | fn to_ansi_color(color: highlighting::Color) -> Option { 178 | if color.a == 0 { 179 | Some(match color.r { 180 | 0x00 => Color::Black, 181 | 0x01 => Color::Red, 182 | 0x02 => Color::Green, 183 | 0x03 => Color::Yellow, 184 | 0x04 => Color::Blue, 185 | 0x05 => Color::Magenta, 186 | 0x06 => Color::Cyan, 187 | 0x07 => Color::White, 188 | n => Color::Ansi256(n), 189 | }) 190 | } else if color.a == 1 { 191 | None 192 | } else { 193 | Some(Color::Rgb(color.r, color.g, color.b)) 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /hl7-send-receive/src/main.rs: -------------------------------------------------------------------------------- 1 | use bytes::BytesMut; 2 | use color_eyre::eyre::{Context, Result}; 3 | use futures::{SinkExt, StreamExt}; 4 | use hl7_mllp_codec::MllpCodec; 5 | use std::fmt::Display; 6 | use std::io::{IsTerminal, Write}; 7 | use std::time::Duration; 8 | use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; 9 | use tokio::net::{TcpListener, TcpStream}; 10 | use tokio::time::timeout; 11 | use tokio_util::codec::Framed; 12 | 13 | mod ack; 14 | mod cli; 15 | mod print; 16 | 17 | fn log(s: S, level: u8, stderr: &mut StandardStream) -> Result<()> { 18 | let mut colour = ColorSpec::new(); 19 | let colour = match level { 20 | 1 => colour.set_fg(Some(Color::Cyan)), 21 | 2 => colour.set_fg(Some(Color::Magenta)), 22 | 3 => colour.set_fg(Some(Color::White)).set_dimmed(true), 23 | _ => colour.set_fg(Some(Color::White)), 24 | }; 25 | 26 | stderr 27 | .set_color(colour) 28 | .wrap_err_with(|| "Failed to set terminal colour")?; 29 | writeln!(stderr, "{}", s).wrap_err_with(|| "Failed to write to stderr")?; 30 | stderr 31 | .reset() 32 | .wrap_err_with(|| "Failed to reset terminal colour")?; 33 | 34 | Ok(()) 35 | } 36 | 37 | macro_rules! info { 38 | ($stdout:ident, $loglevel:ident, $($arg:tt)*) => { 39 | if $loglevel >= 1 { 40 | log(format!($($arg)*), 1, &mut $stdout).wrap_err_with(|| "Failed to log info message")?; 41 | } 42 | }; 43 | } 44 | 45 | macro_rules! debug { 46 | ($stdout:ident, $loglevel:ident, $($arg:tt)*) => { 47 | if $loglevel >= 2 { 48 | log(format!($($arg)*), 2, &mut $stdout).wrap_err_with(|| "Failed to log debug message")?; 49 | } 50 | }; 51 | } 52 | 53 | macro_rules! trace { 54 | ($stdout:ident, $loglevel:ident, $($arg:tt)*) => { 55 | if $loglevel >= 3 { 56 | log(format!($($arg)*), 3, &mut $stdout).wrap_err_with(|| "Failed to log debug message")?; 57 | } 58 | }; 59 | } 60 | 61 | #[tokio::main] 62 | async fn main() -> Result<()> { 63 | color_eyre::install()?; 64 | 65 | let cli = cli::cli(); 66 | let loglevel = cli.verbose; 67 | let mut stdout = open_stdout(&cli); 68 | let mut stderr = open_stderr(&cli); 69 | 70 | match cli.command { 71 | cli::Command::Send { 72 | wait_time, 73 | no_parse, 74 | destination, 75 | input, 76 | } => { 77 | let input = if let Some(input) = &input { 78 | info!(stderr, loglevel, "Reading input from file: {:?}", input); 79 | std::fs::read_to_string(input) 80 | .wrap_err_with(|| format!("Failed to read input file: {:?}", input.display()))? 81 | } else { 82 | info!(stderr, loglevel, "Reading input from stdin"); 83 | use std::io::Read; 84 | let mut input = String::new(); 85 | std::io::stdin() 86 | .read_to_string(&mut input) 87 | .wrap_err_with(|| "Failed to read from stdin")?; 88 | input 89 | }; 90 | let input = strip_ansi_escapes::strip_str(input); 91 | trace!(stderr, loglevel, "Read input:\n{:?}", input); 92 | 93 | let input = if cli.no_correct_newlines { 94 | trace!(stderr, loglevel, "Not correcting newlines"); 95 | input 96 | } else { 97 | trace!(stderr, loglevel, "Correcting newlines"); 98 | correct_newlines(&input) 99 | }; 100 | let input = input.trim_end_matches('\r').to_string(); 101 | trace!(stderr, loglevel, "Corrected input:\n{:?}", input); 102 | 103 | if !no_parse { 104 | debug!(stderr, loglevel, "Parsing input"); 105 | hl7_parser::ParsedMessage::parse(&input, false) 106 | .wrap_err_with(|| "Failed to parse input message")?; 107 | } 108 | 109 | debug!( 110 | stdout, 111 | loglevel, "Connecting to HL7 destination: {}", destination 112 | ); 113 | let stream = TcpStream::connect(destination) 114 | .await 115 | .with_context(|| "Failed to connect to HL7 destination!")?; 116 | let mut transport = Framed::new(stream, MllpCodec::new()); 117 | info!( 118 | stdout, 119 | loglevel, "Connected to HL7 destination: {}", destination 120 | ); 121 | 122 | debug!(stderr, loglevel, "Sending message"); 123 | transport 124 | .send(BytesMut::from(input.as_bytes())) 125 | .await 126 | .wrap_err_with(|| "Failed to send message")?; 127 | info!(stderr, loglevel, "Sent message"); 128 | 129 | debug!(stderr, loglevel, "Waiting for response"); 130 | let message_response = timeout(Duration::from_secs_f64(wait_time), transport.next()) 131 | .await 132 | .ok() 133 | .flatten(); 134 | if let Some(received) = message_response { 135 | info!(stderr, loglevel, "Received response"); 136 | let received = received.wrap_err_with(|| "Failed to receive response")?; 137 | trace!(stderr, loglevel, "Response bytes:\n{:?}", received); 138 | let message = String::from_utf8_lossy(&received); 139 | if no_parse { 140 | print::print_message_nohl(message) 141 | .wrap_err_with(|| "Failed to print message")?; 142 | } else { 143 | let message = hl7_parser::ParsedMessage::parse(&message, false) 144 | .wrap_err_with(|| "Failed to parse message")?; 145 | print::print_message_hl(&mut stdout, message) 146 | .wrap_err_with(|| "Failed to print message")?; 147 | } 148 | } else { 149 | info!(stderr, loglevel, "No response received"); 150 | } 151 | } 152 | cli::Command::Listen { 153 | message_count, 154 | ack_mode, 155 | bind, 156 | } => { 157 | debug!(stderr, loglevel, "Starting to listen on {bind}"); 158 | let listener = TcpListener::bind(&bind) 159 | .await 160 | .wrap_err_with(|| format!("Failed to start listening on {bind}"))?; 161 | info!(stderr, loglevel, "Listening on {bind}"); 162 | 163 | let mut received_messages: usize = 0; 164 | 'accept: loop { 165 | let Ok((stream, remote)) = listener.accept().await else { 166 | info!(stderr, loglevel, "Failed to accept connection"); 167 | continue 'accept; 168 | }; 169 | trace!(stderr, loglevel, "Remote connection: {:?}", remote); 170 | 171 | let mut transport = Framed::new(stream, MllpCodec::new()); 172 | 'messages: while let Some(result) = transport.next().await { 173 | trace!(stderr, loglevel, "Received message"); 174 | trace!(stderr, loglevel, "Message bytes:\n{:?}", result); 175 | let Ok(message) = result else { 176 | break 'messages; 177 | }; 178 | let message = String::from_utf8_lossy(&message); 179 | let message = if cli.no_correct_newlines { 180 | trace!(stderr, loglevel, "Not correcting newlines"); 181 | message.to_string() 182 | } else { 183 | trace!(stderr, loglevel, "Correcting newlines"); 184 | correct_newlines(message.as_ref()) 185 | }; 186 | 187 | let ack = match ack_mode { 188 | cli::AckMode::Success => { 189 | debug!(stderr, loglevel, "Generating success ACK"); 190 | Some( 191 | ack::generate_ack(&message, cli::AckMode::Success) 192 | .wrap_err_with(|| "Failed to generate ACK")?, 193 | ) 194 | } 195 | cli::AckMode::Error => { 196 | debug!(stderr, loglevel, "Generating error ACK"); 197 | Some( 198 | ack::generate_ack(&message, cli::AckMode::Error) 199 | .wrap_err_with(|| "Failed to generate ACK")?, 200 | ) 201 | } 202 | cli::AckMode::Ignore => { 203 | debug!(stderr, loglevel, "Not generating ACK"); 204 | None 205 | } 206 | }; 207 | let parsed_message = if let Some((ack, parsed_message)) = ack { 208 | info!(stderr, loglevel, "Sending ACK"); 209 | debug!(stderr, loglevel, "ACK:\n{}", ack); 210 | transport 211 | .send(BytesMut::from(ack.as_bytes())) 212 | .await 213 | .wrap_err_with(|| "Failed to send ACK")?; 214 | Some(parsed_message) 215 | } else { 216 | None 217 | }; 218 | if let Some(parsed_message) = parsed_message { 219 | print::print_message_hl(&mut stdout, parsed_message) 220 | .wrap_err_with(|| "Failed to print message")?; 221 | } else { 222 | print::print_message_nohl(&message) 223 | .wrap_err_with(|| "Failed to print message")?; 224 | } 225 | 226 | received_messages += 1; 227 | trace!( 228 | stderr, 229 | loglevel, 230 | "Received {} messages so far", 231 | received_messages 232 | ); 233 | if let Some(message_count) = message_count { 234 | if received_messages >= message_count { 235 | info!( 236 | stderr, 237 | loglevel, "Received {} messages, quitting", received_messages 238 | ); 239 | break 'accept; 240 | } 241 | } 242 | } 243 | } 244 | } 245 | } 246 | 247 | Ok(()) 248 | } 249 | 250 | fn open_stdout(cli: &cli::Cli) -> StandardStream { 251 | let colour = match cli.colour { 252 | clap::ColorChoice::Auto => termcolor::ColorChoice::Auto, 253 | clap::ColorChoice::Always => termcolor::ColorChoice::Always, 254 | clap::ColorChoice::Never => termcolor::ColorChoice::Never, 255 | }; 256 | let colour = if !std::io::stdout().is_terminal() { 257 | termcolor::ColorChoice::Never 258 | } else { 259 | colour 260 | }; 261 | StandardStream::stdout(colour) 262 | } 263 | 264 | fn open_stderr(cli: &cli::Cli) -> StandardStream { 265 | let colour = match cli.colour { 266 | clap::ColorChoice::Auto => termcolor::ColorChoice::Auto, 267 | clap::ColorChoice::Always => termcolor::ColorChoice::Always, 268 | clap::ColorChoice::Never => termcolor::ColorChoice::Never, 269 | }; 270 | let colour = if !std::io::stderr().is_terminal() { 271 | termcolor::ColorChoice::Never 272 | } else { 273 | colour 274 | }; 275 | StandardStream::stderr(colour) 276 | } 277 | 278 | fn correct_newlines(message: &str) -> String { 279 | message.replace("\r\n", "\r").replace('\n', "\r") 280 | } 281 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /hl7-query/assets/ansi.tmTheme: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | author 11 | Template: Chris Kempson, Scheme: Mitchell Kember 12 | name 13 | ANSI 14 | colorSpaceName 15 | sRGB 16 | settings 17 | 18 | 19 | settings 20 | 21 | background 22 | #00000001 23 | foreground 24 | #00000001 25 | 29 | gutter 30 | #00000001 31 | gutterForeground 32 | #00000001 33 | 34 | 35 | 36 | name 37 | Comments 38 | scope 39 | comment, punctuation.definition.comment 40 | settings 41 | 42 | foreground 43 | #02000000 44 | 45 | 46 | 47 | name 48 | Keywords 49 | scope 50 | keyword 51 | settings 52 | 53 | foreground 54 | #05000000 55 | 56 | 57 | 58 | name 59 | Functions 60 | scope 61 | entity.name.function, meta.require, support.function.any-method 62 | settings 63 | 64 | foreground 65 | #04000000 66 | 67 | 68 | 69 | name 70 | Labels 71 | scope 72 | entity.name.label 73 | settings 74 | 75 | foreground 76 | #06000000 77 | 78 | 79 | 80 | name 81 | Classes 82 | scope 83 | support.class, entity.name.class, entity.name.type.class 84 | settings 85 | 86 | foreground 87 | #03000000 88 | 89 | 90 | 91 | name 92 | Methods 93 | scope 94 | keyword.other.special-method 95 | settings 96 | 97 | foreground 98 | #04000000 99 | 100 | 101 | 102 | name 103 | Storage 104 | scope 105 | storage 106 | settings 107 | 108 | foreground 109 | #05000000 110 | 111 | 112 | 113 | name 114 | Support 115 | scope 116 | support.function 117 | settings 118 | 119 | foreground 120 | #06000000 121 | 122 | 123 | 124 | name 125 | Strings, Inherited Class 126 | scope 127 | string, constant.other.symbol, entity.other.inherited-class 128 | settings 129 | 130 | foreground 131 | #02000000 132 | 133 | 134 | 135 | name 136 | Integers 137 | scope 138 | constant.numeric 139 | settings 140 | 141 | foreground 142 | #03000000 143 | 144 | 145 | 146 | name 147 | Floats 148 | scope 149 | none 150 | settings 151 | 152 | foreground 153 | #03000000 154 | 155 | 156 | 157 | name 158 | Boolean 159 | scope 160 | none 161 | settings 162 | 163 | foreground 164 | #03000000 165 | 166 | 167 | 168 | name 169 | Constants 170 | scope 171 | constant 172 | settings 173 | 174 | foreground 175 | #03000000 176 | 177 | 178 | 179 | name 180 | Tags 181 | scope 182 | entity.name.tag 183 | settings 184 | 185 | foreground 186 | #01000000 187 | 188 | 189 | 190 | name 191 | Attributes 192 | scope 193 | entity.other.attribute-name 194 | settings 195 | 196 | foreground 197 | #03000000 198 | 199 | 200 | 201 | name 202 | Attribute IDs 203 | scope 204 | entity.other.attribute-name.id, punctuation.definition.entity 205 | settings 206 | 207 | foreground 208 | #04000000 209 | 210 | 211 | 212 | name 213 | Selector 214 | scope 215 | meta.selector 216 | settings 217 | 218 | foreground 219 | #05000000 220 | 221 | 222 | 223 | name 224 | Values 225 | scope 226 | none 227 | settings 228 | 229 | foreground 230 | #03000000 231 | 232 | 233 | 234 | name 235 | Headings 236 | scope 237 | markup.heading punctuation.definition.heading, entity.name.section 238 | settings 239 | 240 | fontStyle 241 | 242 | foreground 243 | #04000000 244 | 245 | 246 | 247 | name 248 | Units 249 | scope 250 | keyword.other.unit 251 | settings 252 | 253 | foreground 254 | #03000000 255 | 256 | 257 | 258 | name 259 | Bold 260 | scope 261 | markup.bold, punctuation.definition.bold 262 | settings 263 | 264 | fontStyle 265 | bold 266 | foreground 267 | #03000000 268 | 269 | 270 | 271 | name 272 | Italic 273 | scope 274 | markup.italic, punctuation.definition.italic 275 | settings 276 | 277 | fontStyle 278 | italic 279 | foreground 280 | #05000000 281 | 282 | 283 | 284 | name 285 | Code 286 | scope 287 | markup.raw.inline 288 | settings 289 | 290 | foreground 291 | #02000000 292 | 293 | 294 | 295 | name 296 | Link Text 297 | scope 298 | string.other.link, punctuation.definition.string.end.markdown, punctuation.definition.string.begin.markdown 299 | settings 300 | 301 | foreground 302 | #01000000 303 | 304 | 305 | 306 | name 307 | Link Url 308 | scope 309 | meta.link 310 | settings 311 | 312 | foreground 313 | #03000000 314 | 315 | 316 | 317 | name 318 | Quotes 319 | scope 320 | markup.quote 321 | settings 322 | 323 | foreground 324 | #03000000 325 | 326 | 327 | 328 | name 329 | Inserted 330 | scope 331 | markup.inserted 332 | settings 333 | 334 | foreground 335 | #02000000 336 | 337 | 338 | 339 | name 340 | Deleted 341 | scope 342 | markup.deleted 343 | settings 344 | 345 | foreground 346 | #01000000 347 | 348 | 349 | 350 | name 351 | Changed 352 | scope 353 | markup.changed 354 | settings 355 | 356 | foreground 357 | #05000000 358 | 359 | 360 | 361 | name 362 | Colors 363 | scope 364 | constant.other.color 365 | settings 366 | 367 | foreground 368 | #06000000 369 | 370 | 371 | 372 | name 373 | Regular Expressions 374 | scope 375 | string.regexp 376 | settings 377 | 378 | foreground 379 | #06000000 380 | 381 | 382 | 383 | name 384 | Escape Characters 385 | scope 386 | constant.character.escape 387 | settings 388 | 389 | foreground 390 | #06000000 391 | 392 | 393 | 394 | name 395 | Embedded 396 | scope 397 | punctuation.section.embedded, variable.interpolation 398 | settings 399 | 400 | foreground 401 | #05000000 402 | 403 | 404 | 405 | name 406 | Illegal 407 | scope 408 | invalid.illegal 409 | settings 410 | 411 | background 412 | #01000000 413 | 414 | 415 | 416 | name 417 | Broken 418 | scope 419 | invalid.broken 420 | settings 421 | 422 | background 423 | #03000000 424 | 425 | 426 | 427 | uuid 428 | uuid 429 | 430 | 431 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "android-tzdata" 31 | version = "0.1.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 34 | 35 | [[package]] 36 | name = "android_system_properties" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 40 | dependencies = [ 41 | "libc", 42 | ] 43 | 44 | [[package]] 45 | name = "anstream" 46 | version = "0.6.7" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" 49 | dependencies = [ 50 | "anstyle", 51 | "anstyle-parse", 52 | "anstyle-query", 53 | "anstyle-wincon", 54 | "colorchoice", 55 | "utf8parse", 56 | ] 57 | 58 | [[package]] 59 | name = "anstyle" 60 | version = "1.0.4" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 63 | 64 | [[package]] 65 | name = "anstyle-parse" 66 | version = "0.2.3" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 69 | dependencies = [ 70 | "utf8parse", 71 | ] 72 | 73 | [[package]] 74 | name = "anstyle-query" 75 | version = "1.0.2" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 78 | dependencies = [ 79 | "windows-sys 0.52.0", 80 | ] 81 | 82 | [[package]] 83 | name = "anstyle-wincon" 84 | version = "3.0.2" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 87 | dependencies = [ 88 | "anstyle", 89 | "windows-sys 0.52.0", 90 | ] 91 | 92 | [[package]] 93 | name = "assert_cmd" 94 | version = "2.0.13" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467" 97 | dependencies = [ 98 | "anstyle", 99 | "bstr", 100 | "doc-comment", 101 | "predicates", 102 | "predicates-core", 103 | "predicates-tree", 104 | "wait-timeout", 105 | ] 106 | 107 | [[package]] 108 | name = "autocfg" 109 | version = "1.1.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 112 | 113 | [[package]] 114 | name = "backtrace" 115 | version = "0.3.69" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 118 | dependencies = [ 119 | "addr2line", 120 | "cc", 121 | "cfg-if", 122 | "libc", 123 | "miniz_oxide", 124 | "object", 125 | "rustc-demangle", 126 | ] 127 | 128 | [[package]] 129 | name = "base64" 130 | version = "0.21.7" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 133 | 134 | [[package]] 135 | name = "bincode" 136 | version = "1.3.3" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 139 | dependencies = [ 140 | "serde", 141 | ] 142 | 143 | [[package]] 144 | name = "bit-set" 145 | version = "0.5.3" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 148 | dependencies = [ 149 | "bit-vec", 150 | ] 151 | 152 | [[package]] 153 | name = "bit-vec" 154 | version = "0.6.3" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 157 | 158 | [[package]] 159 | name = "bitflags" 160 | version = "1.3.2" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 163 | 164 | [[package]] 165 | name = "bitflags" 166 | version = "2.4.1" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 169 | 170 | [[package]] 171 | name = "bstr" 172 | version = "1.9.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" 175 | dependencies = [ 176 | "memchr", 177 | "regex-automata", 178 | "serde", 179 | ] 180 | 181 | [[package]] 182 | name = "bumpalo" 183 | version = "3.14.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 186 | 187 | [[package]] 188 | name = "bytecount" 189 | version = "0.6.7" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" 192 | 193 | [[package]] 194 | name = "bytes" 195 | version = "1.5.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 198 | 199 | [[package]] 200 | name = "cc" 201 | version = "1.0.83" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 204 | dependencies = [ 205 | "libc", 206 | ] 207 | 208 | [[package]] 209 | name = "cfg-if" 210 | version = "1.0.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 213 | 214 | [[package]] 215 | name = "chrono" 216 | version = "0.4.31" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" 219 | dependencies = [ 220 | "android-tzdata", 221 | "iana-time-zone", 222 | "js-sys", 223 | "num-traits", 224 | "wasm-bindgen", 225 | "windows-targets 0.48.5", 226 | ] 227 | 228 | [[package]] 229 | name = "clap" 230 | version = "4.4.16" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" 233 | dependencies = [ 234 | "clap_builder", 235 | "clap_derive", 236 | ] 237 | 238 | [[package]] 239 | name = "clap_builder" 240 | version = "4.4.16" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" 243 | dependencies = [ 244 | "anstream", 245 | "anstyle", 246 | "clap_lex", 247 | "strsim", 248 | "terminal_size", 249 | ] 250 | 251 | [[package]] 252 | name = "clap_derive" 253 | version = "4.4.7" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" 256 | dependencies = [ 257 | "heck", 258 | "proc-macro2", 259 | "quote", 260 | "syn", 261 | ] 262 | 263 | [[package]] 264 | name = "clap_lex" 265 | version = "0.6.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" 268 | 269 | [[package]] 270 | name = "color-eyre" 271 | version = "0.6.2" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" 274 | dependencies = [ 275 | "backtrace", 276 | "color-spantrace", 277 | "eyre", 278 | "indenter", 279 | "once_cell", 280 | "owo-colors", 281 | "tracing-error", 282 | ] 283 | 284 | [[package]] 285 | name = "color-spantrace" 286 | version = "0.2.1" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" 289 | dependencies = [ 290 | "once_cell", 291 | "owo-colors", 292 | "tracing-core", 293 | "tracing-error", 294 | ] 295 | 296 | [[package]] 297 | name = "colorchoice" 298 | version = "1.0.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 301 | 302 | [[package]] 303 | name = "core-foundation-sys" 304 | version = "0.8.6" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 307 | 308 | [[package]] 309 | name = "crc32fast" 310 | version = "1.3.2" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 313 | dependencies = [ 314 | "cfg-if", 315 | ] 316 | 317 | [[package]] 318 | name = "deranged" 319 | version = "0.3.11" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 322 | dependencies = [ 323 | "powerfmt", 324 | ] 325 | 326 | [[package]] 327 | name = "difflib" 328 | version = "0.4.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 331 | 332 | [[package]] 333 | name = "doc-comment" 334 | version = "0.3.3" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 337 | 338 | [[package]] 339 | name = "either" 340 | version = "1.9.0" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 343 | 344 | [[package]] 345 | name = "equivalent" 346 | version = "1.0.1" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 349 | 350 | [[package]] 351 | name = "errno" 352 | version = "0.3.8" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 355 | dependencies = [ 356 | "libc", 357 | "windows-sys 0.52.0", 358 | ] 359 | 360 | [[package]] 361 | name = "eyre" 362 | version = "0.6.11" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" 365 | dependencies = [ 366 | "indenter", 367 | "once_cell", 368 | ] 369 | 370 | [[package]] 371 | name = "fancy-regex" 372 | version = "0.11.0" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" 375 | dependencies = [ 376 | "bit-set", 377 | "regex", 378 | ] 379 | 380 | [[package]] 381 | name = "flate2" 382 | version = "1.0.28" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 385 | dependencies = [ 386 | "crc32fast", 387 | "miniz_oxide", 388 | ] 389 | 390 | [[package]] 391 | name = "float-cmp" 392 | version = "0.9.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 395 | dependencies = [ 396 | "num-traits", 397 | ] 398 | 399 | [[package]] 400 | name = "fnv" 401 | version = "1.0.7" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 404 | 405 | [[package]] 406 | name = "futures" 407 | version = "0.3.30" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 410 | dependencies = [ 411 | "futures-channel", 412 | "futures-core", 413 | "futures-executor", 414 | "futures-io", 415 | "futures-sink", 416 | "futures-task", 417 | "futures-util", 418 | ] 419 | 420 | [[package]] 421 | name = "futures-channel" 422 | version = "0.3.30" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 425 | dependencies = [ 426 | "futures-core", 427 | "futures-sink", 428 | ] 429 | 430 | [[package]] 431 | name = "futures-core" 432 | version = "0.3.30" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 435 | 436 | [[package]] 437 | name = "futures-executor" 438 | version = "0.3.30" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 441 | dependencies = [ 442 | "futures-core", 443 | "futures-task", 444 | "futures-util", 445 | ] 446 | 447 | [[package]] 448 | name = "futures-io" 449 | version = "0.3.30" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 452 | 453 | [[package]] 454 | name = "futures-macro" 455 | version = "0.3.30" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 458 | dependencies = [ 459 | "proc-macro2", 460 | "quote", 461 | "syn", 462 | ] 463 | 464 | [[package]] 465 | name = "futures-sink" 466 | version = "0.3.30" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 469 | 470 | [[package]] 471 | name = "futures-task" 472 | version = "0.3.30" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 475 | 476 | [[package]] 477 | name = "futures-util" 478 | version = "0.3.30" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 481 | dependencies = [ 482 | "futures-channel", 483 | "futures-core", 484 | "futures-io", 485 | "futures-macro", 486 | "futures-sink", 487 | "futures-task", 488 | "memchr", 489 | "pin-project-lite", 490 | "pin-utils", 491 | "slab", 492 | ] 493 | 494 | [[package]] 495 | name = "getrandom" 496 | version = "0.2.12" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 499 | dependencies = [ 500 | "cfg-if", 501 | "libc", 502 | "wasi", 503 | ] 504 | 505 | [[package]] 506 | name = "gimli" 507 | version = "0.28.1" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 510 | 511 | [[package]] 512 | name = "hashbrown" 513 | version = "0.14.3" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 516 | 517 | [[package]] 518 | name = "heck" 519 | version = "0.4.1" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 522 | 523 | [[package]] 524 | name = "hermit-abi" 525 | version = "0.3.3" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" 528 | 529 | [[package]] 530 | name = "hl7-mllp-codec" 531 | version = "0.4.0" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "62421b02530df8d7754ef9c6623512dbb4ee91cbca05e41cde79b8450f788034" 534 | dependencies = [ 535 | "bytes", 536 | "log", 537 | "tokio-util", 538 | ] 539 | 540 | [[package]] 541 | name = "hl7-parser" 542 | version = "0.1.0" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "940756b5d3f49adff8abfb256dee901b6db942a20b96812a83519deb3101a895" 545 | dependencies = [ 546 | "indexmap", 547 | "nom", 548 | "nom_locate", 549 | "serde", 550 | "thiserror", 551 | "time", 552 | ] 553 | 554 | [[package]] 555 | name = "hl7-query" 556 | version = "0.1.0" 557 | dependencies = [ 558 | "assert_cmd", 559 | "chrono", 560 | "clap", 561 | "color-eyre", 562 | "hl7-parser", 563 | "nom", 564 | "predicates", 565 | "rand", 566 | "serde", 567 | "serde_json", 568 | "strip-ansi-escapes", 569 | "syntect", 570 | "termcolor", 571 | ] 572 | 573 | [[package]] 574 | name = "hl7-send-receive" 575 | version = "0.1.0" 576 | dependencies = [ 577 | "bytes", 578 | "chrono", 579 | "clap", 580 | "color-eyre", 581 | "futures", 582 | "hl7-mllp-codec", 583 | "hl7-parser", 584 | "rand", 585 | "strip-ansi-escapes", 586 | "termcolor", 587 | "tokio", 588 | "tokio-util", 589 | ] 590 | 591 | [[package]] 592 | name = "iana-time-zone" 593 | version = "0.1.59" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" 596 | dependencies = [ 597 | "android_system_properties", 598 | "core-foundation-sys", 599 | "iana-time-zone-haiku", 600 | "js-sys", 601 | "wasm-bindgen", 602 | "windows-core", 603 | ] 604 | 605 | [[package]] 606 | name = "iana-time-zone-haiku" 607 | version = "0.1.2" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 610 | dependencies = [ 611 | "cc", 612 | ] 613 | 614 | [[package]] 615 | name = "indenter" 616 | version = "0.3.3" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 619 | 620 | [[package]] 621 | name = "indexmap" 622 | version = "2.1.0" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" 625 | dependencies = [ 626 | "equivalent", 627 | "hashbrown", 628 | "serde", 629 | ] 630 | 631 | [[package]] 632 | name = "itertools" 633 | version = "0.11.0" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" 636 | dependencies = [ 637 | "either", 638 | ] 639 | 640 | [[package]] 641 | name = "itoa" 642 | version = "1.0.10" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 645 | 646 | [[package]] 647 | name = "js-sys" 648 | version = "0.3.67" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" 651 | dependencies = [ 652 | "wasm-bindgen", 653 | ] 654 | 655 | [[package]] 656 | name = "lazy_static" 657 | version = "1.4.0" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 660 | 661 | [[package]] 662 | name = "libc" 663 | version = "0.2.152" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" 666 | 667 | [[package]] 668 | name = "line-wrap" 669 | version = "0.1.1" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" 672 | dependencies = [ 673 | "safemem", 674 | ] 675 | 676 | [[package]] 677 | name = "linked-hash-map" 678 | version = "0.5.6" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 681 | 682 | [[package]] 683 | name = "linux-raw-sys" 684 | version = "0.4.12" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" 687 | 688 | [[package]] 689 | name = "lock_api" 690 | version = "0.4.11" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 693 | dependencies = [ 694 | "autocfg", 695 | "scopeguard", 696 | ] 697 | 698 | [[package]] 699 | name = "log" 700 | version = "0.4.20" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 703 | 704 | [[package]] 705 | name = "memchr" 706 | version = "2.7.1" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 709 | 710 | [[package]] 711 | name = "minimal-lexical" 712 | version = "0.2.1" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 715 | 716 | [[package]] 717 | name = "miniz_oxide" 718 | version = "0.7.1" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 721 | dependencies = [ 722 | "adler", 723 | ] 724 | 725 | [[package]] 726 | name = "mio" 727 | version = "0.8.10" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" 730 | dependencies = [ 731 | "libc", 732 | "wasi", 733 | "windows-sys 0.48.0", 734 | ] 735 | 736 | [[package]] 737 | name = "nom" 738 | version = "7.1.3" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 741 | dependencies = [ 742 | "memchr", 743 | "minimal-lexical", 744 | ] 745 | 746 | [[package]] 747 | name = "nom_locate" 748 | version = "4.2.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" 751 | dependencies = [ 752 | "bytecount", 753 | "memchr", 754 | "nom", 755 | ] 756 | 757 | [[package]] 758 | name = "normalize-line-endings" 759 | version = "0.3.0" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 762 | 763 | [[package]] 764 | name = "num-traits" 765 | version = "0.2.17" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 768 | dependencies = [ 769 | "autocfg", 770 | ] 771 | 772 | [[package]] 773 | name = "num_cpus" 774 | version = "1.16.0" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 777 | dependencies = [ 778 | "hermit-abi", 779 | "libc", 780 | ] 781 | 782 | [[package]] 783 | name = "object" 784 | version = "0.32.2" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 787 | dependencies = [ 788 | "memchr", 789 | ] 790 | 791 | [[package]] 792 | name = "once_cell" 793 | version = "1.19.0" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 796 | 797 | [[package]] 798 | name = "owo-colors" 799 | version = "3.5.0" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" 802 | 803 | [[package]] 804 | name = "parking_lot" 805 | version = "0.12.1" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 808 | dependencies = [ 809 | "lock_api", 810 | "parking_lot_core", 811 | ] 812 | 813 | [[package]] 814 | name = "parking_lot_core" 815 | version = "0.9.9" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 818 | dependencies = [ 819 | "cfg-if", 820 | "libc", 821 | "redox_syscall", 822 | "smallvec", 823 | "windows-targets 0.48.5", 824 | ] 825 | 826 | [[package]] 827 | name = "pin-project-lite" 828 | version = "0.2.13" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 831 | 832 | [[package]] 833 | name = "pin-utils" 834 | version = "0.1.0" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 837 | 838 | [[package]] 839 | name = "plist" 840 | version = "1.6.0" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" 843 | dependencies = [ 844 | "base64", 845 | "indexmap", 846 | "line-wrap", 847 | "quick-xml", 848 | "serde", 849 | "time", 850 | ] 851 | 852 | [[package]] 853 | name = "powerfmt" 854 | version = "0.2.0" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 857 | 858 | [[package]] 859 | name = "ppv-lite86" 860 | version = "0.2.17" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 863 | 864 | [[package]] 865 | name = "predicates" 866 | version = "3.0.4" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" 869 | dependencies = [ 870 | "anstyle", 871 | "difflib", 872 | "float-cmp", 873 | "itertools", 874 | "normalize-line-endings", 875 | "predicates-core", 876 | "regex", 877 | ] 878 | 879 | [[package]] 880 | name = "predicates-core" 881 | version = "1.0.6" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" 884 | 885 | [[package]] 886 | name = "predicates-tree" 887 | version = "1.0.9" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" 890 | dependencies = [ 891 | "predicates-core", 892 | "termtree", 893 | ] 894 | 895 | [[package]] 896 | name = "proc-macro2" 897 | version = "1.0.76" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" 900 | dependencies = [ 901 | "unicode-ident", 902 | ] 903 | 904 | [[package]] 905 | name = "quick-xml" 906 | version = "0.31.0" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" 909 | dependencies = [ 910 | "memchr", 911 | ] 912 | 913 | [[package]] 914 | name = "quote" 915 | version = "1.0.35" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 918 | dependencies = [ 919 | "proc-macro2", 920 | ] 921 | 922 | [[package]] 923 | name = "rand" 924 | version = "0.8.5" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 927 | dependencies = [ 928 | "libc", 929 | "rand_chacha", 930 | "rand_core", 931 | ] 932 | 933 | [[package]] 934 | name = "rand_chacha" 935 | version = "0.3.1" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 938 | dependencies = [ 939 | "ppv-lite86", 940 | "rand_core", 941 | ] 942 | 943 | [[package]] 944 | name = "rand_core" 945 | version = "0.6.4" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 948 | dependencies = [ 949 | "getrandom", 950 | ] 951 | 952 | [[package]] 953 | name = "redox_syscall" 954 | version = "0.4.1" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 957 | dependencies = [ 958 | "bitflags 1.3.2", 959 | ] 960 | 961 | [[package]] 962 | name = "regex" 963 | version = "1.10.2" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 966 | dependencies = [ 967 | "aho-corasick", 968 | "memchr", 969 | "regex-automata", 970 | "regex-syntax 0.8.2", 971 | ] 972 | 973 | [[package]] 974 | name = "regex-automata" 975 | version = "0.4.3" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 978 | dependencies = [ 979 | "aho-corasick", 980 | "memchr", 981 | "regex-syntax 0.8.2", 982 | ] 983 | 984 | [[package]] 985 | name = "regex-syntax" 986 | version = "0.7.5" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" 989 | 990 | [[package]] 991 | name = "regex-syntax" 992 | version = "0.8.2" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 995 | 996 | [[package]] 997 | name = "rustc-demangle" 998 | version = "0.1.23" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1001 | 1002 | [[package]] 1003 | name = "rustix" 1004 | version = "0.38.29" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "0a1a81a2478639a14e68937903356dbac62cf52171148924f754bb8a8cd7a96c" 1007 | dependencies = [ 1008 | "bitflags 2.4.1", 1009 | "errno", 1010 | "libc", 1011 | "linux-raw-sys", 1012 | "windows-sys 0.52.0", 1013 | ] 1014 | 1015 | [[package]] 1016 | name = "ryu" 1017 | version = "1.0.16" 1018 | source = "registry+https://github.com/rust-lang/crates.io-index" 1019 | checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" 1020 | 1021 | [[package]] 1022 | name = "safemem" 1023 | version = "0.3.3" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 1026 | 1027 | [[package]] 1028 | name = "same-file" 1029 | version = "1.0.6" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1032 | dependencies = [ 1033 | "winapi-util", 1034 | ] 1035 | 1036 | [[package]] 1037 | name = "scopeguard" 1038 | version = "1.2.0" 1039 | source = "registry+https://github.com/rust-lang/crates.io-index" 1040 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1041 | 1042 | [[package]] 1043 | name = "serde" 1044 | version = "1.0.195" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" 1047 | dependencies = [ 1048 | "serde_derive", 1049 | ] 1050 | 1051 | [[package]] 1052 | name = "serde_derive" 1053 | version = "1.0.195" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" 1056 | dependencies = [ 1057 | "proc-macro2", 1058 | "quote", 1059 | "syn", 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "serde_json" 1064 | version = "1.0.111" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" 1067 | dependencies = [ 1068 | "itoa", 1069 | "ryu", 1070 | "serde", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "sharded-slab" 1075 | version = "0.1.7" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1078 | dependencies = [ 1079 | "lazy_static", 1080 | ] 1081 | 1082 | [[package]] 1083 | name = "signal-hook-registry" 1084 | version = "1.4.1" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 1087 | dependencies = [ 1088 | "libc", 1089 | ] 1090 | 1091 | [[package]] 1092 | name = "slab" 1093 | version = "0.4.9" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1096 | dependencies = [ 1097 | "autocfg", 1098 | ] 1099 | 1100 | [[package]] 1101 | name = "smallvec" 1102 | version = "1.11.2" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" 1105 | 1106 | [[package]] 1107 | name = "socket2" 1108 | version = "0.5.5" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" 1111 | dependencies = [ 1112 | "libc", 1113 | "windows-sys 0.48.0", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "strip-ansi-escapes" 1118 | version = "0.2.0" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" 1121 | dependencies = [ 1122 | "vte", 1123 | ] 1124 | 1125 | [[package]] 1126 | name = "strsim" 1127 | version = "0.10.0" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1130 | 1131 | [[package]] 1132 | name = "syn" 1133 | version = "2.0.48" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 1136 | dependencies = [ 1137 | "proc-macro2", 1138 | "quote", 1139 | "unicode-ident", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "syntect" 1144 | version = "5.1.0" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "e02b4b303bf8d08bfeb0445cba5068a3d306b6baece1d5582171a9bf49188f91" 1147 | dependencies = [ 1148 | "bincode", 1149 | "bitflags 1.3.2", 1150 | "fancy-regex", 1151 | "flate2", 1152 | "fnv", 1153 | "once_cell", 1154 | "plist", 1155 | "regex-syntax 0.7.5", 1156 | "serde", 1157 | "serde_json", 1158 | "thiserror", 1159 | "walkdir", 1160 | "yaml-rust", 1161 | ] 1162 | 1163 | [[package]] 1164 | name = "termcolor" 1165 | version = "1.4.1" 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" 1167 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1168 | dependencies = [ 1169 | "winapi-util", 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "terminal_size" 1174 | version = "0.3.0" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" 1177 | dependencies = [ 1178 | "rustix", 1179 | "windows-sys 0.48.0", 1180 | ] 1181 | 1182 | [[package]] 1183 | name = "termtree" 1184 | version = "0.4.1" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" 1187 | 1188 | [[package]] 1189 | name = "thiserror" 1190 | version = "1.0.56" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" 1193 | dependencies = [ 1194 | "thiserror-impl", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "thiserror-impl" 1199 | version = "1.0.56" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" 1202 | dependencies = [ 1203 | "proc-macro2", 1204 | "quote", 1205 | "syn", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "thread_local" 1210 | version = "1.1.7" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" 1213 | dependencies = [ 1214 | "cfg-if", 1215 | "once_cell", 1216 | ] 1217 | 1218 | [[package]] 1219 | name = "time" 1220 | version = "0.3.31" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" 1223 | dependencies = [ 1224 | "deranged", 1225 | "itoa", 1226 | "powerfmt", 1227 | "serde", 1228 | "time-core", 1229 | "time-macros", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "time-core" 1234 | version = "0.1.2" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 1237 | 1238 | [[package]] 1239 | name = "time-macros" 1240 | version = "0.2.16" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" 1243 | dependencies = [ 1244 | "time-core", 1245 | ] 1246 | 1247 | [[package]] 1248 | name = "tokio" 1249 | version = "1.35.1" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" 1252 | dependencies = [ 1253 | "backtrace", 1254 | "bytes", 1255 | "libc", 1256 | "mio", 1257 | "num_cpus", 1258 | "parking_lot", 1259 | "pin-project-lite", 1260 | "signal-hook-registry", 1261 | "socket2", 1262 | "tokio-macros", 1263 | "windows-sys 0.48.0", 1264 | ] 1265 | 1266 | [[package]] 1267 | name = "tokio-macros" 1268 | version = "2.2.0" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 1271 | dependencies = [ 1272 | "proc-macro2", 1273 | "quote", 1274 | "syn", 1275 | ] 1276 | 1277 | [[package]] 1278 | name = "tokio-util" 1279 | version = "0.7.10" 1280 | source = "registry+https://github.com/rust-lang/crates.io-index" 1281 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" 1282 | dependencies = [ 1283 | "bytes", 1284 | "futures-core", 1285 | "futures-sink", 1286 | "pin-project-lite", 1287 | "tokio", 1288 | "tracing", 1289 | ] 1290 | 1291 | [[package]] 1292 | name = "tracing" 1293 | version = "0.1.40" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1296 | dependencies = [ 1297 | "pin-project-lite", 1298 | "tracing-core", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "tracing-core" 1303 | version = "0.1.32" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1306 | dependencies = [ 1307 | "once_cell", 1308 | "valuable", 1309 | ] 1310 | 1311 | [[package]] 1312 | name = "tracing-error" 1313 | version = "0.2.0" 1314 | source = "registry+https://github.com/rust-lang/crates.io-index" 1315 | checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" 1316 | dependencies = [ 1317 | "tracing", 1318 | "tracing-subscriber", 1319 | ] 1320 | 1321 | [[package]] 1322 | name = "tracing-subscriber" 1323 | version = "0.3.18" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 1326 | dependencies = [ 1327 | "sharded-slab", 1328 | "thread_local", 1329 | "tracing-core", 1330 | ] 1331 | 1332 | [[package]] 1333 | name = "unicode-ident" 1334 | version = "1.0.12" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1337 | 1338 | [[package]] 1339 | name = "utf8parse" 1340 | version = "0.2.1" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1343 | 1344 | [[package]] 1345 | name = "valuable" 1346 | version = "0.1.0" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1349 | 1350 | [[package]] 1351 | name = "vte" 1352 | version = "0.11.1" 1353 | source = "registry+https://github.com/rust-lang/crates.io-index" 1354 | checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" 1355 | dependencies = [ 1356 | "utf8parse", 1357 | "vte_generate_state_changes", 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "vte_generate_state_changes" 1362 | version = "0.1.1" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" 1365 | dependencies = [ 1366 | "proc-macro2", 1367 | "quote", 1368 | ] 1369 | 1370 | [[package]] 1371 | name = "wait-timeout" 1372 | version = "0.2.0" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 1375 | dependencies = [ 1376 | "libc", 1377 | ] 1378 | 1379 | [[package]] 1380 | name = "walkdir" 1381 | version = "2.4.0" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 1384 | dependencies = [ 1385 | "same-file", 1386 | "winapi-util", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "wasi" 1391 | version = "0.11.0+wasi-snapshot-preview1" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1394 | 1395 | [[package]] 1396 | name = "wasm-bindgen" 1397 | version = "0.2.90" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" 1400 | dependencies = [ 1401 | "cfg-if", 1402 | "wasm-bindgen-macro", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "wasm-bindgen-backend" 1407 | version = "0.2.90" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" 1410 | dependencies = [ 1411 | "bumpalo", 1412 | "log", 1413 | "once_cell", 1414 | "proc-macro2", 1415 | "quote", 1416 | "syn", 1417 | "wasm-bindgen-shared", 1418 | ] 1419 | 1420 | [[package]] 1421 | name = "wasm-bindgen-macro" 1422 | version = "0.2.90" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" 1425 | dependencies = [ 1426 | "quote", 1427 | "wasm-bindgen-macro-support", 1428 | ] 1429 | 1430 | [[package]] 1431 | name = "wasm-bindgen-macro-support" 1432 | version = "0.2.90" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" 1435 | dependencies = [ 1436 | "proc-macro2", 1437 | "quote", 1438 | "syn", 1439 | "wasm-bindgen-backend", 1440 | "wasm-bindgen-shared", 1441 | ] 1442 | 1443 | [[package]] 1444 | name = "wasm-bindgen-shared" 1445 | version = "0.2.90" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" 1448 | 1449 | [[package]] 1450 | name = "winapi" 1451 | version = "0.3.9" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1454 | dependencies = [ 1455 | "winapi-i686-pc-windows-gnu", 1456 | "winapi-x86_64-pc-windows-gnu", 1457 | ] 1458 | 1459 | [[package]] 1460 | name = "winapi-i686-pc-windows-gnu" 1461 | version = "0.4.0" 1462 | source = "registry+https://github.com/rust-lang/crates.io-index" 1463 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1464 | 1465 | [[package]] 1466 | name = "winapi-util" 1467 | version = "0.1.6" 1468 | source = "registry+https://github.com/rust-lang/crates.io-index" 1469 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 1470 | dependencies = [ 1471 | "winapi", 1472 | ] 1473 | 1474 | [[package]] 1475 | name = "winapi-x86_64-pc-windows-gnu" 1476 | version = "0.4.0" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1479 | 1480 | [[package]] 1481 | name = "windows-core" 1482 | version = "0.52.0" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1485 | dependencies = [ 1486 | "windows-targets 0.52.0", 1487 | ] 1488 | 1489 | [[package]] 1490 | name = "windows-sys" 1491 | version = "0.48.0" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1494 | dependencies = [ 1495 | "windows-targets 0.48.5", 1496 | ] 1497 | 1498 | [[package]] 1499 | name = "windows-sys" 1500 | version = "0.52.0" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1503 | dependencies = [ 1504 | "windows-targets 0.52.0", 1505 | ] 1506 | 1507 | [[package]] 1508 | name = "windows-targets" 1509 | version = "0.48.5" 1510 | source = "registry+https://github.com/rust-lang/crates.io-index" 1511 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1512 | dependencies = [ 1513 | "windows_aarch64_gnullvm 0.48.5", 1514 | "windows_aarch64_msvc 0.48.5", 1515 | "windows_i686_gnu 0.48.5", 1516 | "windows_i686_msvc 0.48.5", 1517 | "windows_x86_64_gnu 0.48.5", 1518 | "windows_x86_64_gnullvm 0.48.5", 1519 | "windows_x86_64_msvc 0.48.5", 1520 | ] 1521 | 1522 | [[package]] 1523 | name = "windows-targets" 1524 | version = "0.52.0" 1525 | source = "registry+https://github.com/rust-lang/crates.io-index" 1526 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 1527 | dependencies = [ 1528 | "windows_aarch64_gnullvm 0.52.0", 1529 | "windows_aarch64_msvc 0.52.0", 1530 | "windows_i686_gnu 0.52.0", 1531 | "windows_i686_msvc 0.52.0", 1532 | "windows_x86_64_gnu 0.52.0", 1533 | "windows_x86_64_gnullvm 0.52.0", 1534 | "windows_x86_64_msvc 0.52.0", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "windows_aarch64_gnullvm" 1539 | version = "0.48.5" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1542 | 1543 | [[package]] 1544 | name = "windows_aarch64_gnullvm" 1545 | version = "0.52.0" 1546 | source = "registry+https://github.com/rust-lang/crates.io-index" 1547 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 1548 | 1549 | [[package]] 1550 | name = "windows_aarch64_msvc" 1551 | version = "0.48.5" 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" 1553 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1554 | 1555 | [[package]] 1556 | name = "windows_aarch64_msvc" 1557 | version = "0.52.0" 1558 | source = "registry+https://github.com/rust-lang/crates.io-index" 1559 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 1560 | 1561 | [[package]] 1562 | name = "windows_i686_gnu" 1563 | version = "0.48.5" 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" 1565 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1566 | 1567 | [[package]] 1568 | name = "windows_i686_gnu" 1569 | version = "0.52.0" 1570 | source = "registry+https://github.com/rust-lang/crates.io-index" 1571 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 1572 | 1573 | [[package]] 1574 | name = "windows_i686_msvc" 1575 | version = "0.48.5" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1578 | 1579 | [[package]] 1580 | name = "windows_i686_msvc" 1581 | version = "0.52.0" 1582 | source = "registry+https://github.com/rust-lang/crates.io-index" 1583 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 1584 | 1585 | [[package]] 1586 | name = "windows_x86_64_gnu" 1587 | version = "0.48.5" 1588 | source = "registry+https://github.com/rust-lang/crates.io-index" 1589 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1590 | 1591 | [[package]] 1592 | name = "windows_x86_64_gnu" 1593 | version = "0.52.0" 1594 | source = "registry+https://github.com/rust-lang/crates.io-index" 1595 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 1596 | 1597 | [[package]] 1598 | name = "windows_x86_64_gnullvm" 1599 | version = "0.48.5" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1602 | 1603 | [[package]] 1604 | name = "windows_x86_64_gnullvm" 1605 | version = "0.52.0" 1606 | source = "registry+https://github.com/rust-lang/crates.io-index" 1607 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 1608 | 1609 | [[package]] 1610 | name = "windows_x86_64_msvc" 1611 | version = "0.48.5" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1614 | 1615 | [[package]] 1616 | name = "windows_x86_64_msvc" 1617 | version = "0.52.0" 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" 1619 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 1620 | 1621 | [[package]] 1622 | name = "yaml-rust" 1623 | version = "0.4.5" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 1626 | dependencies = [ 1627 | "linked-hash-map", 1628 | ] 1629 | --------------------------------------------------------------------------------