diff --git a/Cargo.toml b/Cargo.toml index ff9df4a..3f6e3eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,6 @@ serde_json = "^1.0" url = "^2.2" uuid = { version = "^1.0", features = ["serde"] } reqwest = "~0.9" + +[dependencies.log] +version = "0.4" diff --git a/src/models/add_favorite_request.rs b/src/models/add_favorite_request.rs index 5e7bf3d..3075752 100644 --- a/src/models/add_favorite_request.rs +++ b/src/models/add_favorite_request.rs @@ -18,11 +18,11 @@ pub struct AddFavoriteRequest { pub favorite_id: String, /// Tags indicate which group this favorite belongs to. Adding multiple groups makes it show up in all. Removing it from one in that case removes it from all. #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, } impl AddFavoriteRequest { - pub fn new(r#type: crate::models::FavoriteType, favorite_id: String, tags: Vec) -> AddFavoriteRequest { + pub fn new(r#type: crate::models::FavoriteType, favorite_id: String, tags: Vec) -> AddFavoriteRequest { AddFavoriteRequest { r#type, favorite_id, diff --git a/src/models/avatar.rs b/src/models/avatar.rs index d0c3aa3..511ed23 100644 --- a/src/models/avatar.rs +++ b/src/models/avatar.rs @@ -39,7 +39,7 @@ pub struct Avatar { pub release_status: crate::models::ReleaseStatus, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "thumbnailImageUrl")] pub thumbnail_image_url: String, #[serde(rename = "unityPackageUrl")] @@ -56,7 +56,7 @@ pub struct Avatar { impl Avatar { /// - pub fn new(author_id: String, author_name: String, created_at: String, description: String, featured: bool, id: String, image_url: String, name: String, release_status: crate::models::ReleaseStatus, tags: Vec, thumbnail_image_url: String, unity_package_url: String, unity_package_url_object: crate::models::AvatarUnityPackageUrlObject, unity_packages: Vec, updated_at: String, version: i32) -> Avatar { + pub fn new(author_id: String, author_name: String, created_at: String, description: String, featured: bool, id: String, image_url: String, name: String, release_status: crate::models::ReleaseStatus, tags: Vec, thumbnail_image_url: String, unity_package_url: String, unity_package_url_object: crate::models::AvatarUnityPackageUrlObject, unity_packages: Vec, updated_at: String, version: i32) -> Avatar { Avatar { asset_url: None, asset_url_object: None, diff --git a/src/models/current_user.rs b/src/models/current_user.rs index 4da9bdf..8fccf53 100644 --- a/src/models/current_user.rs +++ b/src/models/current_user.rs @@ -44,7 +44,7 @@ pub struct CurrentUser { #[serde(rename = "currentAvatarThumbnailImageUrl")] pub current_avatar_thumbnail_image_url: String, #[serde(rename = "currentAvatarTags")] - pub current_avatar_tags: Vec, + pub current_avatar_tags: Vec, #[serde(rename = "date_joined")] pub date_joined: String, #[serde(rename = "developerType")] @@ -135,7 +135,7 @@ pub struct CurrentUser { #[serde(rename = "steamId")] pub steam_id: String, #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "twoFactorAuthEnabled")] pub two_factor_auth_enabled: bool, #[serde(rename = "twoFactorAuthEnabledDate", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] @@ -152,7 +152,7 @@ pub struct CurrentUser { } impl CurrentUser { - pub fn new(accepted_tos_version: i32, allow_avatar_copying: bool, bio: String, bio_links: Vec, current_avatar: String, current_avatar_asset_url: String, current_avatar_image_url: String, current_avatar_thumbnail_image_url: String, current_avatar_tags: Vec, date_joined: String, developer_type: crate::models::DeveloperType, display_name: String, email_verified: bool, friend_group_names: Vec, friend_key: String, friends: Vec, has_birthday: bool, has_email: bool, has_logged_in_from_client: bool, has_pending_email: bool, home_location: String, id: String, is_friend: bool, last_login: String, last_mobile: Option, last_platform: String, obfuscated_email: String, obfuscated_pending_email: String, oculus_id: String, past_display_names: Vec, profile_pic_override: String, pronouns: String, state: crate::models::UserState, status: crate::models::UserStatus, status_description: String, status_first_time: bool, status_history: Vec, steam_details: serde_json::Value, steam_id: String, tags: Vec, two_factor_auth_enabled: bool, unsubscribe: bool, user_icon: String) -> CurrentUser { + pub fn new(accepted_tos_version: i32, allow_avatar_copying: bool, bio: String, bio_links: Vec, current_avatar: String, current_avatar_asset_url: String, current_avatar_image_url: String, current_avatar_thumbnail_image_url: String, current_avatar_tags: Vec, date_joined: String, developer_type: crate::models::DeveloperType, display_name: String, email_verified: bool, friend_group_names: Vec, friend_key: String, friends: Vec, has_birthday: bool, has_email: bool, has_logged_in_from_client: bool, has_pending_email: bool, home_location: String, id: String, is_friend: bool, last_login: String, last_mobile: Option, last_platform: String, obfuscated_email: String, obfuscated_pending_email: String, oculus_id: String, past_display_names: Vec, profile_pic_override: String, pronouns: String, state: crate::models::UserState, status: crate::models::UserStatus, status_description: String, status_first_time: bool, status_history: Vec, steam_details: serde_json::Value, steam_id: String, tags: Vec, two_factor_auth_enabled: bool, unsubscribe: bool, user_icon: String) -> CurrentUser { CurrentUser { accepted_tos_version, accepted_privacy_version: None, diff --git a/src/models/favorite.rs b/src/models/favorite.rs index ad2fbe6..cfb25ce 100644 --- a/src/models/favorite.rs +++ b/src/models/favorite.rs @@ -19,14 +19,14 @@ pub struct Favorite { pub id: String, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "type")] pub r#type: crate::models::FavoriteType, } impl Favorite { /// - pub fn new(favorite_id: String, id: String, tags: Vec, r#type: crate::models::FavoriteType) -> Favorite { + pub fn new(favorite_id: String, id: String, tags: Vec, r#type: crate::models::FavoriteType) -> Favorite { Favorite { favorite_id, id, diff --git a/src/models/favorite_group.rs b/src/models/favorite_group.rs index b962f4b..40e73f9 100644 --- a/src/models/favorite_group.rs +++ b/src/models/favorite_group.rs @@ -25,7 +25,7 @@ pub struct FavoriteGroup { pub owner_id: String, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "type")] pub r#type: crate::models::FavoriteType, #[serde(rename = "visibility")] @@ -34,7 +34,7 @@ pub struct FavoriteGroup { impl FavoriteGroup { /// - pub fn new(display_name: String, id: String, name: String, owner_display_name: String, owner_id: String, tags: Vec, r#type: crate::models::FavoriteType, visibility: crate::models::FavoriteGroupVisibility) -> FavoriteGroup { + pub fn new(display_name: String, id: String, name: String, owner_display_name: String, owner_id: String, tags: Vec, r#type: crate::models::FavoriteType, visibility: crate::models::FavoriteGroupVisibility) -> FavoriteGroup { FavoriteGroup { display_name, id, diff --git a/src/models/file.rs b/src/models/file.rs index 04b4247..df369cc 100644 --- a/src/models/file.rs +++ b/src/models/file.rs @@ -26,7 +26,7 @@ pub struct File { pub owner_id: String, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, /// #[serde(rename = "versions")] pub versions: Vec, @@ -34,7 +34,7 @@ pub struct File { impl File { /// - pub fn new(extension: String, id: String, mime_type: crate::models::MimeType, name: String, owner_id: String, tags: Vec, versions: Vec) -> File { + pub fn new(extension: String, id: String, mime_type: crate::models::MimeType, name: String, owner_id: String, tags: Vec, versions: Vec) -> File { File { extension, id, diff --git a/src/models/info_push.rs b/src/models/info_push.rs index 627f0f5..1facf3a 100644 --- a/src/models/info_push.rs +++ b/src/models/info_push.rs @@ -22,7 +22,7 @@ pub struct InfoPush { pub priority: i32, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "data")] pub data: Box, /// Unknown usage, MD5 @@ -41,7 +41,7 @@ pub struct InfoPush { impl InfoPush { /// - pub fn new(id: String, is_enabled: bool, release_status: crate::models::ReleaseStatus, priority: i32, tags: Vec, data: crate::models::InfoPushData, hash: String, created_at: String, updated_at: String) -> InfoPush { + pub fn new(id: String, is_enabled: bool, release_status: crate::models::ReleaseStatus, priority: i32, tags: Vec, data: crate::models::InfoPushData, hash: String, created_at: String, updated_at: String) -> InfoPush { InfoPush { id, is_enabled, diff --git a/src/models/instance.rs b/src/models/instance.rs index 76a881b..3d36446 100644 --- a/src/models/instance.rs +++ b/src/models/instance.rs @@ -52,7 +52,7 @@ pub struct Instance { pub short_name: Option>, /// The tags array on Instances usually contain the language tags of the people in the instance. #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "type")] pub r#type: crate::models::InstanceType, /// WorldID be \"offline\" on User profiles if you are not friends with that user. @@ -98,7 +98,7 @@ pub struct Instance { impl Instance { /// * `hidden` field is only present if InstanceType is `hidden` aka \"Friends+\", and is instance creator. * `friends` field is only present if InstanceType is `friends` aka \"Friends\", and is instance creator. * `private` field is only present if InstanceType is `private` aka \"Invite\" or \"Invite+\", and is instance creator. - pub fn new(active: bool, can_request_invite: bool, capacity: i32, client_number: String, full: bool, id: String, instance_id: String, location: String, n_users: i32, name: String, permanent: bool, photon_region: crate::models::Region, platforms: crate::models::InstancePlatforms, region: crate::models::InstanceRegion, secure_name: String, tags: Vec, r#type: crate::models::InstanceType, world_id: String, queue_enabled: bool, queue_size: i32, recommended_capacity: i32, strict: bool, user_count: i32, world: crate::models::World) -> Instance { + pub fn new(active: bool, can_request_invite: bool, capacity: i32, client_number: String, full: bool, id: String, instance_id: String, location: String, n_users: i32, name: String, permanent: bool, photon_region: crate::models::Region, platforms: crate::models::InstancePlatforms, region: crate::models::InstanceRegion, secure_name: String, tags: Vec, r#type: crate::models::InstanceType, world_id: String, queue_enabled: bool, queue_size: i32, recommended_capacity: i32, strict: bool, user_count: i32, world: crate::models::World) -> Instance { Instance { active, can_request_invite, diff --git a/src/models/limited_user.rs b/src/models/limited_user.rs index 22ecbcd..32f991e 100644 --- a/src/models/limited_user.rs +++ b/src/models/limited_user.rs @@ -49,7 +49,7 @@ pub struct LimitedUser { pub status_description: String, /// <- Always empty. #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "userIcon", skip_serializing_if = "Option::is_none")] pub user_icon: Option, /// -| **DEPRECATED:** VRChat API no longer return usernames of other users. [See issue by Tupper for more information](https://github.com/pypy-vrc/VRCX/issues/429). @@ -63,7 +63,7 @@ pub struct LimitedUser { impl LimitedUser { /// - pub fn new(developer_type: crate::models::DeveloperType, display_name: String, id: String, is_friend: bool, last_platform: String, status: crate::models::UserStatus, status_description: String, tags: Vec) -> LimitedUser { + pub fn new(developer_type: crate::models::DeveloperType, display_name: String, id: String, is_friend: bool, last_platform: String, status: crate::models::UserStatus, status_description: String, tags: Vec) -> LimitedUser { LimitedUser { bio: None, bio_links: None, diff --git a/src/models/limited_world.rs b/src/models/limited_world.rs index 6138bcf..99ea723 100644 --- a/src/models/limited_world.rs +++ b/src/models/limited_world.rs @@ -52,7 +52,7 @@ pub struct LimitedWorld { pub release_status: crate::models::ReleaseStatus, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "thumbnailImageUrl")] pub thumbnail_image_url: String, /// @@ -66,7 +66,7 @@ pub struct LimitedWorld { impl LimitedWorld { /// - pub fn new(author_id: String, author_name: String, capacity: i32, created_at: String, favorites: i32, heat: i32, id: String, image_url: String, labs_publication_date: String, name: String, occupants: i32, organization: String, popularity: i32, publication_date: String, release_status: crate::models::ReleaseStatus, tags: Vec, thumbnail_image_url: String, unity_packages: Vec, updated_at: String) -> LimitedWorld { + pub fn new(author_id: String, author_name: String, capacity: i32, created_at: String, favorites: i32, heat: i32, id: String, image_url: String, labs_publication_date: String, name: String, occupants: i32, organization: String, popularity: i32, publication_date: String, release_status: crate::models::ReleaseStatus, tags: Vec, thumbnail_image_url: String, unity_packages: Vec, updated_at: String) -> LimitedWorld { LimitedWorld { author_id, author_name, diff --git a/src/models/mod.rs b/src/models/mod.rs index fb46a5d..d9b1f6a 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -278,3 +278,4 @@ pub mod world_metadata; pub use self::world_metadata::WorldMetadata; pub mod world_publish_status; pub use self::world_publish_status::WorldPublishStatus; +pub mod tags; diff --git a/src/models/tags.rs b/src/models/tags.rs new file mode 100644 index 0000000..c4a1e08 --- /dev/null +++ b/src/models/tags.rs @@ -0,0 +1,462 @@ +use std::fmt::{Display, Formatter}; +use serde::{Deserialize, Serialize}; +macro_rules! impl_into_str { + ($type_from:ty, $type_to:ty) => { +impl From<&$type_from> for $type_to{ + fn from(value: &$type_from) -> Self { + value.get_tag() + } +} +impl From<$type_from> for $type_to{ + fn from(value: $type_from) -> Self { + value.get_tag() + } +} + }; +} +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str")] +pub enum Tags{ + Language(String), + Show(ShowTags), + //Avatar Tags + Content(ContentTags), + Admin(AdminTags), + //Avatar Tags? + Author(AuthorTags), + //Favorite Group Start + Group(String), + World(String), + Avatars(String), + //Favorite Group end + System(SystemTags), +} + +impl Tags{ + pub fn get_tag(&self) -> String { + match self { + Tags::Language(language) => format!("language_{language}"), + Tags::Show(show) => format!("show_{}", show.get_tag()), + Tags::Content(content) => format!("content_{}", content.get_tag()), + Tags::Admin(admin) => format!("admin_{}", admin.get_tag()), + Tags::Author(author) => format!("author_{}", author.get_tag()), + Tags::Group(group) => format!("group_{group}"), + Tags::World(world) => format!("worlds{world}"), + Tags::Avatars(avatar) => format!("avatars{avatar}"), + Tags::System(system) => format!("system_{}", system.get_tag()), + } + } +} + +impl Display for Tags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Language(language) => write!(f, "Language: {language}"), + Self::Show(show) => write!(f, "Show: {show}"), + Self::Content(content) => write!(f, "Content Warning: {content}"), + Self::Admin(admin) => write!(f, "Admin: {admin}"), + Self::Author(author) => write!(f, "Author: {author}"), + Self::Group(i) => write!(f, "User: {i}"), + Self::World(i) => write!(f, "World: {i}"), + Self::Avatars(i) => write!(f, "Avatars: {i}"), + Self::System(system) => write!(f, "System: {system}"), + } + } +} + +impl<'a> core::convert::TryFrom<&'a str> for Tags{ + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + if let Some(lang) = value.strip_prefix("language_"){ + Ok(Self::Language(lang.to_string())) + } else if let Some(system) = value.strip_prefix("system_"){ + Ok(Self::System(SystemTags::try_from(system)?)) + } else if let Some(show) = value.strip_prefix("show_"){ + Ok(Self::Show(ShowTags::try_from(show)?)) + } else if let Some(show) = value.strip_prefix("content_"){ + Ok(Self::Content(ContentTags::try_from(show)?)) + }else if let Some(show) = value.strip_prefix("admin_"){ + Ok(Self::Admin(AdminTags::try_from(show)?)) + }else if let Some(show) = value.strip_prefix("author_"){ + Ok(Self::Author(AuthorTags::try_from(show)?)) + }else if let Some(group) = value.strip_prefix("group_"){ + Ok(Self::Group(group.to_string())) + }else if let Some(world) = value.strip_prefix("worlds"){ + Ok(Self::World(world.to_string())) + }else if let Some(avatar) = value.strip_prefix("avatars"){ + Ok(Self::Avatars(avatar.to_string())) + } else { + log::error!("NEW UNKNOWN TAG: {}", value); + Err(value) + } + } +} +impl_into_str!(Tags, String); + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str")] +pub enum ShowTags{ + SocialRank, +} + +impl ShowTags{ + pub const fn get_tag(&self) -> &'static str { + match self { + Self::SocialRank => "social_rank", + } + } +} + +impl Display for ShowTags{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self{ + Self::SocialRank => write!(f, "Show Social Rank"), + } + } +} +impl<'a> core::convert::TryFrom<&'a str> for ShowTags{ + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + match value { + "social_rank" => Ok(Self::SocialRank), + other => { + log::error!("NEW UNKNOWN TAG: show_{}", value); + Err(other) + } + } + } +} +impl_into_str!(ShowTags, &'static str); + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str")] +pub enum ContentTags{ + Sex, + Adult, + Violence, + Gore, + Horror, + Other, +} + +impl ContentTags{ + pub const fn get_tag(&self) -> &'static str { + match self { + Self::Sex => "sex", + Self::Adult => "adult", + Self::Violence => "violence", + Self::Gore => "gore", + Self::Horror => "horror", + Self::Other => "other", + } + } +} + +impl Display for ContentTags{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self{ + Self::Sex => write!(f, "Sexually Suggestive"), + Self::Adult => write!(f, "Adult Language and Themes"), + Self::Violence => write!(f, "Graphic Violence"), + Self::Gore => write!(f, "Excessive Gore"), + Self::Horror => write!(f, "Extreme Horror"), + Self::Other => write!(f, "Other Content Warning"), + } + } +} +impl<'a> core::convert::TryFrom<&'a str> for ContentTags{ + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + match value { + "sex" => Ok(Self::Sex), + "adult" => Ok(Self::Adult), + "violence" => Ok(Self::Violence), + "gore" => Ok(Self::Gore), + "horror" => Ok(Self::Horror), + "other" => Ok(Self::Other), + other => { + log::error!("NEW UNKNOWN TAG: content_{}", value); + Err(other) + } + } + } +} +impl_into_str!(ContentTags, &'static str); + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str")] +pub enum AdminTags{ + ContentReviewed, + QuestFallbackExtended, + Featured(FeaturedTags), +} + +impl AdminTags{ + pub fn get_tag(&self) -> String { + match self { + Self::ContentReviewed => String::from("content_reviewed"), + Self::QuestFallbackExtended => String::from("quest_fallback_extended"), + Self::Featured(feature) => format!("featured_{}", feature.get_tag()), + } + } +} + +impl<'a> core::convert::TryFrom<&'a str> for AdminTags { + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + if let Some(featured) = value.strip_prefix("featured_"){ + Ok(Self::Featured(FeaturedTags::try_from(featured)?)) + } else{ + match value { + "content_reviewed" => Ok(Self::ContentReviewed), + "quest_fallback_extended" => Ok(Self::QuestFallbackExtended), + other => { + log::error!("NEW UNKNOWN TAG: admin_{other}"); + Err(other) + } + } + } + } +} +impl_into_str!(AdminTags, String); + +impl Display for AdminTags{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self{ + Self::ContentReviewed => write!(f, "Content Reviewed"), + Self::QuestFallbackExtended => write!(f, "Extended Quest Fallback"), + Self::Featured(featured) => write!(f, "Featured: {featured}"), + } + } +} + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str")] +pub enum FeaturedTags { + LegacyContent, + Quest, +} + +impl FeaturedTags{ + pub const fn get_tag(&self) -> &'static str { + match self { + Self::LegacyContent => "legacy", + Self::Quest => "quest", + } + } +} + +impl<'a> core::convert::TryFrom<&'a str> for FeaturedTags{ + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + match value { + "legacy" => Ok(Self::LegacyContent), + "quest" => Ok(Self::Quest), + other => { + log::error!("NEW UNKNOWN TAG: admin_featured_{value}"); + Err(other) + } + } + } +} +impl_into_str!(FeaturedTags, &'static str); + +impl Display for FeaturedTags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::LegacyContent => write!(f, "Legacy Content"), + Self::Quest => write!(f, "Quest"), + } + } +} + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str")] +pub enum AuthorTags { + QuestFallback, +} +impl AuthorTags{ + pub const fn get_tag(&self) -> &'static str { + match self { + Self::QuestFallback => "quest_fallback", + } + } +} +impl<'a> core::convert::TryFrom<&'a str> for AuthorTags { + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + match value { + "quest_fallback" => Ok(Self::QuestFallback), + other => { + log::error!("NEW UNKNOWN TAG: author_{value}"); + Err(other) + } + } + } +} +impl_into_str!(AuthorTags, &'static str); + +impl Display for AuthorTags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::QuestFallback => write!(f, "Quest Fallback"), + } + } +} + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str", into = "String")] +pub enum SystemTags { + Access(SystemAccess), + Trust(SystemTrust), + Jam(String), + EarlyAdopter, + Supporter, + NoCaptcha, +} +impl SystemTags{ + pub fn get_tag(&self) -> String { + match self { + Self::Access(access) => format!("{}_access", access.get_tag()), + Self::Trust(trust) => format!("trust_{}", trust.get_tag()), + Self::Jam(id) => format!("jam{id}"), + Self::EarlyAdopter => "early_adopter".to_string(), + Self::Supporter => "supporter".to_string(), + Self::NoCaptcha => "no_captcha".to_string(), + } + } +} +impl Display for SystemTags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Access(access) => write!(f, "{access}"), + Self::Trust(trust) => write!(f, "Trust: {trust}"), + Self::Jam(jam) => write!(f, "Jam: {jam}"), + Self::EarlyAdopter => write!(f, "EarlyAdopter"), + Self::Supporter => write!(f, "Supporter"), + Self::NoCaptcha => write!(f, "NoCaptcha"), + } + } +} + +impl<'a> core::convert::TryFrom<&'a str> for SystemTags{ + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + if let Some(access) = value.strip_suffix("_access") { + Ok(Self::Access(SystemAccess::try_from(access)?)) + } else if let Some(trust) = value.strip_prefix("trust_") { + Ok(Self::Trust(SystemTrust::try_from(trust)?)) + }else if let Some(jam) = value.strip_prefix("jam") { + Ok(Self::Jam(jam.to_string())) + } else { + match value{ + "early_adopter" => Ok(Self::EarlyAdopter), + "supporter" => Ok(Self::Supporter), + "no_captcha" => Ok(Self::NoCaptcha), + value=> { + log::error!("NEW UNKNOWN TAG: system_{}", value); + Err(value) + }, + } + } + } +} +impl_into_str!(SystemTags, String); + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str", into = "&str")] +pub enum SystemTrust { + Veteran, + Trusted, + Known, + Basic, + //Visitors have no Trust tag +} +impl SystemTrust{ + pub const fn get_tag(&self) -> &'static str { + match self { + Self::Basic => "basic", + Self::Known => "known", + Self::Trusted => "trusted", + Self::Veteran => "veteran", + } + } +} +impl Display for SystemTrust { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Basic => write!(f, "New User"), + Self::Known => write!(f, "User"), + Self::Trusted => write!(f, "Known User"), + Self::Veteran => write!(f, "Trusted User"), + } + } +} + +impl<'a> core::convert::TryFrom<&'a str> for SystemTrust { + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + match value { + "basic" => Ok(Self::Basic), + "known" => Ok(Self::Known), + "trusted" => Ok(Self::Trusted), + "veteran" => Ok(Self::Veteran), + other => { + log::error!("NEW UNKNOWN TAG: system_trust_{}", value); + Err(other) + } + } + } +} +impl_into_str!(SystemTrust, &'static str); + +#[non_exhaustive] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] +#[serde(try_from = "&str", into = "&str")] +pub enum SystemAccess { + Avatar, + World, + Feedback, +} +impl SystemAccess{ + pub const fn get_tag(&self) -> &'static str { + match self { + Self::Avatar => "avatar", + Self::World => "world", + Self::Feedback => "feedback", + } + } +} +impl Display for SystemAccess { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Avatar => write!(f, "Avatar Access"), + Self::World => write!(f, "World Access"), + Self::Feedback => write!(f, "Feedback Access"), + } + } +} + +impl<'a> core::convert::TryFrom<&'a str> for SystemAccess{ + type Error = &'a str; + fn try_from(value: &'a str) -> Result { + match value { + "avatar" => Ok(Self::Avatar), + "world" => Ok(Self::World), + "feedback" => Ok(Self::Feedback), + other => { + log::error!("NEW UNKNOWN TAG: system_{}_access", value); + Err(other) + } + } + } +} +impl_into_str!(SystemAccess, &'static str); \ No newline at end of file diff --git a/src/models/user.rs b/src/models/user.rs index f19ac53..09315ac 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -27,7 +27,7 @@ pub struct User { #[serde(rename = "currentAvatarThumbnailImageUrl")] pub current_avatar_thumbnail_image_url: String, #[serde(rename = "currentAvatarTags")] - pub current_avatar_tags: Vec, + pub current_avatar_tags: Vec, #[serde(rename = "date_joined")] pub date_joined: String, #[serde(rename = "developerType")] @@ -74,7 +74,7 @@ pub struct User { pub status_description: String, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "travelingToInstance", skip_serializing_if = "Option::is_none")] pub traveling_to_instance: Option, #[serde(rename = "travelingToLocation", skip_serializing_if = "Option::is_none")] @@ -92,7 +92,7 @@ pub struct User { } impl User { - pub fn new(allow_avatar_copying: bool, bio: String, bio_links: Vec, current_avatar_image_url: String, current_avatar_thumbnail_image_url: String, current_avatar_tags: Vec, date_joined: String, developer_type: crate::models::DeveloperType, display_name: String, friend_key: String, id: String, is_friend: bool, last_activity: String, last_login: String, last_platform: String, profile_pic_override: String, pronouns: String, state: crate::models::UserState, status: crate::models::UserStatus, status_description: String, tags: Vec, user_icon: String) -> User { + pub fn new(allow_avatar_copying: bool, bio: String, bio_links: Vec, current_avatar_image_url: String, current_avatar_thumbnail_image_url: String, current_avatar_tags: Vec, date_joined: String, developer_type: crate::models::DeveloperType, display_name: String, friend_key: String, id: String, is_friend: bool, last_activity: String, last_login: String, last_platform: String, profile_pic_override: String, pronouns: String, state: crate::models::UserState, status: crate::models::UserStatus, status_description: String, tags: Vec, user_icon: String) -> User { User { allow_avatar_copying, badges: None, diff --git a/src/models/world.rs b/src/models/world.rs index 7788792..550da5b 100644 --- a/src/models/world.rs +++ b/src/models/world.rs @@ -66,7 +66,7 @@ pub struct World { pub release_status: crate::models::ReleaseStatus, /// #[serde(rename = "tags")] - pub tags: Vec, + pub tags: Vec, #[serde(rename = "thumbnailImageUrl")] pub thumbnail_image_url: String, /// Empty if unauthenticated. @@ -84,7 +84,7 @@ pub struct World { impl World { /// - pub fn new(author_id: String, author_name: String, capacity: i32, recommended_capacity: i32, created_at: String, description: String, featured: bool, heat: i32, id: String, image_url: String, labs_publication_date: String, name: String, organization: String, popularity: i32, publication_date: String, release_status: crate::models::ReleaseStatus, tags: Vec, thumbnail_image_url: String, updated_at: String, version: i32, visits: i32) -> World { + pub fn new(author_id: String, author_name: String, capacity: i32, recommended_capacity: i32, created_at: String, description: String, featured: bool, heat: i32, id: String, image_url: String, labs_publication_date: String, name: String, organization: String, popularity: i32, publication_date: String, release_status: crate::models::ReleaseStatus, tags: Vec, thumbnail_image_url: String, updated_at: String, version: i32, visits: i32) -> World { World { author_id, author_name,