protoflow_blocks/blocks/io/
encode_hex.rs

1// This is free and unencumbered software released into the public domain.
2
3use crate::{
4    prelude::{format, Bytes, String},
5    IoBlocks, StdioConfig, StdioError, StdioSystem, System,
6};
7use protoflow_core::{Block, BlockResult, BlockRuntime, InputPort, OutputPort};
8use protoflow_derive::Block;
9use simple_mermaid::mermaid;
10
11/// A block that encodes a byte stream into hexadecimal form.
12///
13/// # Block Diagram
14#[doc = mermaid!("../../../doc/io/encode_hex.mmd")]
15///
16/// # Sequence Diagram
17#[doc = mermaid!("../../../doc/io/encode_hex.seq.mmd" framed)]
18///
19/// # Examples
20///
21/// ## Using the block in a system
22///
23/// ```rust
24/// # use protoflow_blocks::*;
25/// # fn main() {
26/// System::build(|s| {
27///     let stdin = s.read_stdin();
28///     let hex_encoder = s.encode_hex();
29///     let stdout = s.write_stdout();
30///     s.connect(&stdin.output, &hex_encoder.input);
31///     s.connect(&hex_encoder.output, &stdout.input);
32/// });
33/// # }
34/// ```
35///
36/// ## Running the block via the CLI
37///
38/// ```console
39/// $ protoflow execute EncodeHex
40/// ```
41///
42#[derive(Block, Clone)]
43pub struct EncodeHex {
44    /// The input byte stream.
45    #[input]
46    pub input: InputPort<Bytes>,
47
48    /// The output text stream.
49    #[output]
50    pub output: OutputPort<Bytes>,
51}
52
53impl EncodeHex {
54    pub fn new(input: InputPort<Bytes>, output: OutputPort<Bytes>) -> Self {
55        Self { input, output }
56    }
57
58    pub fn with_system(system: &System) -> Self {
59        use crate::SystemBuilding;
60        Self::new(system.input(), system.output())
61    }
62}
63
64impl Block for EncodeHex {
65    fn execute(&mut self, runtime: &dyn BlockRuntime) -> BlockResult {
66        runtime.wait_for(&self.input)?;
67
68        while let Some(message) = self.input.recv()? {
69            let mut buffer = String::with_capacity(message.len() * 2);
70            for byte in message.iter() {
71                buffer.push_str(format!("{:02x}", byte).as_str()); // TODO: optimize
72            }
73            let message = Bytes::from(buffer);
74            self.output.send(&message)?;
75        }
76
77        Ok(())
78    }
79}
80
81#[cfg(feature = "std")]
82impl StdioSystem for EncodeHex {
83    fn build_system(config: StdioConfig) -> Result<System, StdioError> {
84        use crate::SystemBuilding;
85
86        config.reject_any()?;
87
88        Ok(System::build(|s| {
89            let stdin = config.read_stdin(s);
90            let hex_encoder = s.encode_hex();
91            let stdout = config.write_stdout(s);
92            s.connect(&stdin.output, &hex_encoder.input);
93            s.connect(&hex_encoder.output, &stdout.input);
94        }))
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::EncodeHex;
101    use crate::{System, SystemBuilding};
102
103    #[test]
104    fn instantiate_block() {
105        // Check that the block is constructible:
106        let _ = System::build(|s| {
107            let _ = s.block(EncodeHex::new(s.input(), s.output()));
108        });
109    }
110}