use crate::bos::{Bos, DefaultStr};
use crate::types::cid::Cid;
use crate::{CowStr, IntoStatic, types::cid::CidLink};
use alloc::string::{String, ToString};
use core::convert::Infallible;
use core::{fmt, hash::Hash, ops::Deref, str::FromStr};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
#[serde(bound(deserialize = "S: Bos<str> + AsRef<str> + Deserialize<'de>"))]
pub struct Blob<S: Bos<str> + AsRef<str> = DefaultStr> {
pub r#ref: CidLink<S>,
pub mime_type: MimeType<S>,
pub size: usize,
}
impl<S: Bos<str> + AsRef<str>> Blob<S> {
pub fn cid(&self) -> &Cid<S> {
&self.r#ref.0
}
}
impl<S: Bos<str> + AsRef<str> + Serialize> Serialize for Blob<S> {
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: Serializer,
{
use serde::ser::SerializeMap;
if serializer.is_human_readable() {
let mut map = serializer.serialize_map(Some(4))?;
map.serialize_entry("$type", "blob")?;
let mut ref_map = alloc::collections::BTreeMap::new();
ref_map.insert("$link", self.r#ref.as_str());
map.serialize_entry("ref", &ref_map)?;
map.serialize_entry("mimeType", &self.mime_type)?;
map.serialize_entry("size", &self.size)?;
map.end()
} else {
let mut map = serializer.serialize_map(Some(4))?;
map.serialize_entry("$type", "blob")?;
map.serialize_entry("ref", &self.r#ref)?;
map.serialize_entry("mimeType", &self.mime_type)?;
map.serialize_entry("size", &self.size)?;
map.end()
}
}
}
impl<S: Bos<str> + AsRef<str> + IntoStatic> IntoStatic for Blob<S>
where
S::Output: Bos<str> + AsRef<str>,
{
type Output = Blob<S::Output>;
fn into_static(self) -> Self::Output {
Blob {
r#ref: self.r#ref.into_static(),
mime_type: self.mime_type.into_static(),
size: self.size,
}
}
}
impl<S: Bos<str> + AsRef<str>> Blob<S> {
pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Blob<B> {
Blob {
r#ref: self.r#ref.convert(),
mime_type: self.mime_type.convert(),
size: self.size,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(tag = "$type", rename_all = "lowercase")]
#[serde(bound(
serialize = "S: Bos<str> + AsRef<str> + Serialize",
deserialize = "S: Bos<str> + AsRef<str> + Deserialize<'de>"
))]
pub enum BlobRef<S: Bos<str> + AsRef<str> = DefaultStr> {
Blob(Blob<S>),
}
impl<S: Bos<str> + AsRef<str>> BlobRef<S> {
pub fn blob(&self) -> &Blob<S> {
match self {
BlobRef::Blob(blob) => blob,
}
}
}
impl<S: Bos<str> + AsRef<str>> From<BlobRef<S>> for Blob<S> {
fn from(blob_ref: BlobRef<S>) -> Self {
match blob_ref {
BlobRef::Blob(blob) => blob,
}
}
}
impl<S: Bos<str> + AsRef<str>> From<Blob<S>> for BlobRef<S> {
fn from(blob: Blob<S>) -> Self {
BlobRef::Blob(blob)
}
}
impl<S: Bos<str> + AsRef<str> + IntoStatic> IntoStatic for BlobRef<S>
where
S::Output: Bos<str> + AsRef<str>,
{
type Output = BlobRef<S::Output>;
fn into_static(self) -> Self::Output {
match self {
BlobRef::Blob(blob) => BlobRef::Blob(blob.into_static()),
}
}
}
impl<S: Bos<str> + AsRef<str>> BlobRef<S> {
pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> BlobRef<B> {
match self {
BlobRef::Blob(blob) => BlobRef::Blob(blob.convert()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct MimeType<S: Bos<str> = DefaultStr>(pub S);
impl<S: Bos<str> + AsRef<str>> MimeType<S> {
pub fn as_str(&self) -> &str {
self.0.as_ref()
}
}
impl<S: Bos<str>> MimeType<S> {
pub fn new(mime_type: S) -> Self {
Self(mime_type)
}
pub fn convert<B: Bos<str> + From<S>>(self) -> MimeType<B> {
MimeType(B::from(self.0))
}
}
impl<S: Bos<str> + FromStr> MimeType<S> {
pub fn new_owned(mime_type: impl AsRef<str>) -> Self {
Self(S::from_str(mime_type.as_ref()).unwrap_or_else(|_| unreachable!()))
}
pub fn new_static(mime_type: &'static str) -> Self {
Self(S::from_str(mime_type).unwrap_or_else(|_| unreachable!()))
}
}
impl<'de, S> Deserialize<'de> for MimeType<S>
where
S: Bos<str> + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(MimeType(S::deserialize(deserializer)?))
}
}
impl FromStr for MimeType {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::new_owned(s))
}
}
impl<S: Bos<str> + IntoStatic> IntoStatic for MimeType<S>
where
S::Output: Bos<str>,
{
type Output = MimeType<S::Output>;
fn into_static(self) -> Self::Output {
MimeType(self.0.into_static())
}
}
impl<S: Bos<str> + AsRef<str>> fmt::Display for MimeType<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl<S: Bos<str> + AsRef<str>> From<MimeType<S>> for String {
fn from(value: MimeType<S>) -> Self {
value.as_str().to_string()
}
}
impl From<String> for MimeType {
fn from(value: String) -> Self {
Self::new_owned(value)
}
}
impl<'m> From<CowStr<'m>> for MimeType<CowStr<'m>> {
fn from(value: CowStr<'m>) -> Self {
Self(value)
}
}
impl<S: Bos<str> + AsRef<str>> AsRef<str> for MimeType<S> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<S: Bos<str> + AsRef<str>> Deref for MimeType<S> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}