use std::path::PathBuf; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::models::profiles::types::{DbActorProfile, ProfileUpdateData}; use crate::utils::files::{FileError, save_validated_b64_file, get_file_url}; /// https://docs.joinmastodon.org/entities/source/ #[derive(Serialize)] pub struct Source { pub note: Option, } /// https://docs.joinmastodon.org/entities/account/ #[derive(Serialize)] pub struct Account { pub id: Uuid, pub username: String, pub acct: String, pub display_name: Option, pub created_at: DateTime, pub note: Option, pub avatar: Option, pub header: Option, pub followers_count: i32, pub following_count: i32, pub statuses_count: i32, pub source: Option, } impl Account { pub fn from_profile(profile: DbActorProfile, instance_url: &str) -> Self { let avatar_url = profile.avatar_file_name.map(|name| get_file_url(instance_url, &name)); let header_url = profile.banner_file_name.map(|name| get_file_url(instance_url, &name)); let source = if profile.actor_json.is_some() { // Remote actor None } else { let source = Source { note: profile.bio_source }; Some(source) }; Self { id: profile.id, username: profile.username, acct: profile.acct, display_name: profile.display_name, created_at: profile.created_at, note: profile.bio, avatar: avatar_url, header: header_url, followers_count: profile.follower_count, following_count: profile.following_count, statuses_count: profile.post_count, source, } } } /// https://docs.joinmastodon.org/methods/accounts/ #[derive(Deserialize)] pub struct AccountUpdateData { pub display_name: Option, pub note: Option, pub note_source: Option, pub avatar: Option, pub header: Option, } fn process_b64_image_field_value( form_value: Option, db_value: Option, output_dir: &PathBuf, ) -> Result, FileError> { let maybe_file_name = match form_value { Some(b64_data) => { if b64_data == "" { // Remove file None } else { // Decode and save file let (file_name, _) = save_validated_b64_file( &b64_data, &output_dir, "image/", )?; Some(file_name) } }, // Keep current value None => db_value, }; Ok(maybe_file_name) } impl AccountUpdateData { pub fn into_profile_data( self, current_avatar: &Option, current_banner: &Option, media_dir: &PathBuf, ) -> Result { let avatar = process_b64_image_field_value( self.avatar, current_avatar.clone(), media_dir, )?; let banner = process_b64_image_field_value( self.header, current_banner.clone(), media_dir, )?; let profile_data = ProfileUpdateData { display_name: self.display_name, bio: self.note, bio_source: self.note_source, avatar, banner, }; Ok(profile_data) } }