From bdc2b5094068d368e9941d3bc4b6064687dc3ef7 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 12 Dec 2024 11:58:18 +0200 Subject: [PATCH] remove mime type enumeration as external feature, parse and return raw string instead --- src/client/connection/response/meta/mime.rs | 174 +++--------------- .../connection/response/meta/mime/error.rs | 13 +- 2 files changed, 29 insertions(+), 158 deletions(-) diff --git a/src/client/connection/response/meta/mime.rs b/src/client/connection/response/meta/mime.rs index 5aa362c..4568ce8 100644 --- a/src/client/connection/response/meta/mime.rs +++ b/src/client/connection/response/meta/mime.rs @@ -1,50 +1,19 @@ -//! MIME type parser for different data types: -//! -//! * UTF-8 buffer with entire response or just with meta slice (that include entire **header**) -//! * String (that include **header**) -//! * [Uri](https://docs.gtk.org/glib/struct.Uri.html) (that include **extension**) -//! * `std::Path` (that include **extension**) +//! MIME type parser for different data types pub mod error; pub use error::Error; -use glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags, Uri}; -use std::path::Path; +use glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags}; /// https://geminiprotocol.net/docs/gemtext-specification.gmi#media-type-parameters -#[derive(Debug)] -pub enum Mime { - // text - TextGemini, - TextPlain, - /// Match unlisted `text/*` - Text, - // image - ImageGif, - ImageJpeg, - ImagePng, - ImageSvg, - ImageWebp, - /// Match unlisted `image/*` - Image, - // audio - AudioFlac, - AudioMpeg, - AudioOgg, - /// Match unlisted `audio/*` - Audio, - // other - /// Match unlisted `application/*` - Application, -} // @TODO + +pub struct Mime { + pub value: String, +} impl Mime { /// Create new `Self` from UTF-8 buffer (that includes **header**) - /// - /// * result could be `None` for some [status codes](https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes) - /// that does not expect MIME type in header - /// * includes `Self::from_string` parser, - /// it means that given buffer should contain some **header** (not filepath or any other type of strings) + /// * return `None` for non 2* [status codes](https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes) pub fn from_utf8(buffer: &[u8]) -> Result, Error> { // Define max buffer length for this method const MAX_LEN: usize = 0x400; // 1024 @@ -62,118 +31,27 @@ impl Mime { } } - /// Create new `Self` from `std::Path` that includes file **extension** - pub fn from_path(path: &Path) -> Result { - match path.extension().and_then(|extension| extension.to_str()) { - // Text - Some("gmi" | "gemini") => Ok(Self::TextGemini), - Some("txt") => Ok(Self::TextPlain), - - // Image - Some("gif") => Ok(Self::ImageGif), - Some("jpeg" | "jpg") => Ok(Self::ImageJpeg), - Some("png") => Ok(Self::ImagePng), - Some("svg") => Ok(Self::ImageSvg), - Some("webp") => Ok(Self::ImageWebp), - - // Audio - Some("flac") => Ok(Self::AudioFlac), - Some("mp3") => Ok(Self::AudioMpeg), - Some("oga" | "ogg" | "opus" | "spx") => Ok(Self::AudioOgg), - _ => Err(Error::Undefined(None)), - } // @TODO extension to lowercase - } - /// Create new `Self` from string that includes **header** - /// - /// **Return** - /// - /// * `None` if MIME type not found - /// * `Error::Undefined` if status code 2* and type not found in `Mime` enum - pub fn from_string(value: &str) -> Result, Error> { - // Text - if value.contains("text/gemini") { - return Ok(Some(Self::TextGemini)); + /// * return `None` for non 2* [status codes](https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes) + pub fn from_string(subject: &str) -> Result, Error> { + if !subject.starts_with("2") { + return Ok(None); } - - if value.contains("text/plain") { - return Ok(Some(Self::TextPlain)); + match parse(subject) { + Some(value) => Ok(Some(Self { value })), + None => Err(Error::Undefined), } - - if value.contains("text/") { - return Ok(Some(Self::Text)); - } - - // Image - if value.contains("image/gif") { - return Ok(Some(Self::ImageGif)); - } - - if value.contains("image/jpeg") { - return Ok(Some(Self::ImageJpeg)); - } - - if value.contains("image/png") { - return Ok(Some(Self::ImagePng)); - } - - if value.contains("image/svg+xml") { - return Ok(Some(Self::ImageSvg)); - } - - if value.contains("image/webp") { - return Ok(Some(Self::ImageWebp)); - } - - if value.contains("image/") { - return Ok(Some(Self::Image)); - } - - // Audio - if value.contains("audio/flac") { - return Ok(Some(Self::AudioFlac)); - } - - if value.contains("audio/mpeg") { - return Ok(Some(Self::AudioMpeg)); - } - - if value.contains("audio/ogg") { - return Ok(Some(Self::AudioOgg)); - } - - if value.contains("audio/") { - return Ok(Some(Self::Audio)); - } - - // Other - if value.contains("application/") { - return Ok(Some(Self::Application)); - } - - // application/x-tar - - // Some type exist, but not defined yet (on status code is 2*) - if value.starts_with("2") && value.contains("/") { - return Err(Error::Undefined( - Regex::split_simple( - r"^2\d{1}\s([^\/]+\/[^\s]+)", - value, - RegexCompileFlags::DEFAULT, - RegexMatchFlags::DEFAULT, - ) - .get(1) - .map(|this| this.to_string()), - )); - } - - // Done - Ok(None) // may be empty (status code ^2*) - } - - /// Create new `Self` from [Uri](https://docs.gtk.org/glib/struct.Uri.html) - /// that includes file **extension** - pub fn from_uri(uri: &Uri) -> Result { - Self::from_path(Path::new(&uri.to_string())) } } + +/// Extract MIME type from from string that includes **header** +pub fn parse(value: &str) -> Option { + Regex::split_simple( + r"^2\d{1}\s([^\/]+\/[^\s;]+)", + value, + RegexCompileFlags::DEFAULT, + RegexMatchFlags::DEFAULT, + ) + .get(1) + .map(|this| this.to_string()) +} diff --git a/src/client/connection/response/meta/mime/error.rs b/src/client/connection/response/meta/mime/error.rs index d79790a..9ea72ba 100644 --- a/src/client/connection/response/meta/mime/error.rs +++ b/src/client/connection/response/meta/mime/error.rs @@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter, Result}; pub enum Error { Decode(std::string::FromUtf8Error), Protocol, - Undefined(Option), + Undefined, } impl Display for Error { @@ -16,15 +16,8 @@ impl Display for Error { Self::Protocol => { write!(f, "Protocol error") } - Self::Undefined(e) => { - write!( - f, - "{}", - match e { - Some(value) => format!("`{value}` undefined"), - None => "Could not parse value".to_string(), - } - ) + Self::Undefined => { + write!(f, "MIME type undefined") } } }