use std::{
fmt::Display,
io::{Read, Write},
str::FromStr,
};
use regex::Regex;
#[derive(Debug)]
pub struct Resource {
image_type: Type,
buffer: Vec<u8>,
}
impl Resource {
#[must_use]
pub fn new(image_type: Type) -> Self {
Self {
image_type,
buffer: Vec::new(),
}
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.buffer.as_slice()
}
#[must_use]
pub fn filename(&self) -> String {
self.image_type.to_string()
}
#[must_use]
pub fn get_type(&self) -> Type {
self.image_type.clone()
}
}
impl Write for Resource {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.buffer.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.buffer.flush()
}
}
impl Read for Resource {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
buf.as_mut().write(&self.buffer)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Version {
Standard,
Size2X,
Size3X,
}
impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Version::Standard => "",
Version::Size2X => "@2x",
Version::Size3X => "@3x",
};
write!(f, "{str}")
}
}
impl FromStr for Version {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"" => Ok(Version::Standard),
"@2x" => Ok(Version::Size2X),
"@3x" => Ok(Version::Size3X),
_ => Err(()),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Type {
Background(Version),
Footer(Version),
Icon(Version),
Logo(Version),
Strip(Version),
Thumbnail(Version),
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Background(v) => write!(f, "background{v}.png"),
Type::Footer(v) => write!(f, "footer{v}.png"),
Type::Icon(v) => write!(f, "icon{v}.png"),
Type::Logo(v) => write!(f, "logo{v}.png"),
Type::Strip(v) => write!(f, "strip{v}.png"),
Type::Thumbnail(v) => write!(f, "thumbnail{v}.png"),
}
}
}
impl FromStr for Type {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let re = Regex::new(r"(?P<type>\w+)(?P<version>@\dx)?\.(?P<format>png)").unwrap();
let captures = re.captures(s);
if let Some(captures) = captures {
let version = if let Some(version) = captures.name("version") {
version
.as_str()
.parse::<Version>()
.unwrap_or(Version::Standard)
} else {
Version::Standard
};
match &captures["type"] {
"background" => Ok(Type::Background(version)),
"footer" => Ok(Type::Footer(version)),
"icon" => Ok(Type::Icon(version)),
"logo" => Ok(Type::Logo(version)),
"strip" => Ok(Type::Strip(version)),
"thumbnail" => Ok(Type::Thumbnail(version)),
_ => Err(()),
}
} else {
Err(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_resource() {
let data = [0u8; 2048];
let mut resource = Resource::new(Type::Icon(Version::Standard));
resource.write_all(&data).unwrap();
println!("{}", resource.buffer.len());
assert_eq!(resource.buffer.len(), 2048);
assert_eq!(resource.get_type(), Type::Icon(Version::Standard));
}
#[test]
fn check_type_string() {
let t = Type::Footer(Version::Standard);
assert_eq!("footer.png", t.to_string());
let t = Type::Logo(Version::Size2X);
assert_eq!("logo@2x.png", t.to_string());
}
#[test]
fn check_type_from_string() {
let t = "footer.png".parse::<Type>().unwrap();
assert_eq!(Type::Footer(Version::Standard), t);
let t = Type::from_str("logo@2x.png").unwrap();
assert_eq!(Type::Logo(Version::Size2X), t);
}
}