From e46b85875863f837b8794de89c63e9874d7d260f Mon Sep 17 00:00:00 2001 From: Scott Lyons Date: Wed, 14 Aug 2024 09:09:28 -0700 Subject: [PATCH] refactor(models): saving some changes in prep of python library --- szurubooru-client/src/models.rs | 866 ++++++++++++++++++++++++++++++-- 1 file changed, 837 insertions(+), 29 deletions(-) diff --git a/szurubooru-client/src/models.rs b/szurubooru-client/src/models.rs index fc318b4..c5183a0 100644 --- a/szurubooru-client/src/models.rs +++ b/szurubooru-client/src/models.rs @@ -5,12 +5,18 @@ //! See [here](https://github.com/rr-/szurubooru/blob/master/doc/API.md#field-selecting) for //! more information. +use crate::errors::SzurubooruClientError; use chrono::{DateTime, Utc}; use derive_builder::Builder; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use strum_macros::AsRefStr; +#[cfg(feature = "python")] +use pyo3::{exceptions::PyValueError, prelude::*, types::*}; +#[cfg(feature = "python")] +use serde_pyobject::to_pyobject; + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] /// Enum used to represent something that's either `Left` or `Right` @@ -82,6 +88,7 @@ impl WithBaseURL for Vec { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] /// A [tag resource](TagResource) stripped down to `names`, `category` and `usages` fields. pub struct MicroTagResource { /// The tag names and aliases @@ -116,8 +123,9 @@ pub struct ResourceVersion { pub version: u32, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] /// A single tag. Tags are used to let users search for posts. pub struct TagResource { /// resource version. See [versioning](ResourceVersion) @@ -161,7 +169,10 @@ pub struct TagResource { /// .expect("A new tag"); /// ``` #[derive(Debug, Clone, Serialize, Deserialize, Builder, Default)] -#[builder(setter(strip_option))] +//#[builder(pattern="owned")] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[builder(setter(strip_option), build_fn(error = "SzurubooruClientError"))] pub struct CreateUpdateTag { #[serde(skip_serializing_if = "Option::is_none")] #[builder(default)] @@ -189,7 +200,83 @@ pub struct CreateUpdateTag { pub suggestions: Option>, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateTag { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdateTagBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateTagBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_category")] + pub fn category_py(mut slf: PyRefMut<'_, Self>, cat: String) -> PyResult> { + slf.category(cat); + Ok(slf) + } + + #[pyo3(name = "with_names")] + pub fn names_py<'py>( + mut slf: PyRefMut<'py, Self>, + names: Vec, + ) -> PyResult> { + slf.names(names); + Ok(slf) + } + + #[pyo3(name = "with_description")] + pub fn description_py( + mut slf: PyRefMut<'_, Self>, + desc: String, + ) -> PyResult> { + slf.description(desc); + Ok(slf) + } + + #[pyo3(name = "with_implications")] + pub fn implications_py( + mut slf: PyRefMut<'_, Self>, + implications: Vec, + ) -> PyResult> { + slf.implications(implications); + Ok(slf) + } + + #[pyo3(name = "with_suggestions")] + pub fn suggestions_py( + mut slf: PyRefMut<'_, Self>, + suggestions: Vec, + ) -> PyResult> { + slf.suggestions(suggestions); + Ok(slf) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] /// A single tag category. The primary purpose of tag categories is to distinguish certain tag /// types (such as characters, media type etc.), which improves user experience. pub struct TagCategoryResource { @@ -208,7 +295,9 @@ pub struct TagCategoryResource { } #[derive(Debug, Clone, Serialize, Deserialize, Default, Builder)] -#[builder(setter(strip_option))] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[builder(setter(strip_option), build_fn(error = "SzurubooruClientError"))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] /// Used for creating or updating a Tag Category pub struct CreateUpdateTagCategory { /// Resource version. See [versioning](ResourceVersion) @@ -229,23 +318,137 @@ pub struct CreateUpdateTagCategory { pub order: Option, } +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateTagCategory { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdateTagCategoryBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateTagCategoryBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_name")] + pub fn name_py(mut slf: PyRefMut<'_, Self>, name: String) -> PyResult> { + slf.name(name); + Ok(slf) + } + + #[pyo3(name = "with_color")] + pub fn color_py(mut slf: PyRefMut<'_, Self>, color: String) -> PyResult> { + slf.color(color); + Ok(slf) + } + + #[pyo3(name = "with_order")] + pub fn order_py(mut slf: PyRefMut<'_, Self>, order: u32) -> PyResult> { + slf.order(order); + Ok(slf) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, Builder)] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] -#[builder(setter(into))] /// Removes source tag and merges all of its usages, suggestions and implications to the target tag. /// Other tag properties such as category and aliases do not get transferred and are discarded. pub struct MergeTags { /// Version of the tag to remove - pub remove_version: u32, + #[serde(rename = "removeVersion")] + #[cfg(feature = "python")] + pub remove_tag_version: u32, /// The name of the tag to remove - pub remove: String, + #[serde(rename = "remove")] + pub remove_tag: String, /// The version of the tag to merge TO pub merge_to_version: u32, /// The name of the tag to merge TO - pub merge_to: String, + #[serde(rename = "mergeTo")] + pub merge_to_tag: String, +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl MergeTags { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(MergeTagsBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl MergeTagsBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + + #[pyo3(name = "with_remove_tag_version")] + pub fn remove_tag_version_py( + mut slf: PyRefMut<'_, Self>, + v: u32, + ) -> PyResult> { + slf.remove_tag_version(v); + Ok(slf) + } + + #[pyo3(name = "with_remove_tag")] + pub fn remove_tag_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.remove_tag(v); + Ok(slf) + } + + #[pyo3(name = "with_merge_tag_version")] + pub fn merge_to_version_py( + mut slf: PyRefMut<'_, Self>, + v: u32, + ) -> PyResult> { + slf.merge_to_version(v); + Ok(slf) + } + + #[pyo3(name = "with_merge_to_tag")] + pub fn merge_to_tag_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.merge_to_tag(v); + Ok(slf) + } } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] /// Lists siblings of given tag, e.g. tags that were used in the same posts as the given tag pub struct TagSibling { /// The related tag @@ -255,6 +458,7 @@ pub struct TagSibling { } #[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] #[serde(rename_all = "camelCase")] /// The type of post pub enum PostType { @@ -277,6 +481,7 @@ pub enum PostType { } #[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] #[serde(rename_all = "camelCase")] /// How SFW/NSFW the post is pub enum PostSafety { @@ -291,6 +496,7 @@ pub enum PostSafety { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A post resource stripped down to `id` and `thumbnailUrl` fields. pub struct MicroPostResource { @@ -320,6 +526,7 @@ pub(crate) struct PostId { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A post resource pub struct PostResource { @@ -429,7 +636,9 @@ impl WithBaseURL for PostResource { } #[derive(Debug, Clone, Serialize, Deserialize, Builder)] -#[builder(setter(strip_option))] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[builder(setter(strip_option), build_fn(error = "SzurubooruClientError"))] #[serde(rename_all = "camelCase")] /// A `struct` used to create or update a post. For updating purposes /// the [version](CreateUpdatePost::version) field is required @@ -474,8 +683,108 @@ pub struct CreateUpdatePost { pub content_token: Option, } +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdatePost { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdatePostBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdatePostBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_tags")] + pub fn tags_py(mut slf: PyRefMut<'_, Self>, tags: Vec) -> PyResult> { + slf.tags(tags); + Ok(slf) + } + + #[pyo3(name = "with_safety")] + pub fn safety_py( + mut slf: PyRefMut<'_, Self>, + safety: PostSafety, + ) -> PyResult> { + slf.safety(safety); + Ok(slf) + } + + #[pyo3(name = "with_source")] + pub fn source_py(mut slf: PyRefMut<'_, Self>, source: String) -> PyResult> { + slf.source(source); + Ok(slf) + } + + #[pyo3(name = "with_relations")] + pub fn relations_py( + mut slf: PyRefMut<'_, Self>, + relations: Vec, + ) -> PyResult> { + slf.relations(relations); + Ok(slf) + } + + #[pyo3(name = "with_notes")] + pub fn notes_py( + mut slf: PyRefMut<'_, Self>, + notes: Vec, + ) -> PyResult> { + slf.notes(notes); + Ok(slf) + } + + #[pyo3(name = "with_flags")] + pub fn flags_py( + mut slf: PyRefMut<'_, Self>, + flags: Vec, + ) -> PyResult> { + slf.flags(flags); + Ok(slf) + } + + #[pyo3(name = "with_content_url")] + pub fn content_url_py( + mut slf: PyRefMut<'_, Self>, + val: String, + ) -> PyResult> { + slf.content_url(val); + Ok(slf) + } + + #[pyo3(name = "with_content_token")] + pub fn content_token_py( + mut slf: PyRefMut<'_, Self>, + val: String, + ) -> PyResult> { + slf.content_token(val); + Ok(slf) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] /// A token representing a temporary file upload pub struct TemporaryFileUpload { /// Temporary upload token @@ -483,6 +792,8 @@ pub struct TemporaryFileUpload { } #[derive(Debug, Clone, Serialize, Deserialize, Builder)] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[builder(setter(into))] #[serde(rename_all = "camelCase")] /// Removes source post and merges all of its tags, relations, scores, favorites and comments to @@ -492,15 +803,85 @@ pub struct TemporaryFileUpload { /// and are discarded. pub struct MergePost { /// The version of the post to remove - pub remove_version: u32, + #[serde(rename = "removeVersion")] + pub remove_post_version: u32, /// The ID of the post to remove - pub remove: u32, + #[serde(rename = "remove")] + pub remove_post: u32, /// The version of the post to merge TO pub merge_to_version: u32, /// The post ID of the post to merge TO - pub merge_to: u32, + #[serde(rename = "mergeTo")] + pub merge_to_post: u32, /// Whether to replace the content - pub replace_content: bool, + #[serde(rename = "replaceContent")] + pub replace_post_content: bool, +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl MergePost { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(MergePostBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl MergePostBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + + #[pyo3(name = "with_remove_post_version")] + pub fn remove_post_version_py( + mut slf: PyRefMut<'_, Self>, + v: u32, + ) -> PyResult> { + slf.remove_post_version(v); + Ok(slf) + } + + #[pyo3(name = "with_remove_post")] + pub fn remove_post_py(mut slf: PyRefMut<'_, Self>, v: u32) -> PyResult> { + slf.remove_post(v); + Ok(slf) + } + + #[pyo3(name = "with_merge_to_version")] + pub fn merge_to_version_py( + mut slf: PyRefMut<'_, Self>, + v: u32, + ) -> PyResult> { + slf.merge_to_version(v); + Ok(slf) + } + + #[pyo3(name = "with_merge_to_post")] + pub fn merge_to_post_py(mut slf: PyRefMut<'_, Self>, v: u32) -> PyResult> { + slf.merge_to_post(v); + Ok(slf) + } + + #[pyo3(name = "with_replace_post_content")] + pub fn with_replace_post_content_py( + mut slf: PyRefMut<'_, Self>, + v: bool, + ) -> PyResult> { + slf.replace_post_content(v); + Ok(slf) + } } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -510,6 +891,7 @@ pub struct RateResource { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A text annotation rendered on top of the post pub struct NoteResource { @@ -522,7 +904,8 @@ pub struct NoteResource { pub text: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] #[serde(rename_all = "camelCase")] /// The Rank of a given User pub enum UserRank { @@ -538,7 +921,8 @@ pub enum UserRank { Administrator, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] #[serde(rename_all = "camelCase")] /// The kind of User Avatar pub enum UserAvatarStyle { @@ -549,32 +933,54 @@ pub enum UserAvatarStyle { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass)] #[serde(rename_all = "camelCase")] /// A single user pub struct UserResource { + #[cfg(feature = "python")] + #[pyo3(get)] /// Resource version. See [versioning](ResourceVersion) pub version: Option, + #[cfg(feature = "python")] + #[pyo3(get)] /// The user's username pub name: Option, /// The user email. It is available only if the request is authenticated by the same user, /// or the authenticated user can change the email. If it's unavailable, the server returns /// `false`. If the user hasn't specified an email, the server returns [None](Option::None) pub email: Option>, + #[cfg(feature = "python")] + #[pyo3(get)] /// The user rank, which effectively affects their privileges pub rank: Option, + #[cfg(feature = "python")] + #[pyo3(get)] #[serde(rename = "last-login-time")] /// The last login time pub last_login_time: Option>, + #[cfg(feature = "python")] + #[pyo3(get)] #[serde(rename = "creation-time")] /// The user registration time pub creation_time: Option>, + #[cfg(feature = "python")] + #[pyo3(get)] /// How to render the user avatar pub avatar_style: Option, + #[cfg(feature = "python")] + #[pyo3(get)] /// The URL to the avatar pub avatar_url: Option, + #[cfg(not(feature = "python"))] + /// The URL to the avatar + pub avatar_url: Option, + #[cfg(feature = "python")] + #[pyo3(get)] /// Number of comments #[serde(rename = "comment-count")] pub comment_count: Option, + #[cfg(feature = "python")] + #[pyo3(get)] /// Number of uploaded posts #[serde(rename = "uploaded-post-count")] pub uploaded_post_count: Option, @@ -591,6 +997,50 @@ pub struct UserResource { pub favorite_post_count: Option>, } +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl UserResource { + #[getter] + #[pyo3(name = "email")] + pub fn email_py(&self) -> PyResult> { + match &self.email { + None => Ok(None), + Some(SzuruEither::Left(s)) => Ok(Some(s.to_string())), + Some(SzuruEither::Right(_)) => Ok(None), + } + } + + #[getter] + #[pyo3(name = "liked_post_count")] + pub fn liked_post_count_py(&self) -> PyResult> { + match &self.liked_post_count { + None => Ok(None), + Some(SzuruEither::Left(s)) => Ok(Some(*s)), + Some(SzuruEither::Right(_)) => Ok(None), + } + } + + #[getter] + #[pyo3(name = "disliked_post_count")] + pub fn disliked_post_count_py(&self) -> PyResult> { + match &self.disliked_post_count { + None => Ok(None), + Some(SzuruEither::Left(s)) => Ok(Some(*s)), + Some(SzuruEither::Right(_)) => Ok(None), + } + } + + #[getter] + #[pyo3(name = "favorite_post_count")] + pub fn favorite_post_count_py(&self) -> PyResult> { + match &self.favorite_post_count { + None => Ok(None), + Some(SzuruEither::Left(s)) => Ok(Some(*s)), + Some(SzuruEither::Right(_)) => Ok(None), + } + } +} + impl WithBaseURL for UserResource { fn with_base_url(self, url: &str) -> Self { let av_url = self.avatar_url.map(|au| { @@ -608,7 +1058,9 @@ impl WithBaseURL for UserResource { } #[derive(Debug, Clone, Serialize, Deserialize, Default, Builder)] -#[builder(setter(strip_option))] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[builder(setter(strip_option), build_fn(error = "SzurubooruClientError"))] #[serde(rename_all = "camelCase")] /// `struct` used to create or update a user resource. The version field is only used when /// updating an existing resource @@ -636,7 +1088,68 @@ pub struct CreateUpdateUser { pub avatar_style: Option, } +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateUser { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdateUserBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateUserBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_name")] + pub fn name_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.name(v); + Ok(slf) + } + + #[pyo3(name = "with_password")] + pub fn password_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.password(v); + Ok(slf) + } + + #[pyo3(name = "with_rank")] + pub fn rank_py(mut slf: PyRefMut<'_, Self>, v: UserRank) -> PyResult> { + slf.rank(v); + Ok(slf) + } + + #[pyo3(name = "with_avatar_style")] + pub fn avatar_style_py( + mut slf: PyRefMut<'_, Self>, + v: UserAvatarStyle, + ) -> PyResult> { + // Should we accept types other than UserAvatarStyle? + slf.avatar_style(v); + Ok(slf) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A user resource stripped down to `name` and `avatarUrl` fields pub struct MicroUserResource { @@ -660,6 +1173,7 @@ impl WithBaseURL for MicroUserResource { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "kebab-case")] /// A single user token pub struct UserAuthTokenResource { @@ -693,7 +1207,9 @@ impl WithBaseURL for UserAuthTokenResource { } #[derive(Debug, Clone, Serialize, Deserialize, Builder, Default)] -#[builder(setter(into, strip_option))] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[builder(setter(into, strip_option), build_fn(error = "SzurubooruClientError"))] #[serde(rename_all = "kebab-case")] /// `struct` to create or update a UserAuthToken. `version` is only required when updating an /// existing resource @@ -716,6 +1232,59 @@ pub struct CreateUpdateUserAuthToken { pub expiration_time: Option>, } +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateUserAuthToken { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdateUserAuthTokenBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateUserAuthTokenBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_enabled")] + pub fn enabled_py(mut slf: PyRefMut<'_, Self>, v: bool) -> PyResult> { + slf.enabled(v); + Ok(slf) + } + + #[pyo3(name = "with_note")] + pub fn note_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.note(v); + Ok(slf) + } + + #[pyo3(name = "with_expiration_time")] + pub fn password_py( + mut slf: PyRefMut<'_, Self>, + v: DateTime, + ) -> PyResult> { + slf.expiration_time(v); + Ok(slf) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[doc(hidden)] @@ -725,6 +1294,7 @@ pub struct PasswordResetToken { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// Type that represents a new temporary password pub struct TemporaryPassword { @@ -733,6 +1303,7 @@ pub struct TemporaryPassword { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// Simple server configuration pub struct GlobalInfoConfig { @@ -757,6 +1328,7 @@ pub struct GlobalInfoConfig { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// Simple server statistics pub struct GlobalInfo { @@ -776,7 +1348,8 @@ pub struct GlobalInfo { pub config: GlobalInfoConfig, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A single pool category. The primary purpose of pool categories is to distinguish certain pool /// types (such as series, relations etc.), which improves user experience. @@ -794,7 +1367,9 @@ pub struct PoolCategoryResource { } #[derive(Debug, Clone, Serialize, Deserialize, Builder)] -#[builder(setter(strip_option))] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[builder(setter(strip_option), build_fn(error = "SzurubooruClientError"))] /// `struct` used for creating or updating a pool category. This type uses a Builder pattern like /// so: /// @@ -822,7 +1397,52 @@ pub struct CreateUpdatePoolCategory { pub color: Option, } +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdatePoolCategory { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdatePoolCategoryBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdatePoolCategoryBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_name")] + pub fn name_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.name(v); + Ok(slf) + } + + #[pyo3(name = "with_color")] + pub fn color_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.color(v); + Ok(slf) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// Type that represents a Pool resource pub struct PoolResource { @@ -857,7 +1477,9 @@ impl WithBaseURL for PoolResource { } #[derive(Debug, Clone, Serialize, Deserialize, Builder, Default)] -#[builder(setter(strip_option))] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[builder(setter(strip_option), build_fn(error = "SzurubooruClientError"))] #[serde(rename_all = "camelCase")] /// This type is used when creating or updating a pool object. It uses the builder pattern like so: /// @@ -896,7 +1518,65 @@ pub struct CreateUpdatePool { pub posts: Option>, } +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdatePool { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdatePoolBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdatePoolBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_names")] + pub fn names_py(mut slf: PyRefMut<'_, Self>, v: Vec) -> PyResult> { + slf.names(v); + Ok(slf) + } + + #[pyo3(name = "with_category")] + pub fn category_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.category(v); + Ok(slf) + } + + #[pyo3(name = "with_description")] + pub fn description_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.description(v); + Ok(slf) + } + + #[pyo3(name = "with_posts")] + pub fn posts_py(mut slf: PyRefMut<'_, Self>, v: Vec) -> PyResult> { + slf.posts(v); + Ok(slf) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, Builder, Default)] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// This type is used to specify which pools should be merged. Uses the builder pattern like so: /// @@ -913,16 +1593,76 @@ pub struct CreateUpdatePool { /// ``` pub struct MergePool { /// Version of the pool to remove. Must match the current Pool version - pub remove_version: u32, + #[serde(rename = "removePool")] + pub remove_pool_version: u32, /// Pool ID to remove - pub remove: u32, + #[serde(rename = "remove")] + pub remove_pool: u32, /// Version of the pool to merge TO pub merge_to_version: u32, /// Pool ID of the pool to merge TO - pub merge_to: u32, + #[serde(rename = "mergeTo")] + pub merge_to_pool: u32, +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl MergePool { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(MergePoolBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl MergePoolBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + #[pyo3(name = "with_remove_pool_version")] + pub fn remove_pool_version_py( + mut slf: PyRefMut<'_, Self>, + v: u32, + ) -> PyResult> { + slf.remove_pool_version(v); + Ok(slf) + } + + #[pyo3(name = "with_remove_pool")] + pub fn remove_pool_py(mut slf: PyRefMut<'_, Self>, v: u32) -> PyResult> { + slf.remove_pool(v); + Ok(slf) + } + + #[pyo3(name = "with_merge_to_version")] + pub fn merge_to_version_py( + mut slf: PyRefMut<'_, Self>, + v: u32, + ) -> PyResult> { + slf.merge_to_version(v); + Ok(slf) + } + + #[pyo3(name = "with_merge_to_pool")] + pub fn merge_to_pool_py(mut slf: PyRefMut<'_, Self>, v: u32) -> PyResult> { + slf.merge_to_pool(v); + Ok(slf) + } } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A micro resource representing a Pool. A subset of the fields of a [PoolResource]. pub struct MicroPoolResource { @@ -939,6 +1679,7 @@ pub struct MicroPoolResource { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A type representing a Comment on a post pub struct CommentResource { @@ -963,7 +1704,9 @@ pub struct CommentResource { } #[derive(Debug, Clone, Serialize, Deserialize, Builder, Default)] -#[builder(setter(strip_option))] +#[cfg_attr(all(feature = "python"), builder_struct_attr(pyclass))] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[builder(setter(strip_option), build_fn(error = "SzurubooruClientError"))] #[serde(rename_all = "camelCase")] /// This type is used when creating or updating a comment. This type uses the builder pattern like /// so: @@ -992,7 +1735,52 @@ pub struct CreateUpdateComment { pub post_id: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateComment { + #[pyo3(name = "builder")] + #[staticmethod] + pub fn builder_py() -> PyResult { + Ok(CreateUpdateCommentBuilder::default()) + } +} + +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl CreateUpdateCommentBuilder { + #[new] + pub fn new() -> PyResult { + Ok(Self::default()) + } + + #[pyo3(name = "build")] + pub fn build_py(&self) -> PyResult { + match self.build() { + Ok(cutag) => Ok(cutag), + Err(e) => Err(PyErr::new::(e.to_string())), + } + } + #[pyo3(name = "with_version")] + pub fn version_py(mut slf: PyRefMut<'_, Self>, version: u32) -> PyResult> { + slf.version(version); + Ok(slf) + } + + #[pyo3(name = "with_text")] + pub fn with_text_py(mut slf: PyRefMut<'_, Self>, v: String) -> PyResult> { + slf.text(v); + Ok(slf) + } + + #[pyo3(name = "with_post_id")] + pub fn post_id_py(mut slf: PyRefMut<'_, Self>, v: u32) -> PyResult> { + slf.post_id(v); + Ok(slf) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] #[serde(rename_all = "camelCase")] /// The kind of snapshot that has been recorded pub enum SnapshotOperationType { @@ -1006,7 +1794,8 @@ pub enum SnapshotOperationType { Merged, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] #[serde(rename_all = "camelCase")] /// The kind of resource described by this snapshot pub enum SnapshotResourceType { @@ -1024,7 +1813,8 @@ pub enum SnapshotResourceType { PoolCategory, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq))] #[serde(rename_all = "camelCase", untagged)] /// Data for a resource that was created #[allow(clippy::large_enum_variant)] @@ -1055,12 +1845,15 @@ impl WithBaseURL for SnapshotCreationDeletionData { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass)] #[serde(rename_all = "camelCase")] /// Data for a modified resource pub struct SnapshotModificationData { /// The type of snapshot #[serde(rename = "type")] + #[cfg(feature = "python")] + #[pyo3(get)] pub snapshot_type: String, /// The JSON value for the modified resource. A dictionary diff that depends on the resource /// kind. @@ -1070,7 +1863,18 @@ pub struct SnapshotModificationData { pub value: serde_json::Value, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg(feature = "python")] +#[cfg_attr(all(feature = "python"), pymethods)] +impl SnapshotModificationData { + #[getter] + pub fn get_value(&self, py: Python<'_>) -> PyResult> { + let obj = to_pyobject(py, &self.value).unwrap().unbind(); + Ok(obj) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[cfg_attr(all(feature = "python"), pyclass(eq))] #[serde(untagged)] /// Type representing the data as part of a snapshot #[allow(clippy::large_enum_variant)] @@ -1095,6 +1899,7 @@ impl WithBaseURL for SnapshotData { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// Overall type representing some sort of change to a resource pub struct SnapshotResource { @@ -1124,6 +1929,7 @@ impl WithBaseURL for SnapshotResource { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A result when searching for similar posts to a given image pub struct ImageSearchSimilarPost { @@ -1143,6 +1949,7 @@ impl WithBaseURL for ImageSearchSimilarPost { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] #[serde(rename_all = "camelCase")] /// A type to represent the result from an Image search request pub struct ImageSearchResult { @@ -1164,6 +1971,7 @@ impl WithBaseURL for ImageSearchResult { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(all(feature = "python"), pyclass(get_all))] /// A type that represents posts that are before or after an existing post pub struct AroundPostResult { /// A previous post, if it exists