eternalfest_core 0.18.2

Core crate for Eternalfest
Documentation
use crate::core::Instant;
use crate::digest::{DigestSha2, DigestSha3};
#[cfg(feature = "serde")]
use crate::serde_buffer::{buffer_to_hex, hex_to_buffer};
use async_trait::async_trait;
use auto_impl::auto_impl;
use etwin_core::types::AnyError;
use etwin_core::{declare_new_string, declare_new_uuid};
#[cfg(feature = "serde")]
use etwin_serde_tools::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::ops::Range;

declare_new_uuid! {
  pub struct BlobId(Uuid);
  pub type ParseError = BlobIdParseError;
  const SQL_NAME = "blob_id";
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "Blob"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlobIdRef {
  pub id: BlobId,
}

impl BlobIdRef {
  pub const fn new(id: BlobId) -> Self {
    Self { id }
  }
}

impl From<BlobId> for BlobIdRef {
  fn from(id: BlobId) -> Self {
    Self::new(id)
  }
}

declare_new_uuid! {
  pub struct UploadSessionId(Uuid);
  pub type ParseError = UploadSessionIdParseError;
  const SQL_NAME = "upload_session_id";
}

declare_new_string! {
  pub struct MediaType(String);
  pub type ParseError = MediaTypeParseError;
  const PATTERN = r"^[0-9a-z.-]{1,100}/[0-9a-z.-]{1,100}$";
  const SQL_NAME = "media_type";
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "Blob"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Blob {
  pub id: BlobId,
  pub media_type: MediaType,
  pub byte_size: u32,
  pub digest: BlobDigest,
}

impl Blob {
  pub const fn as_ref(&self) -> BlobIdRef {
    BlobIdRef::new(self.id)
  }
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlobDigest {
  pub sha2_256: DigestSha2,
  pub sha3_256: DigestSha3,
}

impl BlobDigest {
  pub fn digest(data: &[u8]) -> Self {
    Self {
      sha2_256: DigestSha2::digest(data),
      sha3_256: DigestSha3::digest(data),
    }
  }
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct UploadSession {
  pub id: UploadSessionId,
  pub expires_at: Instant,
  pub remaining_range: Range<u32>,
  pub blob: Option<Blob>,
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CreateBlobOptions {
  pub media_type: MediaType,
  #[cfg_attr(
    feature = "serde",
    serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")
  )]
  pub data: Vec<u8>,
}

#[derive(Debug, thiserror::Error)]
pub enum CreateBlobError {
  #[error("Blob size exceeds maximum allowed size")]
  MaxSize,
  #[error(transparent)]
  Other(#[from] AnyError),
}

impl CreateBlobError {
  pub fn other<E>(e: E) -> Self
  where
    E: ::std::error::Error + Send + Sync + 'static,
  {
    CreateBlobError::Other(Box::new(e))
  }
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GetBlobOptions {
  pub id: BlobId,
}

#[derive(Debug, thiserror::Error)]
pub enum GetBlobError {
  #[error("Failed to find blob: {:?}", .0)]
  NotFound(BlobId),
  #[error(transparent)]
  Other(#[from] AnyError),
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GetBlobsOptions {
  pub id: HashSet<BlobId>,
  pub now: Instant,
  pub time: Option<Instant>,
}

#[derive(Debug, thiserror::Error)]
pub enum GetBlobsError {
  #[error(transparent)]
  Other(#[from] AnyError),
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GetBlobDataOptions {
  pub id: BlobId,
}

#[derive(Debug, thiserror::Error)]
pub enum GetBlobDataError {
  #[error("Failed to find blob: {:?}", .0)]
  NotFound(BlobId),
  #[error(transparent)]
  Other(#[from] AnyError),
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CreateUploadSessionOptions {
  pub media_type: MediaType,
  pub byte_size: u32,
}

#[derive(Debug, thiserror::Error)]
pub enum CreateUploadSessionError {
  #[error("Upload session blob size exceeds maximum")]
  MaxSize,
  #[error(transparent)]
  Other(#[from] AnyError),
}

impl CreateUploadSessionError {
  pub fn other<E>(e: E) -> Self
  where
    E: ::std::error::Error + Send + Sync + 'static,
  {
    Self::Other(Box::new(e))
  }
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UploadOptions {
  pub upload_session_id: UploadSessionId,
  pub offset: u32,
  #[cfg_attr(
    feature = "serde",
    serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")
  )]
  pub data: Vec<u8>,
}

#[derive(Debug, thiserror::Error)]
pub enum UploadError {
  #[error("Failed to find upload session for id: {:?}", .0)]
  NotFound(UploadSessionId),
  #[error("Upload session expired: {:?}", .0)]
  Expired(UploadSessionId),
  #[error("Upload session expected data from offset {} but received from offset {}", .expected, .actual)]
  BadOffset { actual: u32, expected: u32 },
  #[error("Upload session tried to write past the reserved size")]
  Overflow,
  #[error("Upload session tried to upload an empty chunk")]
  EmptyInputData,
  #[error(transparent)]
  Other(#[from] AnyError),
}

impl UploadError {
  pub fn other<E>(e: E) -> Self
  where
    E: ::std::error::Error + Send + Sync + 'static,
  {
    Self::Other(Box::new(e))
  }
}

#[async_trait]
#[auto_impl(&, Arc)]
pub trait BlobStore: Send + Sync {
  fn has_immutable_blobs(&self) -> bool;

  async fn create_blob(&self, options: &CreateBlobOptions) -> Result<Blob, CreateBlobError>;

  async fn get_blob(&self, options: &GetBlobOptions) -> Result<Blob, GetBlobError>;

  async fn get_blobs(
    &self,
    options: &GetBlobsOptions,
  ) -> Result<HashMap<BlobId, Result<Blob, GetBlobError>>, GetBlobsError>;

  async fn get_blob_data(&self, options: &GetBlobDataOptions) -> Result<Vec<u8>, GetBlobDataError>;

  async fn create_upload_session(
    &self,
    options: &CreateUploadSessionOptions,
  ) -> Result<UploadSession, CreateUploadSessionError>;

  async fn upload(&self, options: &UploadOptions) -> Result<UploadSession, UploadError>;
}