datapipe/
datapipe_types.rs1use bytes::Bytes;
3use log::error;
4use rand::distr::Alphanumeric;
5use rand::{Rng, rng};
6use std::io::{Error, ErrorKind};
7use url::Url;
8
9#[derive(thiserror::Error, Debug)]
11#[non_exhaustive]
12pub enum DatapipeError {
13 #[error("ConfigurationError: {0}")]
15 ConfigurationError(String),
16 #[error("InputOutputError: {0}")]
18 InputOutputError(String),
19 #[error("EncryptionError: {0}")]
21 EncryptionError(String),
22 #[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
58pub 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#[allow(async_fn_in_trait)]
71pub trait InputReader {
72 async fn read(&mut self) -> Result<Bytes, Error>;
73}
74
75#[allow(async_fn_in_trait)]
77pub trait OutputWriter {
78 async fn write(&mut self, bytes: &[u8]) -> Result<(), Error>;
79}
80
81pub fn generate_random_string(len: usize) -> String {
83 rng()
84 .sample_iter(&Alphanumeric)
85 .take(len)
86 .map(char::from)
87 .collect()
88}
89
90const 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#[derive(Clone, Debug)]
99pub struct EncryptionKey {
100 pub key: KeyBytes,
101 pub nonce: NonceBytes,
102}
103
104impl EncryptionKey {
105 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}