use serde::{ de::{self, Visitor}, Deserialize, Deserializer, }; use thiserror::Error; #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct UpResp { pub files: Vec, } #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct CatsResp { pub categories: Vec, } #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct UsersResp { pub users: Vec, } #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct MemesResp { pub memes: Vec, } #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Category { pub id: String, pub name: String, } #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Meme { pub id: String, pub link: String, pub category: String, pub user: String, #[serde(deserialize_with = "deserialize_timestamp")] pub timestamp: i64, pub ipfs: Option, } impl Meme { pub fn file_name(&self) -> Result<&str, MemeFileNameError> { let fname = self.link.split('/').last().ok_or(MemeFileNameError)?; // in a valid URL with a filename, there'll always be at least 4 slashes first. if fname.is_empty() || self.link.split('/').count() < 4 { return Err(MemeFileNameError); } Ok(fname) } } #[derive(Debug, Error, PartialEq, Eq)] #[error("Failed to get file name. Server response invalid!")] pub struct MemeFileNameError; #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct User { pub name: String, pub id: Option, pub tokenhash: Option, pub userdir: Option, pub dayuploads: u16, } impl User { pub fn get_id(&self) -> Option<&String> { self.id .as_ref() .or(self.tokenhash.as_ref()) .or(self.userdir.as_ref()) } } fn deserialize_timestamp<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { struct Vis; impl<'de> Visitor<'de> for Vis { type Value = i64; fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { fmt.write_str( "A timestamp in the form of a string or i64 (because tilera doesn't know integers \ exist)", ) } fn visit_i64(self, v: i64) -> Result where E: de::Error, { Ok(v) } fn visit_borrowed_str(self, v: &'de str) -> Result where E: de::Error, { v.parse() .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(v), &self)) } } deserializer.deserialize_any(Vis) } #[derive(Deserialize, Debug, PartialEq, Eq, Clone)] pub struct RandomResp { pub meme: Meme, } #[cfg(test)] mod tests { use super::*; fn meme_with_link(link: &str) -> Meme { Meme { id: "0".to_string(), link: link.to_string(), category: "test".to_string(), user: "test".to_string(), timestamp: 123, ipfs: None, } } #[test] fn test_meme_file_name() { assert!(meme_with_link("https://").file_name().is_err()); assert!(meme_with_link("https://test").file_name().is_err()); assert_eq!( meme_with_link("https://test/some_meme").file_name(), Ok("some_meme"), ); } }