use std::{
io::{Error, ErrorKind, Read, Result},
os::fd::{AsFd, AsRawFd},
};
use tokio::io::{AsyncRead, AsyncReadExt};
pub(crate) fn proc_self_fd(fd: impl AsFd) -> String {
format!("/proc/self/fd/{}", fd.as_fd().as_raw_fd())
}
pub fn read_exactish(reader: &mut impl Read, buf: &mut [u8]) -> Result<bool> {
let buflen = buf.len();
let mut todo: &mut [u8] = buf;
while !todo.is_empty() {
match reader.read(todo) {
Ok(0) => {
return match todo.len() {
s if s == buflen => Ok(false), _ => Err(Error::from(ErrorKind::UnexpectedEof)),
};
}
Ok(n) => todo = &mut todo[n..],
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
}
Ok(true)
}
pub async fn read_exactish_async(
reader: &mut (impl AsyncRead + Unpin),
buf: &mut [u8],
) -> Result<bool> {
let buflen = buf.len();
let mut todo: &mut [u8] = buf;
while !todo.is_empty() {
match reader.read(todo).await {
Ok(0) => {
return match todo.len() {
s if s == buflen => Ok(false), _ => Err(ErrorKind::UnexpectedEof.into()),
};
}
Ok(n) => todo = &mut todo[n..],
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
}
Ok(true)
}
pub type Sha256Digest = [u8; 32];
pub fn parse_sha256(string: impl AsRef<str>) -> Result<Sha256Digest> {
let mut value = [0u8; 32];
hex::decode_to_slice(string.as_ref(), &mut value)
.map_err(|source| Error::new(ErrorKind::InvalidInput, source))?;
Ok(value)
}
#[cfg(test)]
mod test {
use similar_asserts::assert_eq;
use super::*;
fn read_exactish_common(read9: fn(&mut &[u8]) -> Result<bool>) {
let mut r = b"" as &[u8];
assert_eq!(read9(&mut r).unwrap(), false);
assert_eq!(read9(&mut r).unwrap(), false);
r = b"ninebytes";
assert_eq!(read9(&mut r).unwrap(), true);
assert_eq!(read9(&mut r).unwrap(), false);
r = b"twelve bytes";
assert_eq!(read9(&mut r).unwrap(), true);
assert_eq!(read9(&mut r).unwrap_err().kind(), ErrorKind::UnexpectedEof);
r = b"eighteen(18) bytes";
assert_eq!(read9(&mut r).unwrap(), true);
assert_eq!(read9(&mut r).unwrap(), true);
assert_eq!(read9(&mut r).unwrap(), false);
}
#[test]
fn test_read_exactish() {
read_exactish_common(|r| read_exactish(r, &mut [0; 9]));
}
#[test]
fn test_read_exactish_broken_reader() {
struct BrokenReader;
impl Read for BrokenReader {
fn read(&mut self, _buffer: &mut [u8]) -> Result<usize> {
Err(ErrorKind::NetworkDown.into())
}
}
assert_eq!(
read_exactish(&mut BrokenReader, &mut [0; 9])
.unwrap_err()
.kind(),
ErrorKind::NetworkDown
);
}
#[test]
fn test_read_exactish_async() {
read_exactish_common(|r| {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(read_exactish_async(r, &mut [0; 9]))
});
}
#[tokio::test]
async fn test_read_exactish_broken_reader_async() {
let mut reader = tokio_test::io::Builder::new()
.read_error(Error::from(ErrorKind::NetworkDown))
.build();
assert_eq!(
read_exactish_async(&mut reader, &mut [0; 9])
.await
.unwrap_err()
.kind(),
ErrorKind::NetworkDown
);
}
#[test]
fn test_parse_sha256() {
let valid = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";
let valid_caps = "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFf";
let valid_weird = "00112233445566778899aABbcCDdeEFf00112233445566778899AaBbCcDdEeFf";
assert_eq!(hex::encode(parse_sha256(valid).unwrap()), valid);
assert_eq!(hex::encode(parse_sha256(valid_caps).unwrap()), valid);
assert_eq!(hex::encode(parse_sha256(valid_weird).unwrap()), valid);
fn assert_invalid(x: &str) {
assert_eq!(parse_sha256(x).unwrap_err().kind(), ErrorKind::InvalidInput);
}
assert_invalid("");
assert_invalid("/etc/shadow");
assert_invalid("00112233445566778899aabbccddeeff00112233445566778899aabbccddeef");
assert_invalid("00112233445566778899aabbccddeeff00112233445566778899aabbccddeefff");
assert_invalid("00112233445566778899aabbccddeeff00112233445566778899aabbccddeefg");
}
}