Skip to main content

datapipe/
datapipe_types.rs

1/// Shared datapipe types
2use bytes::Bytes;
3use log::error;
4use rand::distr::Alphanumeric;
5use rand::{Rng, rng};
6use std::io::{Error, ErrorKind};
7use url::Url;
8
9// datapipe error types
10#[derive(thiserror::Error, Debug)]
11#[non_exhaustive]
12pub enum DatapipeError {
13    /// Configuration error
14    #[error("ConfigurationError: {0}")]
15    ConfigurationError(String),
16    /// Input-Output error
17    #[error("InputOutputError: {0}")]
18    InputOutputError(String),
19    /// Encryption error
20    #[error("EncryptionError: {0}")]
21    EncryptionError(String),
22    /// Provided parameter is not valid
23    #[error("ValidationError: {0}")]
24    ValidationError(String),
25}
26
27impl From<chacha20poly1305::Error> for DatapipeError {
28    fn from(error: chacha20poly1305::Error) -> Self {
29        Self::EncryptionError(format!("{error}"))
30    }
31}
32
33impl From<std::io::Error> for DatapipeError {
34    fn from(error: std::io::Error) -> Self {
35        let error_string = error_root_cause(&error);
36        Self::InputOutputError(error_string)
37    }
38}
39
40impl From<std::env::VarError> for DatapipeError {
41    fn from(error: std::env::VarError) -> Self {
42        Self::ConfigurationError(format!("{error}"))
43    }
44}
45
46impl From<rustls::Error> for DatapipeError {
47    fn from(error: rustls::Error) -> Self {
48        Self::ConfigurationError(format!("{error}"))
49    }
50}
51
52impl From<rustls::client::VerifierBuilderError> for DatapipeError {
53    fn from(error: rustls::client::VerifierBuilderError) -> Self {
54        Self::ConfigurationError(format!("{error}"))
55    }
56}
57
58/// get the cause of an error
59pub fn error_root_cause(mut err: &(dyn std::error::Error + 'static)) -> String {
60    use std::fmt::Write;
61    let mut s = format!("{}", err);
62    while let Some(src) = err.source() {
63        let _ = write!(s, "\n\tCaused by: {}", src);
64        err = src;
65    }
66    s
67}
68
69/// Reader trait to simplify reading from various input sources
70#[allow(async_fn_in_trait)]
71pub trait InputReader {
72    async fn read(&mut self) -> Result<Bytes, Error>;
73}
74
75/// Writer trait to simplify reading from various output sinks
76#[allow(async_fn_in_trait)]
77pub trait OutputWriter {
78    async fn write(&mut self, bytes: &[u8]) -> Result<(), Error>;
79}
80
81/// generate a String of the given length with pseudorandom content
82pub fn generate_random_string(len: usize) -> String {
83    rng()
84        .sample_iter(&Alphanumeric)
85        .take(len)
86        .map(char::from)
87        .collect()
88}
89
90// typedefs for EncryptionKey fields
91const KEY_LENGTH: usize = 32;
92type KeyBytes = [u8; KEY_LENGTH];
93const NONCE_LENGTH: usize = 19;
94type NonceBytes = [u8; NONCE_LENGTH];
95const REQUIRED_LENGTH: usize = KEY_LENGTH + NONCE_LENGTH;
96
97/// Symmetric encryption key and nonce
98#[derive(Clone, Debug)]
99pub struct EncryptionKey {
100    pub key: KeyBytes,
101    pub nonce: NonceBytes,
102}
103
104impl EncryptionKey {
105    /// create an EncryptionKey using the provided String; must be exactly 51 bytes
106    pub fn new(encryption_key: &str) -> Result<Self, DatapipeError> {
107        if encryption_key.len() != REQUIRED_LENGTH {
108            let error_message = format!(
109                "Encryption key must be {} bytes long; provided encryption key is {} bytes long",
110                REQUIRED_LENGTH,
111                encryption_key.len()
112            );
113            error!("{error_message}");
114            return Err(DatapipeError::ValidationError(error_message));
115        }
116        let encryption_key_bytes = encryption_key.as_bytes();
117        Ok(Self {
118            key: <KeyBytes>::try_from(&encryption_key_bytes[0..KEY_LENGTH]).unwrap(),
119            nonce: <NonceBytes>::try_from(&encryption_key_bytes[KEY_LENGTH..]).unwrap(),
120        })
121    }
122
123    pub fn generate() -> Self {
124        let encryption_key = generate_random_string(REQUIRED_LENGTH);
125        let encryption_key_bytes = encryption_key.into_bytes();
126        Self {
127            key: <KeyBytes>::try_from(&encryption_key_bytes[0..KEY_LENGTH]).unwrap(),
128            nonce: <NonceBytes>::try_from(&encryption_key_bytes[KEY_LENGTH..]).unwrap(),
129        }
130    }
131}
132
133impl std::fmt::Display for EncryptionKey {
134    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
135        let mut bytes: [u8; REQUIRED_LENGTH] = [0; REQUIRED_LENGTH];
136        bytes[0..KEY_LENGTH].copy_from_slice(self.key.as_slice());
137        bytes[KEY_LENGTH..REQUIRED_LENGTH].copy_from_slice(self.nonce.as_slice());
138        write!(f, "{}", String::from_utf8(bytes.to_vec()).unwrap())
139    }
140}
141
142pub fn good_url(maybe_url: &str, prefix: &str) -> Result<url::Url, Error> {
143    match maybe_url.starts_with(prefix) {
144        true => match Url::parse(maybe_url) {
145            Ok(url) => Ok(url),
146            Err(error) => {
147                let error_message = format!("Error parsing URL '{}': {}", maybe_url, error);
148                error!("{}", error_message);
149                Err(Error::new(ErrorKind::InvalidInput, error_message))
150            }
151        },
152        false => {
153            let error_message = format!("URL '{}' must start with '{}'", maybe_url, prefix);
154            error!("{}", error_message);
155            Err(Error::new(ErrorKind::InvalidInput, error_message))
156        }
157    }
158}