use std::{fmt, str::FromStr}; use eyre::OptionExt; use serde::{Deserialize, Serialize}; use uuid::Uuid; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize)] #[serde(rename_all = "snake_case")] pub enum UploadKind { Thumbnail, Screenshot, File, } impl UploadKind { pub const fn as_str(self) -> &'static str { match self { Self::Thumbnail => "t", Self::Screenshot => "s", Self::File => "f", } } } impl FromStr for UploadKind { type Err = eyre::Report; fn from_str(s: &str) -> Result { Ok(match s { "t" => Self::Thumbnail, "s" => Self::Screenshot, "f" => Self::File, _ => eyre::bail!("wrong upload kind"), }) } } #[derive(Debug)] pub struct UploadId { pub novel: String, pub id: Uuid, pub kind: UploadKind, } impl UploadId { pub fn to_file_name(&self) -> String { format!("{}-{}", self.id, self.kind.as_str()) } } impl FromStr for UploadId { type Err = eyre::Report; fn from_str(s: &str) -> Result { let Some((novel, id_and_kind)) = s.rsplit_once('/') else { eyre::bail!("invalid upload id") }; if id_and_kind.is_empty() { eyre::bail!("invalid upload id"); } let (id, kind) = id_and_kind.split_at(id_and_kind.len().checked_sub(1).ok_or_eyre("loh")?); Ok(Self { novel: novel.to_owned(), kind: kind.parse()?, id: id.parse()?, }) } } impl fmt::Display for UploadId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}/{}{}", self.novel, self.id, self.kind.as_str()) } } impl Serialize for UploadId { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_str(&self.to_string()) } } impl<'de> Deserialize<'de> for UploadId { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let s = <&str>::deserialize(deserializer)?; s.parse().map_err(|e| serde::de::Error::custom(e)) } }