use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct FileField(String);
impl FileField {
pub fn key(&self) -> &str {
&self.0
}
pub fn url(&self) -> String {
crate::storage::storage_opt()
.map(|s| s.url(self.key()))
.unwrap_or_else(|| self.0.clone())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl From<String> for FileField {
fn from(key: String) -> Self {
FileField(key)
}
}
impl From<&str> for FileField {
fn from(key: &str) -> Self {
FileField(key.to_string())
}
}
impl AsRef<str> for FileField {
fn as_ref(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for FileField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct ImageField(FileField);
impl std::ops::Deref for ImageField {
type Target = FileField;
fn deref(&self) -> &FileField {
&self.0
}
}
impl From<String> for ImageField {
fn from(key: String) -> Self {
ImageField(FileField::from(key))
}
}
impl From<&str> for ImageField {
fn from(key: &str) -> Self {
ImageField(FileField::from(key))
}
}
impl AsRef<str> for ImageField {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl std::fmt::Display for ImageField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
macro_rules! impl_string_newtype {
($ty:ty) => {
impl Serialize for $ty {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(self.key())
}
}
impl<'de> Deserialize<'de> for $ty {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let key = String::deserialize(d)?;
Ok(<$ty>::from(key))
}
}
impl sqlx::Type<sqlx::Sqlite> for $ty {
fn type_info() -> sqlx::sqlite::SqliteTypeInfo {
<String as sqlx::Type<sqlx::Sqlite>>::type_info()
}
fn compatible(ty: &sqlx::sqlite::SqliteTypeInfo) -> bool {
<String as sqlx::Type<sqlx::Sqlite>>::compatible(ty)
}
}
impl sqlx::Type<sqlx::Postgres> for $ty {
fn type_info() -> sqlx::postgres::PgTypeInfo {
<String as sqlx::Type<sqlx::Postgres>>::type_info()
}
fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
<String as sqlx::Type<sqlx::Postgres>>::compatible(ty)
}
}
impl<'r> sqlx::Decode<'r, sqlx::Sqlite> for $ty {
fn decode(
value: sqlx::sqlite::SqliteValueRef<'r>,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let s = <String as sqlx::Decode<sqlx::Sqlite>>::decode(value)?;
Ok(<$ty>::from(s))
}
}
impl<'r> sqlx::Decode<'r, sqlx::Postgres> for $ty {
fn decode(
value: sqlx::postgres::PgValueRef<'r>,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let s = <String as sqlx::Decode<sqlx::Postgres>>::decode(value)?;
Ok(<$ty>::from(s))
}
}
impl<'q> sqlx::Encode<'q, sqlx::Sqlite> for $ty {
fn encode_by_ref(
&self,
buf: &mut <sqlx::Sqlite as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
<String as sqlx::Encode<'q, sqlx::Sqlite>>::encode_by_ref(
&self.key().to_string(),
buf,
)
}
}
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for $ty {
fn encode_by_ref(
&self,
buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
<String as sqlx::Encode<'q, sqlx::Postgres>>::encode_by_ref(
&self.key().to_string(),
buf,
)
}
}
};
}
impl_string_newtype!(FileField);
impl_string_newtype!(ImageField);