multiplayer-game/src/shared_enums.rs
2025-10-12 21:12:15 +02:00

308 lines
8.3 KiB
Rust

use std::{io, str::FromStr};
#[derive(Debug)]
pub enum ContentType {
Text(TextType),
Aplication(ApplicationType),
Image(Image),
Any,
}
impl ContentType {
fn to_str(&self) -> Box<str> {
match self {
ContentType::Text(text) => format!("text/{}", text.to_str()).into_boxed_str(),
ContentType::Aplication(app) => {
format!("application/{}", app.to_str()).into_boxed_str()
}
ContentType::Image(img) => format!("image/{}", img.to_str()).into_boxed_str(),
ContentType::Any => "*/*".to_string().into_boxed_str(),
}
}
}
#[derive(Debug)]
pub enum Parameter {
Preference(f32),
Charset(Charset),
Other(Box<str>, Box<str>),
}
impl Parameter {
fn to_str(&self) -> Box<str> {
match &self {
Parameter::Preference(val) => format!("q={val}").into_boxed_str(),
Parameter::Charset(ch) => format!("charset={}", ch.to_str()).into_boxed_str(),
Parameter::Other(p, v) => format!("{p}={v}").into_boxed_str(),
}
}
}
#[derive(Debug)]
pub enum Charset {
UTF8,
}
impl Charset {
fn to_str(&self) -> &'static str {
match self {
Charset::UTF8 => "utf-8",
}
}
}
#[derive(Debug)]
pub struct Content {
pub content_type: ContentType,
pub parameter: Option<Vec<Parameter>>,
}
impl Content {
pub fn to_str(&self) -> Box<str> {
match &self.parameter {
Some(p) => format!(
"{}; {}",
self.content_type.to_str(),
p.iter()
.map(|par| par.to_str())
.collect::<Vec<_>>()
.join("; ")
)
.into_boxed_str(),
None => self.content_type.to_str(),
}
}
}
impl Content {
pub fn new(content_type: ContentType) -> Self {
Self {
content_type,
parameter: None,
}
}
pub fn with_params(content_type: ContentType, params: Vec<Parameter>) -> Self {
Self {
content_type,
parameter: Some(params),
}
}
pub fn add_parameter(&mut self, param: Parameter) {
match &mut self.parameter {
Some(params) => params.push(param),
None => self.parameter = Some(vec![param]),
}
}
pub fn quality(&self) -> Option<f32> {
self.parameter.as_ref()?.iter().find_map(|p| {
if let Parameter::Preference(q) = p {
Some(*q)
} else {
None
}
})
}
pub fn charset(&self) -> Option<&Charset> {
self.parameter.as_ref()?.iter().find_map(|p| {
if let Parameter::Charset(cs) = p {
Some(cs)
} else {
None
}
})
}
pub fn matches(&self, other: &ContentType) -> bool {
type C = ContentType;
match (&self.content_type, other) {
(C::Any, _) | (_, C::Any) => true,
(C::Text(TextType::Any), C::Text(_)) => true,
(C::Text(_), C::Text(TextType::Any)) => true,
(C::Aplication(ApplicationType::Any), C::Aplication(_)) => true,
(C::Aplication(_), C::Aplication(ApplicationType::Any)) => true,
(C::Image(Image::Any), C::Image(_)) => true,
(C::Image(_), C::Image(Image::Any)) => true,
(a, b) => std::mem::discriminant(a) == std::mem::discriminant(b),
}
}
pub fn is_text(&self) -> bool {
matches!(self.content_type, ContentType::Text(_))
}
pub fn is_application(&self) -> bool {
matches!(self.content_type, ContentType::Aplication(_))
}
pub fn is_image(&self) -> bool {
matches!(self.content_type, ContentType::Image(_))
}
pub fn html_utf8() -> Self {
Self::with_params(
ContentType::Text(TextType::Html),
vec![Parameter::Charset(Charset::UTF8)],
)
}
pub fn json_utf8() -> Self {
Self::with_params(
ContentType::Aplication(ApplicationType::Json),
vec![Parameter::Charset(Charset::UTF8)],
)
}
}
pub fn invalid_data_error(error: &str) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, error)
}
impl FromStr for Content {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split(';').collect::<Vec<&str>>().as_slice() {
[val] => Ok(Self {
content_type: ContentType::from_str(val)?,
parameter: None,
}),
[val, par @ ..] => Ok(Self {
content_type: ContentType::from_str(val)?,
parameter: Some(
par.iter()
.map(|p| Parameter::from_str(p))
.collect::<Result<Vec<_>, _>>()?,
),
}),
_ => Err(invalid_data_error("Invalid content-type")),
}
}
}
impl FromStr for Parameter {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split('=').collect::<Vec<&str>>().as_slice() {
["q", value] => {
let pref_val = match value.parse::<f32>() {
Ok(v) => Ok(v),
Err(_) => Err(invalid_data_error("Invalid preference")),
}?;
Ok(Parameter::Preference(pref_val))
}
["charset", "utf-8"] => Ok(Parameter::Charset(Charset::UTF8)),
[t, v] => Ok(Parameter::Other(
t.to_string().into_boxed_str(),
v.to_string().into_boxed_str(),
)),
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid parameter",
)),
}
}
}
impl FromStr for ContentType {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split("/").collect();
match parts.as_slice() {
["*", "*"] => Ok(ContentType::Any),
["text", "*"] => Ok(ContentType::Text(TextType::Any)),
["text", "html"] => Ok(ContentType::Text(TextType::Html)),
["text", "css"] => Ok(ContentType::Text(TextType::Css)),
["text", "javascript"] => Ok(ContentType::Text(TextType::Javascript)),
["application", "json"] => Ok(ContentType::Aplication(ApplicationType::Json)),
["application", "xhtml+xml"] => Ok(ContentType::Aplication(ApplicationType::XhtmlXml)),
["application", "xml"] => Ok(ContentType::Aplication(ApplicationType::Xml)),
["application", "*"] => Ok(ContentType::Aplication(ApplicationType::Any)),
["image", "png"] => Ok(ContentType::Image(Image::Png)),
["image", "jpeg"] | ["image", "jpg"] => Ok(ContentType::Image(Image::Jpeg)),
["image", "avif"] => Ok(ContentType::Image(Image::Avif)),
["image", "webp"] => Ok(ContentType::Image(Image::Webp)),
["image", "svg"] | ["image", "svg+xml"] => Ok(ContentType::Image(Image::Svg)),
["image", "*"] => Ok(ContentType::Image(Image::Any)),
_ => {
println!("{parts:?}");
Err(invalid_data_error("Invalid content-type-type"))
}
}
}
}
#[derive(Debug)]
pub enum Image {
Png,
Avif,
Jpeg,
Webp,
Svg,
Any,
}
impl Image {
fn to_str(&self) -> &'static str {
match self {
Image::Png => "png",
Image::Avif => "avif",
Image::Jpeg => "jpeg",
Image::Webp => "webp",
Image::Svg => "svg",
Image::Any => "*",
}
}
}
#[derive(Debug)]
pub enum TextType {
Html,
Css,
Javascript,
Any,
}
impl TextType {
fn to_str(&self) -> &'static str {
match self {
TextType::Html => "html",
TextType::Css => "css",
TextType::Javascript => "javascript",
TextType::Any => "*",
}
}
}
#[derive(Debug)]
pub enum ApplicationType {
Json,
Any,
XhtmlXml,
Xml,
}
impl ApplicationType {
fn to_str(&self) -> &'static str {
match self {
ApplicationType::Json => "json",
ApplicationType::Any => "*",
ApplicationType::XhtmlXml => "xhtml+xml",
ApplicationType::Xml => "xml",
}
}
}