use base64::{Engine as _, engine::general_purpose::STANDARD};
use bytes::Bytes;
use mime::Mime;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DataUriError {
#[error("malformed data uris")]
MalformedDataUri,
#[error("invalid data uri mime type")]
InvalidMimeType(#[from] mime::FromStrError),
#[error("unsupported data uri format")]
UnsupportedFormat,
}
pub fn parse_data_uri(uri: &str) -> Result<(Bytes, Mime), DataUriError> {
let data_url = uri
.strip_prefix("data:")
.ok_or(DataUriError::MalformedDataUri)?;
let (raw_mime, data_url) = data_url
.split_once(';')
.ok_or(DataUriError::MalformedDataUri)?;
let mime: Mime = raw_mime.parse()?;
let parts: Vec<&str> = data_url.split(',').collect();
if parts.len() != 2 {
return Err(DataUriError::MalformedDataUri);
}
let metadata = parts[0];
let base64_data = parts[1];
if !metadata.contains("base64") {
return Err(DataUriError::UnsupportedFormat);
}
let data = STANDARD
.decode(base64_data)
.map(Bytes::from)
.map_err(|_| DataUriError::MalformedDataUri)?;
Ok((data, mime))
}
#[cfg(test)]
mod test {
use crate::data_uri::DataUriError;
use super::parse_data_uri;
use mime::Mime;
#[test]
fn test_valid_data_uri() {
let input = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA";
let result = parse_data_uri(input);
assert!(result.is_ok());
let (data, mime) = result.unwrap();
assert_eq!(mime, "image/png".parse::<Mime>().unwrap());
assert!(!data.is_empty());
}
#[test]
fn test_missing_data_prefix() {
let input = "image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD";
let result = parse_data_uri(input);
assert!(matches!(result, Err(DataUriError::MalformedDataUri)));
}
#[test]
fn test_missing_base64_format() {
let input = "data:image/png;utf8,iVBORw0KGgoAAAANSUhEUgAAAAUA";
let result = parse_data_uri(input);
assert!(matches!(result, Err(DataUriError::UnsupportedFormat)));
}
#[test]
fn test_malformed_uri_no_semicolon() {
let input = "data:image/pngbase64,iVBORw0KGgoAAAANSUhEUgAAAAUA";
let result = parse_data_uri(input);
assert!(matches!(result, Err(DataUriError::MalformedDataUri)));
}
#[test]
fn test_malformed_uri_no_comma() {
let input = "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAAAUA";
let result = parse_data_uri(input);
assert!(matches!(result, Err(DataUriError::MalformedDataUri)));
}
#[test]
fn test_invalid_mime_type() {
let input = "data:invalidmime;base64,abcd";
let result = parse_data_uri(input);
assert!(matches!(result, Err(DataUriError::InvalidMimeType(_))));
}
#[test]
fn test_invalid_base64_data() {
let input = "data:image/png;base64,!!!not_base64!!!";
let result = parse_data_uri(input);
assert!(matches!(result, Err(DataUriError::MalformedDataUri)));
}
}