use std::borrow::Cow;
use std::fmt::Display;
use std::fs::File;
use std::io::{Cursor, Read, Seek, Write};
use std::ops::Deref;
#[derive(Debug, Clone)]
pub struct LowercaseString<'a>(pub(crate) Cow<'a, str>);
impl Deref for LowercaseString<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for LowercaseString<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<'a> LowercaseString<'a> {
#[must_use]
pub fn new_from_str(str: &'a str) -> Self {
if str.chars().any(|c| c.is_ascii_uppercase()) {
Self(Cow::Owned(str.to_ascii_lowercase()))
} else {
Self(Cow::Borrowed(str))
}
}
#[must_use]
pub fn new_from_string(mut str: String) -> Self {
str.make_ascii_lowercase();
Self(Cow::Owned(str))
}
#[must_use]
pub const fn try_from_str(str: &'a str) -> Option<Self> {
let mut i = 0;
while i < str.len() {
if str.as_bytes()[i].is_ascii_uppercase() {
return None;
}
i += 1;
}
Some(Self(Cow::Borrowed(str)))
}
}
impl<S: AsRef<str>> From<S> for LowercaseString<'static> {
fn from(str: S) -> Self {
Self::new_from_string(str.as_ref().to_string())
}
}
pub trait StorageFile: Read + Write + Seek {
fn set_len(&mut self, new_size: u64) -> crate::Result<()>;
}
impl<T: StorageFile> StorageFile for &mut T {
fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
T::set_len(self, new_size)
}
}
impl StorageFile for File {
fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
Ok(File::set_len(self, new_size)?)
}
}
impl StorageFile for &File {
fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
Ok(File::set_len(self, new_size)?)
}
}
impl StorageFile for Cursor<Vec<u8>> {
fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
#[allow(clippy::cast_possible_truncation)]
self.get_mut().resize(new_size as usize, 0);
Ok(())
}
}
impl StorageFile for Cursor<&mut Vec<u8>> {
fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
#[allow(clippy::cast_possible_truncation)]
self.get_mut().resize(new_size as usize, 0);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dont_allocate_already_lowercase_str() {
let lower = LowercaseString::new_from_str("adsf-adsf");
assert!(matches!(lower.0, Cow::Borrowed(_)));
}
}