use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumString, FromRepr};
use crate::{Paging, endpoint};
pub const URL: &str = "https://avatar.roblox.com/v1";
pub type ColorId = u8;
#[repr(u8)]
#[derive(
Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Display, EnumString, FromRepr,
)]
pub enum AvatarType {
R6 = 1,
R15 = 2,
}
#[repr(u8)]
#[derive(
Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Display, EnumString, FromRepr,
)]
pub enum MorphAvatarType {
MorphR6 = 1,
MorphR15 = 2,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AvatarScales {
pub height: f32,
pub width: f32,
pub head: f32,
pub depth: f32,
pub proportion: f32,
pub body_type: f32,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct BodyColors {
#[serde(rename = "headColorId")]
pub head: ColorId,
#[serde(rename = "torsoColorId")]
pub torso: ColorId,
#[serde(rename = "rightArmColorId")]
pub right_arm: ColorId,
#[serde(rename = "leftArmColorId")]
pub left_arm: ColorId,
#[serde(rename = "rightLegColorId")]
pub right_leg: ColorId,
#[serde(rename = "leftLegColorId")]
pub left_leg: ColorId,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct AssetType {
pub id: u8,
pub name: String,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct AssetMeta {
pub version: u8,
pub order: u16,
pub puffiness: Option<f32>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Asset {
pub id: u64,
pub name: String,
#[serde(rename = "assetType")]
pub kind: AssetType,
pub current_version_id: u64,
pub meta: Option<AssetMeta>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Emote {
#[serde(rename = "assetId")]
pub id: u64,
#[serde(rename = "assetName")]
pub name: String,
pub position: u8,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Outfit {
pub id: u64,
pub name: String,
pub is_editable: bool,
pub outfit_type: Option<()>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AvatarResponse {
#[serde(rename = "playerAvatarType")]
pub kind: AvatarType,
pub assets: Vec<Asset>,
pub scales: AvatarScales,
pub body_colors: BodyColors,
pub default_pants_applied: bool,
pub default_shirt_applied: bool,
pub emotes: Vec<Emote>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OutfitsResponse {
#[serde(rename = "data")]
pub outfits: Vec<Outfit>,
pub total: u64,
pub filtered_count: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OutfitDetails {
pub id: u64,
pub name: String,
pub universe_id: u64,
pub assets: Vec<Asset>,
pub body_colors: BodyColors,
#[serde(rename = "scale")]
pub scales: AvatarScales,
#[serde(rename = "playerAvatarType")]
pub avatar_type: AvatarType,
pub outfit_type: String,
pub is_editable: bool,
pub moderation_status: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UniverseAvatarSettings {
#[serde(rename = "gameAvatarType")]
pub avatar_type: MorphAvatarType,
pub avatar_body_type: String,
pub avatar_collision_type: String,
pub joint_positioning_type: String,
pub avatar_min_scales: AvatarScales,
pub avatar_max_scales: AvatarScales,
pub avatar_asset_overrides: Vec<Option<()>>,
pub message: String,
pub moderation_status: Option<String>,
pub allow_custom_animations: String,
}
endpoint! {
user_avatar(id: u64) -> AvatarResponse {
GET "{URL}/users/{id}/avatar";
}
user_currently_wearing(id: u64) -> Vec<u64> {
GET "{URL}/users/{id}/currently-wearing";
types {
Response {
ids("assetIds"): Vec<u64>,
}
}
map |r: Response| r.ids
}
avatar_set_wearing_assets(assets: Vec<u64>) -> bool {
POST "{URL}/avatar/set-wearing-assets";
types {
Request<'a> {
asset_ids("assetIds"): &'a [u64],
}
Response {
success: bool,
}
}
body_serialize {
&Request { asset_ids: &assets }
}
map |r: Response| r.success
}
avatar_set_type(kind: AvatarType) -> bool {
POST "{URL}/avatar/set-player-avatar-type";
types {
Request {
avatar_type("playerAvatarType"): AvatarType,
}
Response {
success: bool,
}
}
body_serialize {
&Request { avatar_type: kind }
}
map |r: Response| r.success
}
avatar_set_body_colors(colors: BodyColors) -> bool {
POST "{URL}/avatar/set-body-colors";
types {
Response {
success: bool,
}
}
body_serialize {
&colors
}
map |r: Response| r.success
}
avatar_set_scales(scales: AvatarScales) -> bool {
POST "{URL}/avatar/set-scales";
types {
Response {
success: bool,
}
}
body_serialize {
&scales
}
map |r: Response| r.success
}
user_outfits(
id: u64, paging: Paging<'_>, is_editable: Option<bool>
) -> OutfitsResponse {
GET "{URL}/users/{id}/outfits";
prelude {
let limit = paging.limit.unwrap_or(25).to_string();
let cursor = paging.cursor.unwrap_or("1");
let is_editable = match is_editable {
Some(editable) => editable.to_string(),
None => String::new(),
};
}
query {
"page" => cursor,
"itemsPerPage" => &limit,
"isEditable" => &is_editable,
}
}
outfit_details(id: u64) -> OutfitDetails {
GET "{URL}/outfits/{id}/details";
}
remove_outfit(id: u64) -> bool {
POST "{URL}/outfits/{id}/delete";
types {
Response {
success: bool,
}
}
map |r: Response| r.success
}
universe_avatar_settings(id: u64) -> UniverseAvatarSettings {
GET "{URL}/users/{id}/avatar";
}
}