diff --git a/szurubooru-client/src/client.rs b/szurubooru-client/src/client.rs index 342c0eb..e1a83d4 100644 --- a/szurubooru-client/src/client.rs +++ b/szurubooru-client/src/client.rs @@ -128,10 +128,10 @@ impl SzurubooruClient { /// Construct a new request using the existing client auth and base URL /// All requests start with the [SzurubooruClient] struct. - /// The (request)[SzurubooruClient::request], - /// (with_fields)[SzurubooruClient::fields], - /// (limit)[SzurubooruClient::limit] and - /// (offset)[SzurubooruClient::offset] methods all return a [SzurubooruRequest] struct that will + /// The [request](crate::SzurubooruClient::request), + /// [with_fields](crate::SzurubooruClient::with_fields), + /// [with_limit](crate::SzurubooruClient::with_limit) and + /// [with_offset](crate::SzurubooruClient::with_offset) methods all return a [SzurubooruRequest] struct that will /// enable you to actually make the requests. /// ```no_run /// # use szurubooru_client::SzurubooruClient; @@ -149,19 +149,19 @@ impl SzurubooruClient { /// Construct a new request while selecting only the given fields /// The Szurubooru API supports selecting a subset of fields for a given resource. - /// Most resource (models)[szurubooru_client::models] have [Option] fields because of that. + /// Most resource [models](crate::models) have [Option] fields because of that. /// The default is to return all fields for a given resource. /// See [here](https://github.com/rr-/szurubooru/blob/master/doc/API.md#field-selecting) for /// more details /// /// For example, to select only the `version`, `id` and `content_url` fields of a - /// (PostResource)[szurubooru_client::models::PostResource] + /// [PostResource] /// ```no_run /// # use szurubooru_client::SzurubooruClient; /// # #[allow(unused)] /// # async { /// let client = SzurubooruClient::new_with_token("http://localhost:5001", "myuser", "sz-123456", true).unwrap(); - /// let new_request = client.request().with_fields(vec!["version", "id", "content_url"]); + /// let new_request = client.request().with_fields(vec!["version".to_string(), "id".to_string(), "content_url".to_string()]); /// # }; /// # () /// ``` @@ -169,7 +169,7 @@ impl SzurubooruClient { self.request().with_fields(fields) } - /// The same as (with_fields)[SzurubooruClient::with_fields], but accepts an Option type instead + /// The same as [with_fields](SzurubooruClient::with_fields), but accepts an Option type instead pub fn with_optional_fields(&self, fields: Option>) -> SzurubooruRequest { self.request().with_optional_fields(fields) } @@ -178,7 +178,7 @@ impl SzurubooruClient { /// The Szurubooru API supports limiting the number of resources returned for Paginated /// API endpoints. /// - /// For example, to limit the number of pools returned by (list_pools)[SzurubooruRequest::list_pools] + /// For example, to limit the number of pools returned by [list_pools](SzurubooruRequest::list_pools) /// ```no_run /// # use szurubooru_client::SzurubooruClient; /// # #[allow(unused)] @@ -195,7 +195,7 @@ impl SzurubooruClient { self.request().with_limit(limit) } - /// The same as (with_limit)[SzurubooruClient::with_limit], but accepts an Option type instead + /// The same as [with_limit](SzurubooruClient::with_limit), but accepts an Option type instead pub fn with_optional_limit(&self, limit: Option) -> SzurubooruRequest { self.request().with_optional_limit(limit) } @@ -205,7 +205,7 @@ impl SzurubooruClient { /// endpoints. Use this offset in combination with the limit to page through /// large result sets. /// - /// For example, to offset the list of pools returned by (list_pools)[SzurubooruRequest::list_pools] + /// For example, to offset the list of pools returned by [list_pools](SzurubooruRequest::list_pools) /// ```no_run /// # use szurubooru_client::SzurubooruClient; /// # #[allow(unused)] @@ -222,7 +222,7 @@ impl SzurubooruClient { self.request().with_offset(offset) } - /// The same as (with_offset)[SzurubooruClient::with_offset], but accepts an Option type instead + /// The same as [with_offset](SzurubooruClient::with_offset), but accepts an Option type instead pub fn with_optional_offset(&self, offset: Option) -> SzurubooruRequest { self.request().with_optional_offset(offset) } @@ -231,9 +231,13 @@ impl SzurubooruClient { #[derive(Debug)] /// A type that represents a single Szurubooru request. pub struct SzurubooruRequest<'a> { - fields: Option>, - limit: Option, - offset: Option, + /// The currently selected fields to return (if applicable) + pub fields: Option>, + /// The maximum number of resources to return (if supported by the API endpoint) + pub limit: Option, + /// The number of resource to skip before returning any results + /// (if supported by the API endpoint) + pub offset: Option, client: &'a SzurubooruClient, } @@ -249,19 +253,19 @@ impl<'a> SzurubooruRequest<'a> { /// Select which fields to return from the query. /// The Szurubooru API supports selecting a subset of fields for a given resource. - /// Most resource (models)[szurubooru_client::models] have [Option] fields because of that. + /// Most resource [models](crate::models) have [Option] fields because of that. /// The default is to return all fields for a given resource. /// See [here](https://github.com/rr-/szurubooru/blob/master/doc/API.md#field-selecting) for /// more details /// /// For example, to select only the `version`, `id` and `content_url` fields of a - /// (PostResource)[szurubooru_client::models::PostResource] + /// [PostResource] /// ```no_run /// # use szurubooru_client::SzurubooruClient; /// # #[allow(unused)] /// # async { /// let client = SzurubooruClient::new_with_token("http://localhost:5001", "myuser", "sz-123456", true).unwrap(); - /// let new_request = client.request().with_fields(vec!["version", "id", "content_url"]); + /// let new_request = client.request().with_fields(vec!["version".to_string(), "id".to_string(), "content_url".to_string()]); /// # }; /// # () /// ``` @@ -270,8 +274,8 @@ impl<'a> SzurubooruRequest<'a> { self } - /// The same as (with_fields)[SzurubooruRequest::with_fields], but accepts an Option type instead - pub fn with_optional_fields(mut self, val: Option>) -> Self { + /// The same as [with_fields](SzurubooruRequest::with_fields), but accepts an Option type instead + pub fn with_optional_fields(self, val: Option>) -> Self { match val { Some(f) => self.with_fields(f), None => self, @@ -282,7 +286,7 @@ impl<'a> SzurubooruRequest<'a> { /// The Szurubooru API supports limiting the number of resources returned for Paginated /// API endpoints. /// - /// For example, to limit the number of pools returned by (list_pools)[SzurubooruRequest::list_pools] + /// For example, to limit the number of pools returned by [list_pools](SzurubooruRequest::list_pools) /// ```no_run /// # use szurubooru_client::SzurubooruClient; /// # #[allow(unused)] @@ -300,8 +304,8 @@ impl<'a> SzurubooruRequest<'a> { self } - /// The same as (with_limit)[SzurubooruRequest::with_limit], but accepts an Option type instead - pub fn with_optional_limit(mut self, val: Option) -> Self { + /// The same as [with_limit](SzurubooruRequest::with_limit), but accepts an Option type instead + pub fn with_optional_limit(self, val: Option) -> Self { match val { Some(f) => self.with_limit(f), None => self, @@ -313,7 +317,7 @@ impl<'a> SzurubooruRequest<'a> { /// endpoints. Use this offset in combination with the limit to page through /// large result sets. /// - /// For example, to offset the list of pools returned by (list_pools)[SzurubooruRequest::list_pools] + /// For example, to offset the list of pools returned by [list_pools](SzurubooruRequest::list_pools) /// ```no_run /// # use szurubooru_client::SzurubooruClient; /// # #[allow(unused)] @@ -331,7 +335,7 @@ impl<'a> SzurubooruRequest<'a> { self } - /// The same as (with_offset)[SzurubooruRequest::with_offset], but accepts an Option type instead + /// The same as [with_offset](SzurubooruRequest::with_offset), but accepts an Option type instead pub fn with_optional_offset(self, val: Option) -> Self { match val { Some(f) => self.with_offset(f), @@ -427,7 +431,7 @@ impl<'a> SzurubooruRequest<'a> { .map_err(SzurubooruClientError::RequestError)?; let server_error = serde_json::from_str::(&resp_json) - .map_err(|e| SzurubooruClientError::ResponseError(status, resp_json))?; + .map_err(|_e| SzurubooruClientError::ResponseError(status, resp_json))?; Err(SzurubooruClientError::SzurubooruServerError(server_error)) } else { Ok(response) @@ -438,7 +442,7 @@ impl<'a> SzurubooruRequest<'a> { &self, request: RequestBuilder, ) -> SzurubooruResult { - let mut request = request + let request = request .build() .map_err(SzurubooruClientError::RequestBuilderError)?; @@ -487,7 +491,7 @@ impl<'a> SzurubooruRequest<'a> { /// Updates an existing tag category using specified parameters. Name must match /// `tag_category_name_regex` from server's configuration. All fields except - /// [version](models::TagCategoryResource::version) are optional - update concerns only provided fields. + /// [version](crate::models::TagCategoryResource::version) are optional - update concerns only provided fields. pub async fn update_tag_category( &self, name: T, @@ -535,8 +539,9 @@ impl<'a> SzurubooruRequest<'a> { } /// Searches for tags. - /// See the (named tokens)[tokens::TagNamedToken] and (sort tokens)[tokens::TagSortToken] for - /// all possible query tokens, or use (QueryToken)[tokens::QueryToken] for a custom token + /// See the [named tokens](crate::tokens::TagNamedToken) and + /// [sort tokens](crate::tokens::TagSortToken) for all possible query tokens, or use + /// [QueryToken] for a custom token pub async fn list_tags( &self, query: Option<&Vec>, @@ -547,7 +552,7 @@ impl<'a> SzurubooruRequest<'a> { /// Creates a new tag using specified parameters. Names, suggestions and implications must /// match `tag_name_regex` from server's configuration. Category must exist and is the same - /// as the `name` field within (TagCategoryResource)[models::TagCategoryResource] resource. + /// as the `name` field within [TagCategoryResource] resource. /// Suggestions and implications are optional. If specified implied tags or suggested tags do /// not exist yet, they will be automatically created. Tags created automatically have no /// implications, no suggestions, one name and their category is set to the first tag category @@ -559,7 +564,7 @@ impl<'a> SzurubooruRequest<'a> { /// Updates an existing tag using specified parameters. Names, suggestions and implications must /// match `tag_name_regex` from server's configuration. Category must exist and is the same - /// as the `name` field within (TagCategoryResource)[models::TagCategoryResource] resource. + /// as the `name` field within [TagCategoryResource] resource. /// Suggestions and implications are optional. If specified implied tags or suggested tags do /// not exist yet, they will be automatically created. Tags created automatically have no /// implications, no suggestions, one name and their category is set to the first tag category @@ -608,7 +613,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Lists siblings of given tag, e.g. tags that were used in the same posts as the given tag. - /// The (occurrences)[models::TagSibling::occurrences] field signifies how many times a given + /// The [occurrences](crate::models::TagSibling::occurrences) field signifies how many times a given /// sibling appears with given tag. Results are sorted by occurrences count and the list is /// truncated to the first 50 elements. Doesn't use paging. pub async fn get_tag_siblings( @@ -624,9 +629,8 @@ impl<'a> SzurubooruRequest<'a> { } /// Searches for posts. - /// See (PostNamedToken)[tokens::PostNamedToken], (PostSortToken)[tokens::PostSortToken] and - /// (PostSpecialToken)[tokens::PostSpecialToken] for valid tokens to use with this method, or - /// use (QueryToken)[tokens::QueryToken] to construct a custom token + /// See [PostNamedToken], [PostSortToken] and [PostSpecialToken] for valid tokens to use with + /// this method, or use [QueryToken] to construct a custom token pub async fn list_posts( &self, query: Option<&Vec>, @@ -654,7 +658,7 @@ impl<'a> SzurubooruRequest<'a> { /// the image. /// If specified tags do not exist yet, they will be automatically created. Tags created /// automatically have no implications, no suggestions, one name and their category is set to - /// the first tag category found. (safety)[models::CreateUpdatePost::safety] must be any of + /// the first tag category found. [safety](crate::models::CreateUpdatePost::safety) must be any of /// `safe`, `sketchy` or `unsafe`. /// Relations must contain valid post IDs. If `flag` is omitted, they will be defined by /// default (`"loop"` will be set for all video posts, and `"sound"` will be auto-detected). @@ -672,7 +676,7 @@ impl<'a> SzurubooruRequest<'a> { /// Update an existing post /// See [SzurubooruRequest::create_post_from_url] for more details about the fields in - /// (CreateUpdatePost)[models::CreateUpdatePost] + /// [CreateUpdatePost] pub async fn update_post( &self, post_id: u32, @@ -686,7 +690,7 @@ impl<'a> SzurubooruRequest<'a> { /// Update an existing post from a given URL /// See [SzurubooruRequest::create_post_from_url] for more details about the fields in - /// (CreateUpdatePost)[models::CreateUpdatePost] + /// [CreateUpdatePost] pub async fn update_post_from_url( &self, post_id: u32, @@ -748,7 +752,7 @@ impl<'a> SzurubooruRequest<'a> { /// Create a new post from a file handle /// See [SzurubooruRequest::create_post_from_url] for more details about the fields in - /// (CreateUpdatePost)[models::CreateUpdatePost] + /// [CreateUpdatePost] pub async fn create_post_from_file( &self, file: &mut File, @@ -773,7 +777,7 @@ impl<'a> SzurubooruRequest<'a> { /// Create a new post from a file path /// See [SzurubooruRequest::create_post_from_url] for more details about the fields in - /// (CreateUpdatePost)[models::CreateUpdatePost] + /// [CreateUpdatePost] pub async fn create_post_from_file_path( &self, file_path: impl AsRef, @@ -793,7 +797,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Create a post from a token previously generated by - /// (upload_temporary_file_from_path)[SzurubooruRequest::upload_temporary_file_from_path] + /// [upload_temporary_file_from_path](SzurubooruRequest::upload_temporary_file_from_path) pub async fn create_post_from_token( &self, new_post: &CreateUpdatePost, @@ -814,7 +818,7 @@ impl<'a> SzurubooruRequest<'a> { /// Update an existing post from an open File handle /// See [SzurubooruRequest::create_post_from_url] for more details about the fields in - /// (CreateUpdatePost)[models::CreateUpdatePost] + /// [CreateUpdatePost] pub async fn update_post_from_file( &self, post_id: u32, @@ -838,7 +842,7 @@ impl<'a> SzurubooruRequest<'a> { /// Update an existing post from a file path /// See [SzurubooruRequest::create_post_from_url] for more details about the fields in - /// (CreateUpdatePost)[models::CreateUpdatePost] + /// [CreateUpdatePost] pub async fn update_post_from_file_path( &self, post_id: u32, @@ -888,7 +892,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Update a post from a token previously generated by - /// (upload_temporary_file_from_path)[SzurubooruRequest::upload_temporary_file_from_path] + /// [upload_temporary_file_from_path](SzurubooruRequest::upload_temporary_file_from_path) pub async fn update_post_from_token( &self, post_id: u32, @@ -957,7 +961,7 @@ impl<'a> SzurubooruRequest<'a> { .map(|cr| cr.bytes_stream()) } - ///Fetches the given post ID's image as a (Bytes)[bytes::Bytes] struct + ///Fetches the given post ID's image as a [Bytes](bytes::Bytes) struct pub async fn get_image_bytes(&self, post_id: u32) -> SzurubooruResult { let content_response = self.get_post_content(post_id, false).await?; @@ -967,7 +971,7 @@ impl<'a> SzurubooruRequest<'a> { .map_err(SzurubooruClientError::RequestError) } - ///Fetches the given post ID's thumbnail as a (Bytes)[bytes::Bytes] struct + ///Fetches the given post ID's thumbnail as a [Bytes](bytes::Bytes) struct pub async fn get_thumbnail_bytes(&self, post_id: u32) -> SzurubooruResult { let content_response = self.get_post_content(post_id, true).await?; @@ -1090,11 +1094,11 @@ impl<'a> SzurubooruRequest<'a> { let hex_string = hex::encode(hash); let qt = QueryToken::token(PostNamedToken::ContentChecksum, hex_string); - let mut psr = self + let psr = self .list_posts(Some(&vec![qt])) .await .map(|psr| self.propagate_urls(psr))?; - Ok(psr.results.first().map(|pr| pr.clone())) + Ok(psr.results.first().cloned()) } /// Searches for an exact match of a file path based on the SHA1 checksum @@ -1133,7 +1137,7 @@ impl<'a> SzurubooruRequest<'a> { /// /// Removes source post and merges all of its tags, relations, scores, favorites and comments to - /// the target post. If [MergePost::replace_content] is set to `true`, content of the target post + /// the target post. If [MergePost::replace_post_content] is set to `true`, content of the target post /// is replaced using the content of the source post; otherwise it remains unchanged. Source /// post properties such as its safety, source, whether to loop the video and other scalar /// values do not get transferred and are discarded. @@ -1146,7 +1150,7 @@ impl<'a> SzurubooruRequest<'a> { /// Updates score of authenticated user for given post. Valid scores are -1, 0 and 1. pub async fn rate_post(&self, post_id: u32, score: i8) -> SzurubooruResult { - if score < -1 || score > 1 { + if !(-1..=1).contains(&score) { return Err(SzurubooruClientError::ValidationError( "Score must be -1, 0 or 1".to_string(), )); @@ -1175,9 +1179,9 @@ impl<'a> SzurubooruRequest<'a> { } /// Retrieves the post that is currently featured on the main page in web client. If no post is - /// featured, is [Option::None]. Note that this method exists mostly for compatibility - /// with setting featured post - most of the time, you'd want to use query global info which - /// contains more information. + /// featured, the result will be [Option::None]. Note that this method exists mostly for + /// compatibility with setting featured post - most of the time, you'd want to use query global + /// info which contains more information. pub async fn get_featured_post(&self) -> SzurubooruResult> { self.do_request(Method::GET, "/api/featured-post", None, None::<&String>) .await @@ -1213,7 +1217,7 @@ impl<'a> SzurubooruRequest<'a> { /// Updates an existing tag category using specified parameters. Name must match /// `tag_category_name_regex` from server's configuration. All fields except the - /// [version](models::CreateUpdatePoolCategory::version) field are optional - update concerns + /// [version](crate::models::CreateUpdatePoolCategory::version) field are optional - update concerns /// only the provided fields. pub async fn update_pool_category( &self, @@ -1272,7 +1276,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Searches for pools. - /// Anonymous tokens are the same as the [name](tokens::PoolNamedToken::Name) token + /// Anonymous tokens are the same as the [name](crate::tokens::PoolNamedToken::Name) token pub async fn list_pools( &self, query: Option<&Vec>, @@ -1284,8 +1288,8 @@ impl<'a> SzurubooruRequest<'a> { /// Creates a new pool using specified parameters. Names, suggestions and implications must /// match `pool_name_regex` from server's configuration. Category must exist and is the same as - /// [name](models::PoolCategoryResource::name) field. - /// [posts](models::CreateUpdatePool::posts) is an optional list of integer post IDs. If the + /// [name](crate::models::PoolCategoryResource::name) field. + /// [posts](crate::models::CreateUpdatePool::posts) is an optional list of integer post IDs. If the /// specified posts do not exist, an error will be thrown. pub async fn create_pool( &self, @@ -1296,14 +1300,14 @@ impl<'a> SzurubooruRequest<'a> { .map(|r| self.propagate_urls(r)) } - /// Updates an existing pool using specified parameters. [name](models::CreateUpdatePool::name), + /// Updates an existing pool using specified parameters. [names](crate::models::CreateUpdatePool::names), /// must match `pool_name_regex` from server's configuration. - /// [category](models::CreateUpdatePool::category) must exist and is the same as - /// [name](models::PoolCategoryResource::name) field. [posts](models::CreateUpdatePool::posts) + /// [category](crate::models::CreateUpdatePool::category) must exist and is the same as + /// [name](crate::models::PoolCategoryResource::name) field. [posts](crate::models::CreateUpdatePool::posts) /// is an optional list of integer post IDs. If the specified posts do not exist yet, an error /// will be thrown. The full list of post IDs must be provided if they are being updated, and /// the previous list of posts will be replaced with the new one. All fields except - /// [version](models::CreateUpdatePool::version) are optional - update concerns only provided + /// [version](crate::models::CreateUpdatePool::version) are optional - update concerns only provided /// fields. pub async fn update_pool( &self, @@ -1343,7 +1347,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Searches for comments. - /// Anonymous tokens are the same as the [text](tokens::CommentNamedToken::text) token + /// Anonymous tokens are the same as the [text](crate::tokens::CommentNamedToken::Text) token pub async fn list_comments( &self, query: Option<&Vec>, @@ -1394,7 +1398,7 @@ impl<'a> SzurubooruRequest<'a> { comment_id: u32, score: i8, ) -> SzurubooruResult { - if score < -1 || score > 1 { + if !(-1..=1).contains(&score) { return Err(SzurubooruClientError::ValidationError( "Score must be -1, 0 or 1".to_string(), )); @@ -1406,9 +1410,8 @@ impl<'a> SzurubooruRequest<'a> { } /// Searches for users - /// Anonymous tokens are the same as the [name](tokens::UserNamedToken) - /// See [UserNamedToken](tokens::UserNamedToken) and [UserSortToken](tokens::UserSortToken) - /// for type-safe tokens + /// Anonymous tokens are the same as the [name](crate::tokens::UserNamedToken::Name) token + /// See [UserNamedToken] and [UserSortToken] for type-safe tokens pub async fn list_users( &self, query: Option<&Vec>, @@ -1451,7 +1454,7 @@ impl<'a> SzurubooruRequest<'a> { /// Creates a new user using specified parameters. Names and passwords must match /// `user_name_regex` and `password_regex` from server's configuration, respectively. /// Email address, rank and avatar fields are optional. Avatar style can be either - /// [gravatar](models::UserAvatarStyle::Gravatar) or [manual](models::UserAvatarStyle::Manual). + /// [gravatar](crate::models::UserAvatarStyle::Gravatar) or [manual](crate::models::UserAvatarStyle::Manual). /// `manual` avatar style requires client to pass also the `avatar` file. /// If the rank is empty and the user happens to be the first user ever created, /// become an administrator, whereas subsequent users will be given the rank indicated by @@ -1462,7 +1465,7 @@ impl<'a> SzurubooruRequest<'a> { .map(|r| self.propagate_urls(r)) } - /// Create a [UserResource](models::UserResource) with the included Avatar file + /// Create a [UserResource] with the included Avatar file /// See [create_user](SzurubooruRequest::create_user) for other applicable fields and /// restrictions pub async fn create_user_with_avatar_file( @@ -1482,7 +1485,7 @@ impl<'a> SzurubooruRequest<'a> { .map(|r| self.propagate_urls(r)) } - /// Create a [UserResource](models::UserResource) with the included Avatar file path + /// Create a [UserResource] with the included Avatar file path /// See [create_user](SzurubooruRequest::create_user) for other applicable fields and /// restrictions pub async fn create_user_with_avatar_path( @@ -1506,9 +1509,9 @@ impl<'a> SzurubooruRequest<'a> { /// Updates user using specified parameters. Names and passwords must match /// `user_name_regex` and `password_regex` from server's configuration, respectively. /// Email address, rank and avatar fields are optional. Avatar style can be either - /// [gravatar](models::UserAvatarStyle::Gravatar) or [manual](models::UserAvatarStyle::Manual). + /// [gravatar](crate::models::UserAvatarStyle::Gravatar) or [manual](crate::models::UserAvatarStyle::Manual). /// `manual` avatar style requires client to pass also the `avatar` file. - /// All fields except the [version](models::CreateUpdateUser::version) are optional + /// All fields except the [version](crate::models::CreateUpdateUser::version) are optional /// - update concerns only provided fields. pub async fn update_user( &self, @@ -1524,7 +1527,7 @@ impl<'a> SzurubooruRequest<'a> { .map(|r| self.propagate_urls(r)) } - /// Update a [UserResource](models::UserResource) with the included Avatar file + /// Update a [UserResource] with the included Avatar file /// See [update_user](SzurubooruRequest::update_user) for other applicable fields and /// restrictions pub async fn update_user_with_avatar_file( @@ -1549,7 +1552,7 @@ impl<'a> SzurubooruRequest<'a> { .map(|r| self.propagate_urls(r)) } - /// Update a [UserResource](models::UserResource) with the included Avatar file path + /// Update a [UserResource] with the included Avatar file path /// See [update_user](SzurubooruRequest::update_user) for other applicable fields and /// restrictions pub async fn update_user_with_avatar_path( @@ -1629,7 +1632,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Updates an existing user token using specified parameters. All fields except the - /// [version](models::CreateUpdateUserAuthToken::version) are optional - update concerns only + /// [version](crate::models::CreateUpdateUserAuthToken::version) are optional - update concerns only /// provided fields. pub async fn update_user_token( &self, @@ -1647,7 +1650,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Deletes an existing user token using specified parameters. All fields except the - /// [version](models::CreateUpdateUserAuthToken::version) are optional - update concerns only + /// [version](crate::models::CreateUpdateUserAuthToken::version) are optional - update concerns only /// provided fields. pub async fn delete_user_token( &self, @@ -1700,7 +1703,7 @@ impl<'a> SzurubooruRequest<'a> { } /// Lists recent resource snapshots. - /// See [SnapshotNamedToken](tokens::SnapshotNamedToken) for query tokens. + /// See [SnapshotNamedToken] for query tokens. /// There are no sort tokens. The snapshots are always sorted by creation time. pub async fn list_snapshots( &self, @@ -1711,11 +1714,11 @@ impl<'a> SzurubooruRequest<'a> { .map(|r| self.propagate_urls(r)) } - /// Retrieves simple statistics. [featured_post](models::GlobalInfo::featured_post) is - /// [None](Option::None) if there is no featured post yet. - /// [server_time](models::GlobalInfo::server_time) is pretty much the same as the Date HTTP + /// Retrieves simple statistics. [featured_post](crate::models::GlobalInfo::featured_post) is + /// [None] if there is no featured post yet. + /// [server_time](crate::models::GlobalInfo::server_time) is pretty much the same as the Date HTTP /// field, only formatted in a manner consistent with other dates. Values in config key are - /// taken directly from the server config, with the exception of privilege array keys being + /// taken directly from the server config, except for the privilege array keys being /// converted to lower camel case to match the API convention. pub async fn get_global_info(&self) -> SzurubooruResult { self.do_request(Method::GET, "/api/info", None, None::<&String>) diff --git a/szurubooru-client/src/errors.rs b/szurubooru-client/src/errors.rs index 32fc673..97f6353 100644 --- a/szurubooru-client/src/errors.rs +++ b/szurubooru-client/src/errors.rs @@ -57,7 +57,7 @@ pub enum SzurubooruClientError { #[error("JSON Serialization error: {0}")] JSONSerializationError(#[source] serde_json::Error), /// Error when validation fails for one of the Builder types - #[error("Vlidation error: {0}")] + #[error("Validation error: {0}")] ValidationError(String), /// Error occurred when reading a file #[error("IO Error: {0}")] @@ -80,12 +80,17 @@ impl From for SzurubooruClientError { } #[cfg(feature = "python")] -create_exception!(szurubooru_client, SzuruPyClientError, PyException); +create_exception!( + szurubooru_client, + SzuruClientError, + PyException, + "An exception that contains two pieces of information: The error kind and error details" +); #[cfg(feature = "python")] impl std::convert::From for PyErr { fn from(value: SzurubooruClientError) -> Self { - SzuruPyClientError::new_err((value.as_ref().to_string(), value.to_string())) + SzuruClientError::new_err((value.as_ref().to_string(), value.to_string())) } } diff --git a/szurubooru-client/src/lib.rs b/szurubooru-client/src/lib.rs index 20ec374..8b68296 100644 --- a/szurubooru-client/src/lib.rs +++ b/szurubooru-client/src/lib.rs @@ -36,6 +36,7 @@ pub mod models; pub mod tokens; #[cfg(feature = "python")] +#[doc(hidden)] pub mod py; #[cfg(feature = "python")] @@ -49,7 +50,7 @@ mod szurubooru_client { #[pymodule_export] pub use crate::{ - errors::SzuruPyClientError, + errors::SzuruClientError, /*models::{ AroundPostResult, CommentResource, GlobalInfo, ImageSearchResult, ImageSearchSimilarPost, MicroPoolResource, MicroPostResource, MicroTagResource, @@ -66,6 +67,7 @@ mod szurubooru_client { UserNamedToken, UserSortToken, },*/ py::asynchronous::PythonAsyncClient, py::synchronous::PythonSyncClient, + py::PyPagedSearchResult, }; #[pymodule(name = "_tokens")] @@ -77,7 +79,6 @@ mod szurubooru_client { PostSpecialToken, QueryToken, SnapshotNamedToken, TagNamedToken, TagSortToken, UserNamedToken, UserSortToken, }; - use pyo3::prelude::*; } #[pymodule(name = "_models")] diff --git a/szurubooru-client/src/models.rs b/szurubooru-client/src/models.rs index 528b079..a89013e 100644 --- a/szurubooru-client/src/models.rs +++ b/szurubooru-client/src/models.rs @@ -13,7 +13,7 @@ use std::collections::HashMap; use strum_macros::AsRefStr; #[cfg(feature = "python")] -use pyo3::{exceptions::PyValueError, prelude::*, types::*}; +use pyo3::prelude::*; #[cfg(feature = "python")] use serde_pyobject::to_pyobject; @@ -45,16 +45,16 @@ impl WithBaseURL for UnpagedSearchResult { #[derive(Debug, Serialize, Deserialize)] /// A result of search operation that involves paging /// -/// Use (offset)[crate::SzurubooruRequest::offset] and (limit)[crate::SzurubooruRequest::limit] +/// Use [offset](crate::SzurubooruRequest::with_offset) and [limit](crate::SzurubooruRequest::with_limit) /// to fetch the next page pub struct PagedSearchResult { /// The original query for the request pub query: String, - /// The number of [T] to skip forward + /// The number of `T` to skip forward pub offset: u32, - /// The maximum number of [T] to return + /// The maximum number of `T` to return pub limit: u32, - /// The total number of [T] that match the [query](PagedSearchResult::query) + /// The total number of `T` that match the [query](PagedSearchResult::query) pub total: u32, /// The results themselves pub results: Vec, @@ -88,7 +88,10 @@ impl WithBaseURL for Vec { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all, eq))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, eq, module = "szurubooru_client.models") +)] /// A [tag resource](TagResource) stripped down to `names`, `category` and `usages` fields. pub struct MicroTagResource { /// The tag names and aliases @@ -101,7 +104,9 @@ pub struct MicroTagResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl MicroTagResource { + /// Function that generates the representation string for this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -133,7 +138,10 @@ pub struct ResourceVersion { #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] /// A single tag. Tags are used to let users search for posts. pub struct TagResource { /// resource version. See [versioning](ResourceVersion) @@ -162,7 +170,9 @@ pub struct TagResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl TagResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -170,7 +180,7 @@ impl TagResource { /// Creates or updates a tag using specified parameters. Names, suggestions and implications must /// match `tag_name_regex` from server's configuration. Category must exist and is the same as name -/// field within resource. Suggestions and implications are optional. If specified +/// field within [TagCategoryResource] resource. Suggestions and implications are optional. If specified /// implied tags or suggested tags do not exist yet, they will be automatically created. Tags /// created automatically have no implications, no suggestions, one name and their category is set /// to the first tag category found. If there are no tag categories established yet, an error @@ -215,7 +225,10 @@ pub struct CreateUpdateTag { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] /// 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 { @@ -235,7 +248,9 @@ pub struct TagCategoryResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl TagCategoryResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -284,7 +299,10 @@ pub struct MergeTags { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] /// 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 @@ -295,14 +313,19 @@ pub struct TagSibling { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl TagSibling { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } } #[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// The type of post pub enum PostType { @@ -325,7 +348,10 @@ pub enum PostType { } #[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// How SFW/NSFW the post is pub enum PostSafety { @@ -333,14 +359,17 @@ pub enum PostSafety { Safe, /// Post is possibly NSFW Sketchy, - /// Alias of (Sketchy)[PostSafety::Sketchy] + /// Alias of [Sketchy](PostSafety::Sketchy) Questionable, /// Post is NSFW Unsafe, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A post resource stripped down to `id` and `thumbnailUrl` fields. pub struct MicroPostResource { @@ -352,7 +381,9 @@ pub struct MicroPostResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl MicroPostResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -378,7 +409,10 @@ pub(crate) struct PostId { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A post resource pub struct PostResource { @@ -455,7 +489,9 @@ pub struct PostResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl PostResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -586,7 +622,10 @@ pub struct RateResource { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A text annotation rendered on top of the post pub struct NoteResource { @@ -601,14 +640,19 @@ pub struct NoteResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl NoteResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// The Rank of a given User pub enum UserRank { @@ -625,7 +669,10 @@ pub enum UserRank { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// The kind of User Avatar pub enum UserAvatarStyle { @@ -635,8 +682,9 @@ pub enum UserAvatarStyle { Manual, } +// Because pyo3 get_all doesn't let you exclude fields we have to define the fields twice #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass)] +#[cfg_attr(all(feature = "python"), pyclass(module = "szurubooru_client.models"))] #[serde(rename_all = "camelCase")] /// A single user pub struct UserResource { @@ -751,13 +799,16 @@ pub struct UserResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl UserResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } #[getter] #[pyo3(name = "email")] + /// Returns this resource's email field, if the current user has permission to see it pub fn email_py(&self) -> PyResult> { match &self.email { None => Ok(None), @@ -768,6 +819,7 @@ impl UserResource { #[getter] #[pyo3(name = "liked_post_count")] + /// Returns this resource's liked_post_count, if the current user has permission to see it pub fn liked_post_count_py(&self) -> PyResult> { match &self.liked_post_count { None => Ok(None), @@ -778,6 +830,7 @@ impl UserResource { #[getter] #[pyo3(name = "disliked_post_count")] + /// Returns this resource's disliked_post_count, if the current user has permission to see it pub fn disliked_post_count_py(&self) -> PyResult> { match &self.disliked_post_count { None => Ok(None), @@ -788,6 +841,7 @@ impl UserResource { #[getter] #[pyo3(name = "favorite_post_count")] + /// Returns this resource's favorite_post_count, if the current user has permission to see it pub fn favorite_post_count_py(&self) -> PyResult> { match &self.favorite_post_count { None => Ok(None), @@ -843,7 +897,10 @@ pub struct CreateUpdateUser { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A user resource stripped down to `name` and `avatarUrl` fields pub struct MicroUserResource { @@ -855,7 +912,9 @@ pub struct MicroUserResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl MicroUserResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -875,7 +934,10 @@ impl WithBaseURL for MicroUserResource { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "kebab-case")] /// A single user token pub struct UserAuthTokenResource { @@ -901,7 +963,9 @@ pub struct UserAuthTokenResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl UserAuthTokenResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -957,7 +1021,10 @@ pub struct TemporaryPassword { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// Simple server configuration pub struct GlobalInfoConfig { @@ -982,7 +1049,10 @@ pub struct GlobalInfoConfig { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// Simple server statistics pub struct GlobalInfo { @@ -1004,14 +1074,19 @@ pub struct GlobalInfo { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl GlobalInfo { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[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. @@ -1030,7 +1105,9 @@ pub struct PoolCategoryResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl PoolCategoryResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -1066,7 +1143,10 @@ pub struct CreateUpdatePoolCategory { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// Type that represents a Pool resource pub struct PoolResource { @@ -1093,7 +1173,9 @@ pub struct PoolResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl PoolResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -1158,9 +1240,9 @@ pub struct CreateUpdatePool { /// // Merge pool ID 1 at version 1 to pool ID 3 at version 5 /// let merge_pool = MergePoolBuilder::default() /// .remove_pool_version(1) -/// .remove(1) +/// .remove_pool(1) /// .merge_to_version(5) -/// .merge_to(3) +/// .merge_to_pool(3) /// .build() /// .unwrap(); /// ``` @@ -1179,7 +1261,10 @@ pub struct MergePool { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A micro resource representing a Pool. A subset of the fields of a [PoolResource]. pub struct MicroPoolResource { @@ -1197,14 +1282,19 @@ pub struct MicroPoolResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl MicroPoolResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A type representing a Comment on a post pub struct CommentResource { @@ -1230,7 +1320,9 @@ pub struct CommentResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl CommentResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -1267,7 +1359,10 @@ pub struct CreateUpdateComment { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// The kind of snapshot that has been recorded pub enum SnapshotOperationType { @@ -1282,7 +1377,10 @@ pub enum SnapshotOperationType { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// The kind of resource described by this snapshot pub enum SnapshotResourceType { @@ -1301,7 +1399,10 @@ pub enum SnapshotResourceType { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase", untagged)] /// Data for a resource that was created #[allow(clippy::large_enum_variant)] @@ -1320,7 +1421,9 @@ pub enum SnapshotCreationDeletionData { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl SnapshotCreationDeletionData { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -1341,7 +1444,10 @@ impl WithBaseURL for SnapshotCreationDeletionData { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass)] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// Data for a modified resource pub struct SnapshotModificationData { @@ -1366,20 +1472,26 @@ pub struct SnapshotModificationData { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl SnapshotModificationData { #[getter] + /// Get the value associated with this snapshot pub fn get_value(&self, py: Python<'_>) -> PyResult> { let obj = to_pyobject(py, &self.value).unwrap().unbind(); Ok(obj) } + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[cfg_attr(all(feature = "python"), pyclass(eq))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, module = "szurubooru_client.models") +)] #[serde(untagged)] /// Type representing the data as part of a snapshot #[allow(clippy::large_enum_variant)] @@ -1404,7 +1516,10 @@ impl WithBaseURL for SnapshotData { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// Overall type representing some sort of change to a resource pub struct SnapshotResource { @@ -1425,7 +1540,9 @@ pub struct SnapshotResource { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl SnapshotResource { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -1442,7 +1559,10 @@ impl WithBaseURL for SnapshotResource { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A result when searching for similar posts to a given image pub struct ImageSearchSimilarPost { @@ -1454,7 +1574,9 @@ pub struct ImageSearchSimilarPost { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl ImageSearchSimilarPost { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -1470,7 +1592,10 @@ impl WithBaseURL for ImageSearchSimilarPost { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] #[serde(rename_all = "camelCase")] /// A type to represent the result from an Image search request pub struct ImageSearchResult { @@ -1484,7 +1609,9 @@ pub struct ImageSearchResult { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl ImageSearchResult { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } @@ -1500,7 +1627,10 @@ impl WithBaseURL for ImageSearchResult { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(all(feature = "python"), pyclass(get_all))] +#[cfg_attr( + all(feature = "python"), + pyclass(get_all, module = "szurubooru_client.models") +)] /// A type that represents posts that are before or after an existing post pub struct AroundPostResult { /// A previous post, if it exists @@ -1511,7 +1641,9 @@ pub struct AroundPostResult { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pymethods)] +#[doc(hidden)] impl AroundPostResult { + /// Generates a representative string of this resource fn __repr__(&self) -> String { format!("{:?}", self) } diff --git a/szurubooru-client/src/py/asynchronous.rs b/szurubooru-client/src/py/asynchronous.rs index f350fc5..d5be925 100644 --- a/szurubooru-client/src/py/asynchronous.rs +++ b/szurubooru-client/src/py/asynchronous.rs @@ -7,7 +7,10 @@ use pyo3::exceptions::{PyRuntimeError, PyValueError}; use pyo3::prelude::*; use std::path::PathBuf; -#[pyclass(name = "SzurubooruAsyncClient")] +#[pyclass(name = "SzurubooruAsyncClient", module = "szurubooru_client")] +/// An asynchronous client for Szurubooru +/// +/// :see: :class:`~szurubooru_client.SzurubooruSyncClient` for supported parameters pub struct PythonAsyncClient { client: SzurubooruClient, } @@ -16,8 +19,9 @@ pub struct PythonAsyncClient { impl PythonAsyncClient { #[new] #[pyo3(signature = (host, username=None, token=None, password=None, allow_insecure=None))] + /// Creates a new instance of the Asynchornous client /// - /// + /// :see: :class:`~szurubooru_client.SzurubooruSyncClient` for supported parameters pub fn new( host: String, username: Option, @@ -47,6 +51,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (fields=None))] + /// List the available tag categories (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_tag_categories` for parameters and return type pub async fn list_tag_categories( &self, fields: Option>, @@ -60,6 +67,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, color=None, order=None, fields=None))] + /// Creates a new tag category using the specified parameters (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_tag_category` for parameters and return type pub async fn create_tag_category( &self, name: String, @@ -83,11 +93,15 @@ impl PythonAsyncClient { .map_err(Into::into) } - #[pyo3(signature = (name, version, color=None, order=None, fields=None))] + #[pyo3(signature = (name, version, new_name=None, color=None, order=None, fields=None))] + /// Updates an existing tag category (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update_tag_category` for parameters and return type pub async fn update_tag_category( &self, name: String, version: u32, + new_name: Option, color: Option, order: Option, fields: Option>, @@ -95,6 +109,9 @@ impl PythonAsyncClient { let mut cutag = CreateUpdateTagCategoryBuilder::default(); let mut cutag = cutag.version(version); + if let Some(name) = new_name { + cutag = cutag.name(name); + } if let Some(color) = color { cutag = cutag.color(color); } @@ -111,6 +128,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, fields=None))] + /// Fetches a tag category by name (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_tag_category` for parameters and return type pub async fn get_tag_category( &self, name: String, @@ -123,6 +143,10 @@ impl PythonAsyncClient { .map_err(Into::into) } + #[pyo3(signature = (name, version))] + /// Deletes a tag category (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_tag_category` for parameters and return type pub async fn delete_tag_category(&self, name: String, version: u32) -> PyResult<()> { self.client .request() @@ -131,6 +155,8 @@ impl PythonAsyncClient { .map_err(Into::into) } + #[pyo3(signature = (name))] + /// Sets the default tag category for the site (async version) pub async fn set_default_tag_category(&self, name: String) -> PyResult<()> { self.client .request() @@ -140,6 +166,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the tags currently available on the site (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_tags` for parameters and return type pub async fn list_tags( &self, query: Option>, @@ -158,9 +187,11 @@ impl PythonAsyncClient { } #[pyo3(signature = (names, category=None, description=None, implications=None, suggestions=None, fields=None))] + /// Creating a new tag (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_tag` for parameters and return type pub async fn create_tag( &self, - //names: Vec, names: Py, category: Option, description: Option, @@ -203,11 +234,15 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, version, names=None, category=None, description=None, implications=None, suggestions=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing tag (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update_tag` for parameters and return type pub async fn update_tag( &self, name: String, version: u32, - names: Option>, + names: Option>, category: Option, description: Option, implications: Option>, @@ -217,7 +252,18 @@ impl PythonAsyncClient { let mut cubuild = CreateUpdateTagBuilder::default(); cubuild.version(version); if let Some(names) = names { - cubuild.names(names); + Python::with_gil(|py| { + if let Ok(name) = names.extract::(py) { + Ok(cubuild.names(vec![name])) + } else { + let list_res = names.extract::>(py); + if let Ok(names) = list_res { + Ok(cubuild.names(names)) + } else { + Err(list_res.err().unwrap()) + } + } + })?; } if let Some(cat) = category { cubuild.category(cat); @@ -240,6 +286,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, fields=None))] + /// Fetches an existing tag (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_tag` for parameters and return type pub async fn get_tag( &self, name: String, @@ -252,6 +301,10 @@ impl PythonAsyncClient { .map_err(Into::into) } + #[pyo3(signature = (name, version))] + /// Deletes an existing tag (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_tag` for parameters and return type pub async fn delete_tag(&self, name: String, version: u32) -> PyResult<()> { self.client .request() @@ -261,6 +314,10 @@ impl PythonAsyncClient { } #[pyo3(signature = (remove_tag, remove_tag_version, merge_to_tag, merge_to_version, fields=None))] + /// Removes source tag and merges all of its usages, suggestions and implications to the + /// target tag. (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.merge_tags` for parameters and return type pub async fn merge_tags( &self, remove_tag: String, @@ -282,6 +339,11 @@ impl PythonAsyncClient { .map_err(Into::into) } + #[pyo3(signature = (name))] + /// Lists siblings of given tag, e.g. tags that were used in the same posts as the given tag. + /// (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_tag_siblings` for parameters and return type pub async fn get_tag_siblings(&self, name: String) -> PyResult> { self.client .request() @@ -292,6 +354,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// Lists the posts currently available on the site (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_posts` for parameters and return type pub async fn list_posts( &self, query: Option>, @@ -309,12 +374,16 @@ impl PythonAsyncClient { .map(Into::into) } - #[pyo3(signature = (url=None, token=None, file_path=None, thumbnail_path=None, tags=None, safety=None, source=None, + #[pyo3(signature = (url=None, upload_token=None, file_path=None, thumbnail_path=None, tags=None, safety=None, source=None, relations=None, notes=None, flags=None, anonymous=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Create a new post using one of three image sources (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_post` for parameters and return type pub async fn create_post( &self, url: Option, - token: Option, + upload_token: Option, file_path: Option, thumbnail_path: Option, tags: Option>, @@ -349,7 +418,7 @@ impl PythonAsyncClient { cupost.anonymous(anonymous); } - if let Some(token) = token { + if let Some(token) = upload_token { cupost.content_token(token); let cupost = cupost.build()?; self.client @@ -382,6 +451,10 @@ impl PythonAsyncClient { #[pyo3(signature = (post_id, post_version, url=None, token=None, file_path=None, thumbnail_path=None, tags=None, safety=None, source=None, relations=None, notes=None, flags=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing post (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update_post` for parameters and return type pub async fn update_post( &self, post_id: u32, @@ -452,6 +525,10 @@ impl PythonAsyncClient { } } + #[pyo3(signature = (post_id))] + /// Downloads the given post's image as a byte array (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_image_bytes` for parameters and return type pub async fn get_image_bytes(&self, post_id: u32) -> PyResult> { let bytes = self .client @@ -462,6 +539,10 @@ impl PythonAsyncClient { Ok(bytes) } + #[pyo3(signature = (post_id, file_path))] + /// Downloads the given post's image to a path on the filesystem + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.download_image_to_path` for parameters and return type pub async fn download_image_to_path(&self, post_id: u32, file_path: PathBuf) -> PyResult<()> { self.client .request() @@ -470,6 +551,10 @@ impl PythonAsyncClient { .map_err(Into::into) } + #[pyo3(signature = (post_id))] + /// Downloads the given post's thumbnail as a byte array + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_thumbnail_bytes` for parameters and return type pub async fn get_thumbnail_bytes<'py>(&self, post_id: u32) -> PyResult> { let bytes = self .client @@ -480,6 +565,10 @@ impl PythonAsyncClient { Ok(bytes) } + #[pyo3(signature = (post_id, file_path))] + /// Downloads the given post's thumbnail to a path on the filesystem + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.` for parameters and return type pub async fn download_thumbnail_to_path( &self, post_id: u32, @@ -492,6 +581,10 @@ impl PythonAsyncClient { .map_err(Into::into) } + #[pyo3(signature = (image_path))] + /// Reverse image searches for an image from the filesystem (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.reverse_image_search` for parameters and return type pub async fn reverse_image_search(&self, image_path: PathBuf) -> PyResult { self.client .request() @@ -500,6 +593,10 @@ impl PythonAsyncClient { .map_err(Into::into) } + #[pyo3(signature = (image_path))] + /// Searches for an *exact* image match of an image from the filesystem (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.post_for_image` for parameters and return type pub async fn post_for_image(&self, image_path: PathBuf) -> PyResult> { self.client .request() @@ -509,6 +606,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (post_id, fields=None))] + /// Fetches an individual post by its post ID (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_post` for parameters and return type pub async fn get_post( &self, post_id: u32, @@ -521,6 +621,10 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Fetches posts from *around* the given post ID. That means the post before and after, + // if they exist. (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_around_post` for parameters and return type pub async fn get_around_post(&self, post_id: u32) -> PyResult { self.client .request() @@ -529,6 +633,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Deletes a post by its ID (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_post` for parameters and return type pub async fn delete_post(&self, post_id: u32, version: u32) -> PyResult<()> { self.client .request() @@ -539,6 +646,10 @@ impl PythonAsyncClient { #[pyo3(signature = (remove_post, remove_post_version, merge_to_post, merge_to_version, replace_post_content=false, fields=None))] + /// Removes source post and merges all of its tags, relations, scores, favorites and comments to + /// the target post (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.merge_post` for parameters and return type pub async fn merge_post( &self, remove_post: u32, @@ -563,13 +674,17 @@ impl PythonAsyncClient { } #[pyo3(signature = (post_id, rating, fields=None))] + /// Updates score of authenticated user for given post. Valid scores are -1, 0 and 1. + /// (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.rate_post` for parameters and return type pub async fn rate_post( &self, post_id: u32, rating: i8, fields: Option>, ) -> PyResult { - if rating < -1 || rating > 1 { + if !(-1..=1).contains(&rating) { Err(PyValueError::new_err("Rating must be -1, 0, or 1")) } else { self.client @@ -581,6 +696,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (post_id, fields=None))] + /// Marks the post as favorite for the current user (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.favorite_post` for parameters and return type pub async fn favorite_post( &self, post_id: u32, @@ -594,6 +712,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (post_id, fields=None))] + /// Unmarks the post as favorite for the current user (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.unfavorite_post` for parameters and return type pub async fn unfavorite_post( &self, post_id: u32, @@ -607,6 +728,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (fields=None))] + /// Retrieves the post that is currently featured on the main page (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_featured_post` for parameters and return type pub async fn get_featured_post( &self, fields: Option>, @@ -619,6 +743,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (post_id, fields=None))] + /// Features a post on the main page (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.set_featured_post` for parameters and return type pub async fn set_featured_post( &self, post_id: u32, @@ -632,6 +759,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (fields=None))] + /// Lists all pool categories (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_pool_categories` for parameters and return type pub async fn list_pool_categories( &self, fields: Option>, @@ -645,6 +775,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, color=None, fields=None))] + /// Creates a new pool category using specified parameters (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_pool_category` for parameters and return type pub async fn create_pool_category( &self, name: String, @@ -665,6 +798,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, version, new_name=None, color=None, fields=None))] + /// Updates an existing tag category using specified parameters (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update_pool_category` for parameters and return type pub async fn update_pool_category( &self, name: String, @@ -690,6 +826,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, fields=None))] + /// Fetches an existing pool category (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_pool_category` for parameters and return type pub async fn get_pool_category( &self, name: String, @@ -702,6 +841,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Deletes existing pool category (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_pool_category` for parameters and return type pub async fn delete_pool_category(&self, name: String, version: u32) -> PyResult<()> { self.client .request() @@ -711,6 +853,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, fields=None))] + /// Sets given pool category as default (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.set_default_pool_category` for parameters and return type pub async fn set_default_pool_category( &self, name: String, @@ -724,6 +869,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the post pools currently available on the site (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_pools` for parameters and return type pub async fn list_pools( &self, query: Option>, @@ -742,6 +890,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (names, category=None, description=None, posts=None, fields=None))] + /// Creates a new pool using specified parameters (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_pool` for parameters and return type pub async fn create_pool<'py>( &self, names: Py, @@ -781,13 +932,17 @@ impl PythonAsyncClient { .map_err(Into::into) } - #[pyo3(signature = (pool_id, version, names=None, category=None, description=None, + #[pyo3(signature = (pool_id, version, new_names=None, category=None, description=None, posts=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing pool using specified parameters (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update_pool` for parameters and return type pub async fn update_pool( &self, pool_id: u32, version: u32, - names: Option>, + new_names: Option>, category: Option, description: Option, posts: Option>, @@ -795,7 +950,7 @@ impl PythonAsyncClient { ) -> PyResult { let mut cupool = CreateUpdatePoolBuilder::default(); cupool.version(version); - if let Some(names) = names { + if let Some(names) = new_names { cupool.names(names); } @@ -817,6 +972,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (pool_id, fields=None))] + /// Retrieves information about an existing pool (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_pool` for parameters and return type pub async fn get_pool( &self, pool_id: u32, @@ -829,6 +987,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Deletes existing pool (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_pool` for parameters and return type pub async fn delete_pool(&self, pool_id: u32, version: u32) -> PyResult<()> { self.client .request() @@ -838,6 +999,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (remove_pool, remove_pool_version, merge_to_pool, merge_to_version, fields=None))] + /// Removes source pool and merges all of its posts with the target pool. (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.merge_pools` for parameters and return type pub async fn merge_pools( &self, remove_pool: u32, @@ -860,6 +1024,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the comments currently available on the site (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_comments` for parameters and return type pub async fn list_comments( &self, query: Option>, @@ -878,6 +1045,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (text, post_id, fields=None))] + /// Creates a new comment under a given post (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_comment` for parameters and return type pub async fn create_comment( &self, text: String, @@ -897,6 +1067,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (comment_id, version, text, fields=None))] + /// Updates an existing comment with new text (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update_comment` for parameters and return type pub async fn update_comment( &self, comment_id: u32, @@ -917,6 +1090,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (comment_id, fields=None))] + /// Fetches an existing comment (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_comment` for parameters and return type pub async fn get_comment( &self, comment_id: u32, @@ -929,6 +1105,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Deletes an existing comment (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_comment` for parameters and return type pub async fn delete_comment(&self, comment_id: u32, version: u32) -> PyResult<()> { self.client .request() @@ -938,6 +1117,10 @@ impl PythonAsyncClient { } #[pyo3(signature = (comment_id, rating, fields=None))] + /// Updates score of authenticated user for given comment. Valid scores are -1, 0 and 1. + /// (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.rate_comment` for parameters and return type pub async fn rate_comment( &self, comment_id: u32, @@ -952,6 +1135,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the users currently registered on the site (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_users` for parameters and return type pub async fn list_users( &self, query: Option>, @@ -970,6 +1156,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, password, rank=None, avatar_path=None, fields=None))] + /// Creates a new user using specified parameters (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_user` for parameters and return type pub async fn create_user( &self, name: String, @@ -1004,6 +1193,10 @@ impl PythonAsyncClient { } #[pyo3(signature = (name, version, new_name=None, password=None, rank=None, avatar_path=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing user using specified parameters (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update` for parameters and return type pub async fn update_user( &self, name: String, @@ -1045,6 +1238,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (user_name, fields=None))] + /// Retrieves information about an existing user (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.get_user` for parameters and return type pub async fn get_user( &self, user_name: String, @@ -1057,6 +1253,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Deletes an existing user (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_user` for parameters and return type pub async fn delete_user(&self, user_name: String, version: u32) -> PyResult<()> { self.client .request() @@ -1066,6 +1265,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (user_name, fields=None))] + /// Fetches a list of the given user's auth tokens (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_user_tokens` for parameters and return type pub async fn list_user_tokens( &self, user_name: String, @@ -1080,6 +1282,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (user_name, note=None, enabled=None, expiration_time=None, fields=None))] + /// Creates an auth token for the given user (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.create_user_token` for parameters and return type pub async fn create_user_token( &self, user_name: String, @@ -1107,6 +1312,10 @@ impl PythonAsyncClient { } #[pyo3(signature = (user_name, token, version, enabled=None, note=None, expiration_time=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Update a user's existing auth token (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.update_user_token` for parameters and return type pub async fn update_user_token( &self, user_name: String, @@ -1136,6 +1345,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Deletes an existing user auth token (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.delete_user_token` for parameters and return type pub async fn delete_user_token( &self, user_name: String, @@ -1149,6 +1361,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Start a password reset request (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.password_reset_request` for parameters and return type pub async fn password_reset_request(&self, email_or_name: String) -> PyResult<()> { self.client .request() @@ -1157,6 +1372,9 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Confirm a password reset request (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.password_reset_confirm` for parameters and return type pub async fn password_reset_confirm( &self, email_or_name: String, @@ -1171,6 +1389,9 @@ impl PythonAsyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the snapshots currently available on the site (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.list_snapshots` for parameters and return type pub async fn list_snapshots( &self, query: Option>, @@ -1188,6 +1409,15 @@ impl PythonAsyncClient { .map(Into::into) } + /// Retrieves simple statistics. ``featured_post`` is ``None`` if there is no featured post yet. + /// ``server_time`` is pretty much the same as the Date HTTP + /// field, only formatted in a manner consistent with other dates. Values in config key are + /// taken directly from the server config, with the exception of privilege array keys being + /// converted to lower camel case to match the API convention. + /// + /// (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.global_info` for parameters and return type pub async fn global_info(&self) -> PyResult { self.client .request() @@ -1196,6 +1426,10 @@ impl PythonAsyncClient { .map_err(Into::into) } + /// Puts a file from a given file path in temporary storage and assigns it a token that can be + /// used in other requests. (async version) + /// + /// :see: :func:`~szurubooru_client.SzurubooruSyncClient.upload_temporary_file` for parameters and return type pub async fn upload_temporary_file(&self, file_path: PathBuf) -> PyResult { self.client .request() diff --git a/szurubooru-client/src/py/mod.rs b/szurubooru-client/src/py/mod.rs index e77a80d..658289b 100644 --- a/szurubooru-client/src/py/mod.rs +++ b/szurubooru-client/src/py/mod.rs @@ -1,19 +1,26 @@ use crate::models::PagedSearchResult; -use pyo3::exceptions::PyException; use pyo3::prelude::*; use pyo3::types::PyList; -use pyo3::types::PyListMethods; +// rustfmt likes to break the Python docstrings +#[rustfmt::skip] pub mod asynchronous; +#[rustfmt::skip] pub mod synchronous; #[derive(Debug)] -#[pyclass(name = "PagedSearchResult", get_all)] +#[pyclass(name = "PagedResult", get_all, module = "szurubooru_client")] +/// A paged result generated by most of the ``list`` methods of the Szurubooru clients pub struct PyPagedSearchResult { + /// The query string that was used to generate these results pub query: String, + /// The offset for the request, how many resource to skip before returning the results pub offset: u32, + /// The maximum number of results to return pub limit: u32, + /// The total number of results generated by the query pub total: u32, + /// The results themselves pub results: Py, } diff --git a/szurubooru-client/src/py/synchronous.rs b/szurubooru-client/src/py/synchronous.rs index aee0641..feb5b66 100644 --- a/szurubooru-client/src/py/synchronous.rs +++ b/szurubooru-client/src/py/synchronous.rs @@ -3,27 +3,22 @@ use crate::py::asynchronous::PythonAsyncClient; use crate::py::PyPagedSearchResult; use crate::tokens::QueryToken; use chrono::{DateTime, Utc}; -use pyo3::exceptions::{PyRuntimeError, PyValueError}; use pyo3::prelude::*; -use pyo3::types::{PyBytes, PyList}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use tokio::runtime::{Builder, Runtime}; -#[pyclass(name = "SzurubooruSyncClient")] +#[pyclass(name = "SzurubooruSyncClient", module = "szurubooru_client")] /// Constructor for the SzurubooruSyncClient -/// This client is completely synchronous. For the `asyncio` compatible version, -/// see [szurubooru_client.PythonAsyncClient](SzurubooruAsyncClient) +/// This client is completely synchronous. For the ``asyncio`` compatible version, +/// see :class:`SzurubooruAsyncClient` /// -/// ## Arguments -/// * `host`: Base host URL for the Szurubooru instance. Should be the protocol, hostname and any port -/// E.g `http://localhost:9801` -/// * `username`: The username used to authenticate against the Szurubooru instance. Leave blank for -/// anonymous authentication -/// * `password`: The password to use for `Basic` authentication. Token authentication should -/// be preferred -/// * `token`: The token to use for `Bearer` authentication. -/// * `allow_insecure`: Disable cert validation. Disables SSL authentication +/// :param str host: Base host URL for the Szurubooru instance. Should be the protocol, hostname and any port E.g ``http://localhost:9801`` +/// :param str username: The username used to authenticate against the Szurubooru instance. Leave blank for anonymous authentication +/// :param str password: The password to use for ``Basic`` authentication. Token authentication should be preferred +/// :param str token: The token to use for ``Bearer`` authentication. +/// :param bool allow_insecure: Disable cert validation. Disables SSL authentication /// +/// :rtype: SzurubooruSyncClient pub struct PythonSyncClient { client: PythonAsyncClient, runtime: Runtime, @@ -48,6 +43,13 @@ impl PythonSyncClient { #[pyo3(signature = (fields=None))] /// List the available tag categories + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// :return: A ``list`` of Tag Category resources + /// :rtype: list[TagCategoryResource] pub fn list_tag_categories( &self, fields: Option>, @@ -57,6 +59,18 @@ impl PythonSyncClient { } #[pyo3(signature = (name, color=None, order=None, fields=None))] + /// Creates a new tag category using the specified parameters. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The tag category name, must match the server's ``tag_category_name_regex`` + /// :param Optional[str] color: The color name for this tag category + /// :param Optional[str] order: The sort order for the tag category + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Tag Category resources + /// :rtype: :class:`TagCategoryResource ` pub fn create_tag_category( &self, name: String, @@ -68,22 +82,50 @@ impl PythonSyncClient { .block_on(self.client.create_tag_category(name, color, order, fields)) } - #[pyo3(signature = (name, version, color=None, order=None, fields=None))] + #[pyo3(signature = (name, version, new_name=None, color=None, order=None, fields=None))] + /// Updates an existing tag category using the specified parameters. + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The tag category name, must match the server's ``tag_category_name_regex`` + /// :param int version: The existing resource's version + /// :param Optional[str] new_name: The new name for the tag category + /// :param Optional[str] color: The color name for this tag category + /// :param Optional[str] order: The sort order for the tag category + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Tag Category resource + /// :rtype: :class:`TagCategoryResource ` pub fn update_tag_category( &self, name: String, version: u32, + new_name: Option, color: Option, order: Option, fields: Option>, ) -> PyResult { self.runtime.block_on( self.client - .update_tag_category(name, version, color, order, fields), + .update_tag_category(name, version, new_name, color, order, fields), ) } #[pyo3(signature = (name, fields=None))] + /// Fetches a tag category by name + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The name of the tag category + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Tag Category resource + /// :rtype: :class:`TagCategoryResource ` pub fn get_tag_category( &self, name: String, @@ -93,17 +135,53 @@ impl PythonSyncClient { .block_on(self.client.get_tag_category(name, fields)) } + #[pyo3(signature = (name, version))] + /// Deletes a tag category + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str name: The tag category's name + /// :param int version: The existing resource's version + /// pub fn delete_tag_category(&self, name: String, version: u32) -> PyResult<()> { self.runtime .block_on(self.client.delete_tag_category(name, version)) } + #[pyo3(signature = (name))] + /// Sets the default tag category for the site + /// + /// :param str name: The name of the category to set as default pub fn set_default_tag_category(&self, name: String) -> PyResult<()> { self.runtime .block_on(self.client.set_default_tag_category(name)) } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the tags currently available on the site + /// + /// .. note:: + /// This method supports :doc:`Query Tokens ` + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method supports :ref:`Result limits ` + /// + /// .. note:: + /// This method supports :ref:`Result offsets ` + /// + /// :param list[QueryToken] query: A list of query tokens used to filter the results + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// :param Optional[int] limit: The maximum number of resources to return + /// :param Optional[int] offset: The number of resources to skip before returning the resulting resources + /// + /// :see: :class:`szurubooru_client.tokens.TagNamedToken` and :class:`~szurubooru_client.tokens.TagSortToken` for query filtering + /// + /// :return: A paged result of :class:`~szurubooru_client.models.TagResource` + /// :rtype: :class:`~szurubooru_client.PagedResult` pub fn list_tags( &self, query: Option>, @@ -116,9 +194,27 @@ impl PythonSyncClient { } #[pyo3(signature = (names, category=None, description=None, implications=None, suggestions=None, fields=None))] + /// Creates a new tag using specified parameters. Names, suggestions and implications must + /// match `tag_name_regex` from server's configuration. Category must exist and is the same + /// as the `name` field within :class:`~szurubooru_client.models.TagCategoryResource` resource. + /// Suggestions and implications are optional. If specified implied tags or suggested tags do + /// not exist yet, they will be automatically created. Tags created automatically have no + /// implications, no suggestions, one name and their category is set to the first tag category + /// found. If there are no tag categories established yet, an error will be thrown. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param names: A string or list of strings that serve as the name(s) for this tag + /// :param str category: The name of the tag category that this tag belongs to + /// :param list[str] implications: Tags that are automatically implied when this tag is used + /// :param list[str] suggestions: Tags that should be suggested when this tag is used + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Tag resource + /// :rtype: :class:`TagResource ` pub fn create_tag( &self, - //names: Vec, names: Py, category: Option, description: Option, @@ -138,11 +234,36 @@ impl PythonSyncClient { #[pyo3(signature = (name, version, names=None, category=None, description=None, implications=None, suggestions=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing tag using specified parameters. Names, suggestions and implications must + /// match `tag_name_regex` from server's configuration. Category must exist and is the same + /// as the `name` field within :class:`~szurubooru_client.models.TagCategoryResource` resource. + /// Suggestions and implications are optional. If specified implied tags or suggested tags do + /// not exist yet, they will be automatically created. Tags created automatically have no + /// implications, no suggestions, one name and their category is set to the first tag category + /// found. If there are no tag categories established yet, an error will be thrown. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param name: The name of the existing tags + /// :param int version: The existing resource's version + /// :param Optional[list|str] names: A string or list of strings that the tag should be known as + /// :param str category: The name of the tag category that this tag belongs to + /// :param list[str] implications: Tags that are automatically implied when this tag is used + /// :param list[str] suggestions: Tags that should be suggested when this tag is used + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Tag resource + /// :rtype: :class:`TagResource ` pub fn update_tag( &self, name: String, version: u32, - names: Option>, + names: Option>, category: Option, description: Option, implications: Option>, @@ -162,15 +283,51 @@ impl PythonSyncClient { } #[pyo3(signature = (name, fields=None))] + /// Fetches an existing tag + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The name of the tag to fetch + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Tag resource + /// :rtype: :class:`~szurubooru_client.models.TagResource` pub fn get_tag(&self, name: String, fields: Option>) -> PyResult { self.runtime.block_on(self.client.get_tag(name, fields)) } + #[pyo3(signature = (name, version))] + /// Deletes an existing tag + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str name: The name of the tag to delete + /// :param int version: The existing resource's version pub fn delete_tag(&self, name: String, version: u32) -> PyResult<()> { self.runtime.block_on(self.client.delete_tag(name, version)) } #[pyo3(signature = (remove_tag, remove_tag_version, merge_to_tag, merge_to_version, fields=None))] + /// 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. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires two resource versions. See :ref:`Resource Versioning ` + /// + /// :param str remove_tag: The name of the tag to be removed + /// :param int remove_tag_version: The current version of the tag to be removed + /// :param str merge_to_tag: The name of the tag to be merged *to* + /// :param int merge_to_version: The current version of the tag to merge *to* + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Tag resource + /// :rtype: :class:`~szurubooru_client.models.TagResource` pub fn merge_tags( &self, remove_tag: String, @@ -188,11 +345,44 @@ impl PythonSyncClient { )) } + #[pyo3(signature = (name))] + /// Lists siblings of given tag, e.g. tags that were used in the same posts as the given tag. + /// The ``occurrences`` field signifies how many times a given + /// sibling appears with given tag. Results are sorted by occurrences count and the list is + /// truncated to the first 50 elements. + /// + /// :param str name: The name of the tag to fetch siblings for + /// + /// :return: A list of Tag siblings + /// :rtype: list[TagSibling] pub fn get_tag_siblings(&self, name: String) -> PyResult> { self.runtime.block_on(self.client.get_tag_siblings(name)) } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// Lists the posts currently available on the site + /// + /// .. note:: + /// This method supports :doc:`Query Tokens ` + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method supports :ref:`Result limits ` + /// + /// .. note:: + /// This method supports :ref:`Result offsets ` + /// + /// :param Optional[list[QueryToken]] query: A list of query tokens used to filter the results + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// :param Optional[int] limit: The maximum number of resources to return + /// :param Optional[int] offset: The number of results to skip before returning the result + /// + /// :see: :class:`szurubooru_client.tokens.PostNamedToken`, :class:`~szurubooru_client.tokens.PostSortToken`, and :class:`~szurubooru_client.tokens.PostSpecialToken` for query filtering + /// + /// :return: A :class:`~szurubooru_client.PagedResult` of Post resources + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn list_posts( &self, query: Option>, @@ -204,12 +394,39 @@ impl PythonSyncClient { .block_on(self.client.list_posts(query, fields, limit, offset)) } - #[pyo3(signature = (url=None, token=None, file_path=None, thumbnail_path=None, tags=None, safety=None, source=None, + #[pyo3(signature = (url=None, upload_token=None, file_path=None, thumbnail_path=None, tags=None, safety=None, source=None, relations=None, notes=None, flags=None, anonymous=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Creates a new post using one of three image sources: URL, upload token or file path. + /// + /// * URL: The server will download the given Image URL as the post's content + /// * Upload token: The token returned by using :func:`~szurubooru_client.SzurubooruSyncClient.upload_temporary_file` + /// * File path: The ``pathlib.Path`` or ``str`` path to the file to be uploaded from the local filesystem + /// + /// .. warning:: + /// The ``safety`` argument is *required* + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param Optional[str] url: The URL of the image to use for the post's content + /// :param Optional[str] upload_token: The token returned by the temporary upload method + /// :param Optional[str|pathlib.Path] file_path: The local file path to upload + /// :param Optional[str|Path] thumbnail_path: The local file path to the thumbnail for the post + /// :param Optional[list[str]] tags: The list of tag names to use for the post + /// :param PostSafety safety: The safety level of the post + /// :param Optional[list[int]] relations: A list of related post IDs + /// :param Optional[list[NoteResource]] notes: A list of :class:`~szurubooru_client.models.NoteResource` for the post + /// :param Optional[list[str]] flags: A list of flags to apply to the post + /// :param Optional[bool] anonymous: Whether to create the post anonymously + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn create_post( &self, url: Option, - token: Option, + upload_token: Option, file_path: Option, thumbnail_path: Option, tags: Option>, @@ -223,7 +440,7 @@ impl PythonSyncClient { ) -> PyResult { self.runtime.block_on(self.client.create_post( url, - token, + upload_token, file_path, thumbnail_path, tags, @@ -240,6 +457,36 @@ impl PythonSyncClient { #[pyo3(signature = (post_id, post_version, url=None, token=None, file_path=None, thumbnail_path=None, tags=None, safety=None, source=None, relations=None, notes=None, flags=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing post + /// + /// The post's content can be replaced using one of three methods: + /// * URL: The server will download the given Image URL as the post's content + /// * Upload token: The token returned by using :func:`~szurubooru_client.SzurubooruSyncClient.upload_temporary_file` + /// * File path: The ``pathlib.Path`` or string path to the file to be uploaded. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param int post_id: The ID of the post to update + /// :param int version: The existing resource's version + /// :param Optional[str] url: The URL of the image to use for the post's content + /// :param Optional[str] upload_token: The token returned by the temporary upload method + /// :param Optional[str|Path] file_path: The local file path to upload + /// :param Optional[str|Path] thumbnail_path: The local file path to the thumbnail for the post + /// :param Optional[list[str]] tags: The list of tag names to use for the post + /// :param Optional[PostSafety] safety: The safety level of the post + /// :param Optional[list[int]] relations: A list of related post IDs + /// :param Optional[list[NoteResource]] notes: A list of :class:`~szurubooru_client.models.NoteResource` for the post + /// :param Optional[list[str]] flags: A list of flags to apply to the post + /// :param Optional[bool] anonymous: Whether to create the post anonymously + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn update_post( &self, post_id: u32, @@ -273,44 +520,109 @@ impl PythonSyncClient { )) } - pub fn get_image_bytes<'py>(&self, post_id: u32) -> PyResult> { + #[pyo3(signature = (post_id))] + /// Downloads the given post's image as a byte array + /// + /// :param int post_id: The ID of the post to fetch + /// + /// :return: A byte array of the given post's content + /// :rtype: list[byte] + pub fn get_image_bytes(&self, post_id: u32) -> PyResult> { self.runtime.block_on(self.client.get_image_bytes(post_id)) } + #[pyo3(signature = (post_id, file_path))] + /// Downloads the given post's image to a path on the filesystem + /// + /// :param int post_id: The ID of the post to fetch + /// :param Path|str file_path: The path to download the image to pub fn download_image_to_path(&self, post_id: u32, file_path: PathBuf) -> PyResult<()> { self.runtime .block_on(self.client.download_image_to_path(post_id, file_path)) } - pub fn get_thumbnail_bytes<'py>(&self, post_id: u32) -> PyResult> { + #[pyo3(signature = (post_id))] + /// Downloads the given post's thumbnail as a byte array + /// + /// :param int post_id: The ID of the post to fetch + /// + /// :return: A byte array of the given post's content + /// :rtype: list[byte] + pub fn get_thumbnail_bytes(&self, post_id: u32) -> PyResult> { self.runtime .block_on(self.client.get_thumbnail_bytes(post_id)) } + #[pyo3(signature = (post_id, file_path))] + /// Downloads the given post's thumbnail to a path on the filesystem + /// + /// :param int post_id: The ID of the post to fetch + /// :param Path|str file_path: The path to download the thumbnail to pub fn download_thumbnail_to_path(&self, post_id: u32, file_path: PathBuf) -> PyResult<()> { self.runtime .block_on(self.client.download_thumbnail_to_path(post_id, file_path)) } + #[pyo3(signature = (image_path))] + /// Reverse image searches for an image from the filesystem. Returns + /// a list of visually similar images + /// + /// :param Path|str image_path: The path to the image to search for + /// + /// :return: An object containing the IDs of similar posts + /// :rtype: :class:`~szurubooru_client.models.ImageSearchResult` pub fn reverse_image_search(&self, image_path: PathBuf) -> PyResult { self.runtime .block_on(self.client.reverse_image_search(image_path)) } + #[pyo3(signature = (image_path))] + /// Searches for an *exact* image match of an image from the filesystem + /// + /// :param Path|str image_path: The path to the image to search for + /// + /// :return: A Post Resource or None if the image doesn't exist + /// :rtype: None|:class:`~szurubooru_client.models.PostResource` pub fn post_for_image(&self, image_path: PathBuf) -> PyResult> { self.runtime .block_on(self.client.post_for_image(image_path)) } #[pyo3(signature = (post_id, fields=None))] + /// Fetches an individual post by its post ID + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int post_id: The ID of the post to fetch + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A Post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn get_post(&self, post_id: u32, fields: Option>) -> PyResult { self.runtime.block_on(self.client.get_post(post_id, fields)) } + #[pyo3(signature = (post_id))] + /// Fetches posts from *around* the given post ID. That means the post before and after, + /// if they exist. + /// + /// :param int post_id: The ID of the post to fetch + /// + /// :return: A resource containing the IDs of the next and previous IDs + /// :rtype: :class:`~szurubooru_client.models.AroundPostResult` pub fn get_around_post(&self, post_id: u32) -> PyResult { self.runtime.block_on(self.client.get_around_post(post_id)) } + #[pyo3(signature = (post_id, version))] + /// Deletes a post by its ID + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param int post_id: The ID of the post to delete + /// :param int version: The existing resource's version pub fn delete_post(&self, post_id: u32, version: u32) -> PyResult<()> { self.runtime .block_on(self.client.delete_post(post_id, version)) @@ -318,6 +630,27 @@ impl PythonSyncClient { #[pyo3(signature = (remove_post, remove_post_version, merge_to_post, merge_to_version, replace_post_content=false, fields=None))] + /// Removes source post and merges all of its tags, relations, scores, favorites and comments to + /// the target post. If ``replace_content`` is set to ``true``, content of the target post + /// is replaced using the content of the source post; otherwise it remains unchanged. Source + /// post properties such as its safety, source, whether to loop the video and other scalar + /// values do not get transferred and are discarded. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires two resource versions. See :ref:`Resource Versioning ` + /// + /// :param int remove_post: The ID of the source post + /// :param int remove_post_version: The current version of the source post + /// :param int merge_to_post: The ID of the destination post + /// :param int merge_to_version: The current version of the destination post + /// :param bool replace_post_content: Whether to replace the destination post's content with the content from the source post + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn merge_post( &self, remove_post: u32, @@ -338,6 +671,17 @@ impl PythonSyncClient { } #[pyo3(signature = (post_id, rating, fields=None))] + /// Updates score of authenticated user for given post. Valid scores are -1, 0 and 1. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int post_id: The ID of the post to rate + /// :param int rating: The rating to give the post. Must be -1, 0 or 1. + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn rate_post( &self, post_id: u32, @@ -349,6 +693,16 @@ impl PythonSyncClient { } #[pyo3(signature = (post_id, fields=None))] + /// Marks the post as favorite for the current user. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int post_id: The ID of the post to rate + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn favorite_post( &self, post_id: u32, @@ -359,6 +713,16 @@ impl PythonSyncClient { } #[pyo3(signature = (post_id, fields=None))] + /// Unmarks the post as favorite for the current user. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int post_id: The ID of the post to rate + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn unfavorite_post( &self, post_id: u32, @@ -369,11 +733,33 @@ impl PythonSyncClient { } #[pyo3(signature = (fields=None))] + /// Retrieves the post that is currently featured on the main page. If no post is + /// featured, the returned value is ``None``. Note that this method exists mostly for compatibility + /// with setting featured post - most of the time, you'd want to use query global info which + /// contains more information. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A post resource or ``None`` + /// :rtype: Optional[:class:`~szurubooru_client.models.PostResource`] pub fn get_featured_post(&self, fields: Option>) -> PyResult> { self.runtime.block_on(self.client.get_featured_post(fields)) } #[pyo3(signature = (post_id, fields=None))] + /// Features a post on the main page + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int post_id: The ID of the post to feature + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A post resource + /// :rtype: :class:`~szurubooru_client.models.PostResource` pub fn set_featured_post( &self, post_id: u32, @@ -384,6 +770,15 @@ impl PythonSyncClient { } #[pyo3(signature = (fields=None))] + /// Lists all pool categories + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A list of Pool Category resources + /// :rtype: list[:class:`~szurubooru_client.models.PoolCategoryResource`] pub fn list_pool_categories( &self, fields: Option>, @@ -393,6 +788,19 @@ impl PythonSyncClient { } #[pyo3(signature = (name, color=None, fields=None))] + /// Creates a new pool category using specified parameters. Name must match + /// ``pool_category_name_regex`` from server's configuration. First category created becomes + /// the default category. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The name of the pool category to create + /// :param str color: The color to associate with the pool category + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A pool category resource + /// :rtype: :class:`~szurubooru_client.models.PoolCategoryResource` pub fn create_pool_category( &self, name: String, @@ -404,6 +812,20 @@ impl PythonSyncClient { } #[pyo3(signature = (name, version, new_name=None, color=None, fields=None))] + /// Updates an existing tag category using specified parameters. Name must match + /// `tag_category_name_regex` from server's configuration. + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str name: The name of the pool category to modify + /// :param int version: The existing resource's version + /// :param Optional[str] new_name: The new name for the pool category + /// :param Optional[str] color: The new color for the pool category + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: An updated pool category resource + /// :rtype: :class:`~szurubooru_client.models.PoolCategoryResource` pub fn update_pool_category( &self, name: String, @@ -419,6 +841,16 @@ impl PythonSyncClient { } #[pyo3(signature = (name, fields=None))] + /// Fetches an existing pool category + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The name of the pool category to fetch + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A pool category resource + /// :rtype: :class:`~szurubooru_client.models.PoolCategoryResource` pub fn get_pool_category( &self, name: String, @@ -428,12 +860,31 @@ impl PythonSyncClient { .block_on(self.client.get_pool_category(name, fields)) } + #[pyo3(signature = (name, version))] + /// Deletes existing pool category. The pool category to be deleted must have no usages. + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str name: The name of the pool category to delete + /// :param int version: The existing resource's version pub fn delete_pool_category(&self, name: String, version: u32) -> PyResult<()> { self.runtime .block_on(self.client.delete_pool_category(name, version)) } #[pyo3(signature = (name, fields=None))] + /// Sets given pool category as default. All new pools created manually or automatically will + /// have this category. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The name of the pool category to be set as default + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A pool category resource + /// :rtype: :class:`~szurubooru_client.models.PoolCategoryResource` pub fn set_default_pool_category( &self, name: String, @@ -444,6 +895,29 @@ impl PythonSyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the post pools currently available on the site + /// + /// .. note:: + /// This method supports :doc:`Query Tokens ` + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method supports :ref:`Result limits ` + /// + /// .. note:: + /// This method supports :ref:`Result offsets ` + /// + /// :param list[QueryToken] query: A list of query tokens used to filter the results + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// :param Optional[int] limit: The maximum number of resources to return + /// :param Optional[int] offset: The number of resources to skip before returning the resulting resources + /// + /// :see: :class:`szurubooru_client.tokens.PoolNamedToken` and :class:`~szurubooru_client.tokens.PoolSortToken` for query filtering + /// + /// :return: A paged result of :class:`~szurubooru_client.models.PoolResource` + /// :rtype: :class:`~szurubooru_client.PagedResult` pub fn list_pools( &self, query: Option>, @@ -456,6 +930,21 @@ impl PythonSyncClient { } #[pyo3(signature = (names, category=None, description=None, posts=None, fields=None))] + /// Creates a new pool using specified parameters. Names, suggestions and implications must + /// match `pool_name_regex` from server's configuration. ``posts`` is an optional list of + /// post IDs to add to the pool. If the specified posts do not exist, an error will be thrown + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param list[str]|str names: The name or names for the new pool + /// :param Optional[str] category: The pool category for this pool + /// :param Optional[str] description: The description for this pool + /// :param Optional[list[int]] posts: The posts that will be part of this pool + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A pool resource + /// :rtype: :class:`~szurubooru_client.models.PoolResource` pub fn create_pool( &self, names: Py, @@ -470,13 +959,37 @@ impl PythonSyncClient { ) } - #[pyo3(signature = (pool_id, version, names=None, category=None, description=None, + #[pyo3(signature = (pool_id, version, new_names=None, category=None, description=None, posts=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing pool using specified parameters. + /// ``new_names``, if given, must match ``pool_name_regex`` from server's configuration. + /// ``category``, if given, must exist. + /// ``posts`` is an optional list of integer post IDs. If the specified posts do not exist yet, + /// an error will be thrown. The full list of post IDs must be provided if they are being + /// updated, and the previous list of posts will be replaced with the new one. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param int pool_id: The ID of the pool to update + /// :param int version: The existing resource's version + /// :param Optional[list[str]] new_names: The new name(s) for the pool + /// :param Optional[str] category: The new pool category for the pool + /// :param Optional[str] description: The new description for the pool + /// :param Optional[list[int]] posts: The posts that belong to this pool + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A pool category resource + /// :rtype: :class:`~szurubooru_client.models.PoolResource` pub fn update_pool( &self, pool_id: u32, version: u32, - names: Option>, + new_names: Option>, category: Option, description: Option, posts: Option>, @@ -485,7 +998,7 @@ impl PythonSyncClient { self.runtime.block_on(self.client.update_pool( pool_id, version, - names, + new_names, category, description, posts, @@ -494,16 +1007,52 @@ impl PythonSyncClient { } #[pyo3(signature = (pool_id, fields=None))] + /// Retrieves information about an existing pool + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int pool_id: The ID of the pool to fetch + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A pool resource + /// :rtype: :class:`~szurubooru_client.models.PoolResource` pub fn get_pool(&self, pool_id: u32, fields: Option>) -> PyResult { self.runtime.block_on(self.client.get_pool(pool_id, fields)) } + #[pyo3(signature = (pool_id, version))] + /// Deletes existing pool. All posts in the pool will only have their relation to the pool + /// removed. + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param int pool_id: The ID of the pool to delete + /// :param int version: The existing resource's version pub fn delete_pool(&self, pool_id: u32, version: u32) -> PyResult<()> { self.runtime .block_on(self.client.delete_pool(pool_id, version)) } #[pyo3(signature = (remove_pool, remove_pool_version, merge_to_pool, merge_to_version, fields=None))] + /// Removes source pool and merges all of its posts with the target pool. Other pool properties + /// such as category and aliases do not get transferred and are discarded. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires two resource versions. See :ref:`Resource Versioning ` + /// + /// :param int remove_pool: The ID of the source pool + /// :param int remove_pool_version: The current version of the source pool + /// :param int merge_to_pool: The ID of the destination pool + /// :param int merge_to_version: The current version of the destination pool + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A pool resource + /// :rtype: :class:`~szurubooru_client.models.PoolResource` pub fn merge_pools( &self, remove_pool: u32, @@ -522,6 +1071,30 @@ impl PythonSyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the comments currently available on the site. + /// + /// + /// .. note:: + /// This method supports :doc:`Query Tokens ` + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method supports :ref:`Result limits ` + /// + /// .. note:: + /// This method supports :ref:`Result offsets ` + /// + /// :param list[QueryToken] query: A list of query tokens used to filter the results + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// :param Optional[int] limit: The maximum number of resources to return + /// :param Optional[int] offset: The number of resources to skip before returning the resulting resources + /// + /// :see: :class:`~szurubooru_client.tokens.CommentNamedToken` and `~szurubooru_client.tokens.CommentSortToken` for query filtering + /// + /// :return: A paged result of :class:`~szurubooru_client.models.CommentResource` + /// :rtype: :class:`~szurubooru_client.PagedResult` pub fn list_comments( &self, query: Option>, @@ -534,6 +1107,17 @@ impl PythonSyncClient { } #[pyo3(signature = (text, post_id, fields=None))] + /// Creates a new comment under a given post + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str text: The text of the comment + /// :param int post_id: The ID of the post to create the comment on + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A comment resource + /// :rtype: :class:`~szurubooru_client.models.CommentResource` pub fn create_comment( &self, text: String, @@ -545,6 +1129,21 @@ impl PythonSyncClient { } #[pyo3(signature = (comment_id, version, text, fields=None))] + /// Updates an existing comment with new text + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param int comment_id: The ID of the comment to update + /// :param int version: The existing resource's version + /// :param str text: The new text for the comment + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A comment resource + /// :rtype: :class:`~szurubooru_client.models.CommentResource` pub fn update_comment( &self, comment_id: u32, @@ -559,6 +1158,16 @@ impl PythonSyncClient { } #[pyo3(signature = (comment_id, fields=None))] + /// Fetches an existing comment + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int comment_id: The ID of the comment to fetch + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A comment resource + /// :rtype: :class:`~szurubooru_client.models.CommentResource` pub fn get_comment( &self, comment_id: u32, @@ -568,12 +1177,31 @@ impl PythonSyncClient { .block_on(self.client.get_comment(comment_id, fields)) } + #[pyo3(signature = (comment_id, version))] + /// Deletes an existing comment + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param int comment_id: The ID of the comment to delete + /// :param int version: The existing resource's version pub fn delete_comment(&self, comment_id: u32, version: u32) -> PyResult<()> { self.runtime .block_on(self.client.delete_comment(comment_id, version)) } #[pyo3(signature = (comment_id, rating, fields=None))] + /// Updates score of authenticated user for given comment. Valid scores are -1, 0 and 1. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param int comment_id: The ID of the comment to rate + /// :param int rating: The rating to give the comment. Must be -1, 0, or 1 + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A comment resource + /// :rtype: :class:`~szurubooru_client.models.CommentResource` pub fn rate_comment( &self, comment_id: u32, @@ -585,6 +1213,29 @@ impl PythonSyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the users currently registered on the site + /// + /// .. note:: + /// This method supports :doc:`Query Tokens ` + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method supports :ref:`Result limits ` + /// + /// .. note:: + /// This method supports :ref:`Result offsets ` + /// + /// :param list[QueryToken] query: A list of query tokens used to filter the results + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// :param Optional[int] limit: The maximum number of resources to return + /// :param Optional[int] offset: The number of resources to skip before returning the resulting resources + /// + /// :see: :class:`szurubooru_client.tokens.UserNamedToken` and :class:`~szurubooru_client.tokens.UserSortToken` for query filtering + /// + /// :return: A paged result of :class:`~szurubooru_client.models.UserResource` + /// :rtype: :class:`~szurubooru_client.PagedResult` pub fn list_users( &self, query: Option>, @@ -597,6 +1248,26 @@ impl PythonSyncClient { } #[pyo3(signature = (name, password, rank=None, avatar_path=None, fields=None))] + /// Creates a new user using specified parameters. Names and passwords must match + /// ``user_name_regex`` and ``password_regex`` from server's configuration, respectively. + /// Email address, rank and avatar fields are optional. Avatar style can be either + /// ``Gravatar`` or ``Manual`` from the :class`~szurubooru_client.models.UserAvatarStyle` enum. + /// ``Manual`` avatar style requires client to pass also the ``avatar_path`` argument. + /// If the rank is empty and the user happens to be the first user ever created, + /// become an administrator, whereas subsequent users will be given the rank indicated by + /// ``default_rank`` in the server's configuration. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str name: The new user's username + /// :param str password: The new user's password + /// :param Optional[UserRank] rank: The rank to give the new user + /// :param Optional[str] avatar_path: The local file path to the user's avatar image + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A user resource + /// :rtype: :class:`~szurubooru_client.models.UserResource` pub fn create_user( &self, name: String, @@ -612,6 +1283,32 @@ impl PythonSyncClient { } #[pyo3(signature = (name, version, new_name=None, password=None, rank=None, avatar_path=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Updates an existing user using specified parameters. Names and passwords must match + /// ``user_name_regex`` and ``password_regex`` from server's configuration, respectively. + /// Email address, rank and avatar fields are optional. Avatar style can be either + /// ``Gravatar`` or ``Manual`` from the :class`~szurubooru_client.models.UserAvatarStyle` enum. + /// ``Manual`` avatar style requires client to pass also the ``avatar_path`` argument. + /// If the rank is empty and the user happens to be the first user ever created, + /// become an administrator, whereas subsequent users will be given the rank indicated by + /// ``default_rank`` in the server's configuration. + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str name: The existing user's username + /// :param int version: The existing resource's version + /// :param Optional[str] new_name: The user new username + /// :param Optional[str] password: The existing user's password + /// :param Optional[UserRank] rank: The rank to give the existing user + /// :param Optional[str] avatar_path: The local file path to the user's new avatar image + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A user resource + /// :rtype: :class:`~szurubooru_client.models.UserResource` pub fn update_user( &self, name: String, @@ -634,6 +1331,16 @@ impl PythonSyncClient { } #[pyo3(signature = (user_name, fields=None))] + /// Retrieves information about an existing user + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str user_name: The username of the user to fetch + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A user resource + /// :rtype: :class:`~szurubooru_client.models.UserResource` pub fn get_user( &self, user_name: String, @@ -643,12 +1350,30 @@ impl PythonSyncClient { .block_on(self.client.get_user(user_name, fields)) } + #[pyo3(signature = (user_name, version))] + /// Deletes an existing user + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str user_name: The username of the user to delete + /// :param int version: The existing resource's version pub fn delete_user(&self, user_name: String, version: u32) -> PyResult<()> { self.runtime .block_on(self.client.delete_user(user_name, version)) } #[pyo3(signature = (user_name, fields=None))] + /// Fetches a list of the given user's auth tokens + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str user_name: The username of the user to fetch the auth tokens for + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A user auth token resource + /// :rtype: :class:`~szurubooru_client.models.UserAuthTokenResource` pub fn list_user_tokens( &self, user_name: String, @@ -659,6 +1384,19 @@ impl PythonSyncClient { } #[pyo3(signature = (user_name, note=None, enabled=None, expiration_time=None, fields=None))] + /// Creates an auth token for the given user + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// :param str user_name: The username of the user to create an auth token for + /// :param Optional[str] note: A text note to include with the token + /// :param Optional[bool] enabled: Whether the token is enabled or not + /// :param Optional[DateTime] expiration_time: The ``DateTime`` specifying when the token should expire + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A user auth token resource + /// :rtype: :class:`~szurubooru_client.models.UserAuthTokenResource` pub fn create_user_token( &self, user_name: String, @@ -677,6 +1415,25 @@ impl PythonSyncClient { } #[pyo3(signature = (user_name, token, version, enabled=None, note=None, expiration_time=None, fields=None))] + #[allow(clippy::too_many_arguments)] + /// Update a user's existing auth token + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str user_name: The user's username + /// :param str token: The token to update + /// :param int version: The existing resource's version + /// :param Optional[str] note: A text note to include with the token + /// :param Optional[bool] enabled: Whether the token is enabled or not + /// :param Optional[DateTime] expiration_time: The ``DateTime`` specifying when the token should expire + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// + /// :return: A user auth token resource + /// :rtype: :class:`~szurubooru_client.models.UserAuthTokenResource` pub fn update_user_token( &self, user_name: String, @@ -698,6 +1455,15 @@ impl PythonSyncClient { )) } + #[pyo3(signature = (user_name, token, version))] + /// Deletes an existing user auth token + /// + /// .. note:: + /// This method requires a resource version. See :ref:`Resource Versioning ` + /// + /// :param str user_name: The user's username + /// :param str token: The token value + /// :param int version: The existing resource's version pub fn delete_user_token( &self, user_name: String, @@ -708,11 +1474,23 @@ impl PythonSyncClient { .block_on(self.client.delete_user_token(user_name, token, version)) } + #[pyo3(signature = (email_or_name))] + /// Start a password reset request + /// + /// :param str email_or_name: The email or username of the user to request the reset for pub fn password_reset_request(&self, email_or_name: String) -> PyResult<()> { self.runtime .block_on(self.client.password_reset_request(email_or_name)) } + #[pyo3(signature = (email_or_name, reset_token))] + /// Confirm a password reset request + /// + /// :param str email_or_name: The email or username of the user to confirm the reset request + /// :param str reset_token: The token sent to the user's email + /// + /// :return: A new temporary password + /// :rtype: str pub fn password_reset_confirm( &self, email_or_name: String, @@ -725,6 +1503,29 @@ impl PythonSyncClient { } #[pyo3(signature = (query=None, fields=None, limit=None, offset=None))] + /// List the snapshots currently available on the site + /// + /// .. note:: + /// This method supports :doc:`Query Tokens ` + /// + /// .. note:: + /// This method supports :doc:`Field selection ` + /// + /// .. note:: + /// This method supports :ref:`Result limits ` + /// + /// .. note:: + /// This method supports :ref:`Result offsets ` + /// + /// :param list[QueryToken] query: A list of query tokens used to filter the results + /// :param Optional[list[str]] fields: A list of fields to select for the returned object + /// :param Optional[int] limit: The maximum number of resources to return + /// :param Optional[int] offset: The number of resources to skip before returning the resulting resources + /// + /// :see: :class:`szurubooru_client.tokens.SnapshotNamedToken` for query filtering + /// + /// :return: A paged result of :class:`~szurubooru_client.models.SnapshotResource` + /// :rtype: :class:`~szurubooru_client.PagedResult` pub fn list_snapshots( &self, query: Option>, @@ -736,10 +1537,27 @@ impl PythonSyncClient { .block_on(self.client.list_snapshots(query, fields, limit, offset)) } + /// Retrieves simple statistics. ``featured_post`` is ``None`` if there is no featured post yet. + /// ``server_time`` is pretty much the same as the Date HTTP + /// field, only formatted in a manner consistent with other dates. Values in config key are + /// taken directly from the server config, with the exception of privilege array keys being + /// converted to lower camel case to match the API convention. + /// + /// :return: A Global Info object + /// :rtype: :class:`~szurubooru_client.models.GlobalInfo` pub fn global_info(&self) -> PyResult { self.runtime.block_on(self.client.global_info()) } + /// Puts a file from a given file path in temporary storage and assigns it a token that can be + /// used in other requests. + /// The files uploaded that way are deleted after a short while so clients shouldn't use it + /// as a free upload service. + /// + /// :param Path|str file_path: The path to the file to upload from the local filesystem + /// + /// :return: A token that represents the uploaded image + /// :rtype: str pub fn upload_temporary_file(&self, file_path: PathBuf) -> PyResult { self.runtime .block_on(self.client.upload_temporary_file(file_path)) diff --git a/szurubooru-client/src/tokens.rs b/szurubooru-client/src/tokens.rs index c14a49b..35c6b23 100644 --- a/szurubooru-client/src/tokens.rs +++ b/szurubooru-client/src/tokens.rs @@ -3,9 +3,8 @@ //! not guarantee that a given API endpoint will support the given tag. #[cfg(feature = "python")] -use pyo3::{exceptions::PyValueError, prelude::*, types::*}; +use pyo3::{exceptions::PyValueError, prelude::*}; use std::fmt::Display; -use std::str::FromStr; use strum_macros::AsRefStr; /// A named token such as `foo:bar` @@ -26,7 +25,7 @@ pub trait ToQueryString { /// A query token using for searching posts, tags and pools #[derive(Debug, Clone)] -#[cfg_attr(all(feature = "python"), pyclass)] +#[cfg_attr(all(feature = "python"), pyclass(module = "szurubooru_client.tokens"))] pub struct QueryToken { /// The key for this token. For `foo:bar` this would be `foo` pub key: String, @@ -145,24 +144,106 @@ impl QueryToken { #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pyfunction)] +/// Generates a named token. Named tokens are used to filter resources returned by the API. +/// An example of this would be returning posts with a certain safety value. +/// +/// This function will accept any string, but if you want to be more sure about your code you +/// can use any of the :ref:`Named token ` types listed below. +/// See the example below. +/// +/// :param key: String or Named field +/// :param value: The string or int value to use to filter by +/// :returns: The named query token +/// :rtype: QueryToken +/// +/// ----- +/// Usage +/// ----- +/// This lists all posts that are marked as 'safe'. +/// +/// ```python +/// client.list_posts(query=[named_token(PostNamedToken.Safety, 'safe')]) +/// ``` +/// +/// Which is equivalent to the possibly more error-prone: +/// +/// ```python +/// client.list_posts(query=[named_token("safety", 'safe')]) +/// ``` pub fn named_token(key: &Bound<'_, PyAny>, value: &Bound<'_, PyAny>) -> PyResult { QueryToken::token_py(key, value) } #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pyfunction)] +/// Generates a sorting token. Sorting tokens are used to sort resources returned by the API. +/// An example of this would be returning posts by score descending. +/// +/// This function will accept any string, but if you want to be more sure about your code you +/// can use any of the :ref:`Sort token ` types listed below. See the example below. +/// +/// :param key: String or Sort field name +/// :returns: The sort query token +/// :rtype: QueryToken +/// +/// ----- +/// Usage +/// ----- +/// This lists posts by score descending: +/// +/// ```python +/// client.list_posts(query=[-sort_token(PostSortToken.Score)]) +/// ``` +/// +/// Which is equivalent to the possibly more error-prone: +/// +/// ```python +/// client.list_posts(query=[-sort_token("score")]) +/// ``` pub fn sort_token(key: &Bound<'_, PyAny>) -> PyResult { QueryToken::sort_py(key) } #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pyfunction)] +/// Generates an anonymous token. Anonymous tokens are tokens that don't require some +/// sort of prefix to be used in a search. What the anonymous tag corresponds to depends +/// on the type of resource you're listing. For example, when listing posts the anonymous +/// tags correspond to post tags +/// +/// +/// :param str key: The anonymous token to create +/// :returns: The anonymous query token +/// :rtype: QueryToken +/// +/// ----- +/// Usage +/// ----- +/// +/// ```python +/// client.list_posts(fields=[anonymous_token("cat")]) +/// ``` pub fn anonymous_token(key: &Bound<'_, PyAny>) -> PyResult { QueryToken::anonymous_py(key) } #[cfg(feature = "python")] #[cfg_attr(all(feature = "python"), pyfunction)] +/// Special tokens are a very limited set of tokens supported by the ``list_posts`` API. +/// They include being able to filter by posts that the current user has upvoted, or favorited. +/// See :class:`PostSpecialToken` for all the supported token names. +/// +/// :param key: The special token name, string or ``PostSpecialToken`` +/// +/// ----- +/// Usage +/// ----- +/// +/// Selects posts with score of 0, without comments and without favorites +/// +/// ```python +/// client.list_post(fields=[special_token(PostSpecialToken.Tumbleweed)]) +/// ``` pub fn special_token(key: &Bound<'_, PyAny>) -> PyResult { QueryToken::special_py(key) } @@ -171,17 +252,20 @@ pub fn special_token(key: &Bound<'_, PyAny>) -> PyResult { #[cfg_attr(all(feature = "python"), pymethods)] impl QueryToken { #[pyo3(name = "__str__")] + /// Generates a string representation of this QueryToken pub fn to_python_string(&self) -> PyResult { Ok(format!("QueryToken(\"{}\", \"{}\")", self.key, self.value)) } #[pyo3(name = "__repr__")] + /// Generates a string representation of this QueryToken pub fn to_python_repr(&self) -> PyResult { self.to_python_string() } #[pyo3(name = "token")] #[staticmethod] + #[doc(hidden)] pub fn token_py(key: &Bound<'_, PyAny>, value: &Bound<'_, PyAny>) -> PyResult { let value = if let Ok(value) = value.extract::() { value.to_string() @@ -210,6 +294,7 @@ impl QueryToken { #[pyo3(name = "sort")] #[staticmethod] + #[doc(hidden)] pub fn sort_py(key: &Bound<'_, PyAny>) -> PyResult { if let Ok(tnt) = key.extract::() { Ok(QueryToken::sort(tnt)) @@ -230,6 +315,7 @@ impl QueryToken { #[pyo3(name = "anonymous")] #[staticmethod] + #[doc(hidden)] pub fn anonymous_py(key: &Bound<'_, PyAny>) -> PyResult { let key = key.extract::()?; Ok(QueryToken::anonymous(key)) @@ -237,6 +323,7 @@ impl QueryToken { #[pyo3(name = "special")] #[staticmethod] + #[doc(hidden)] pub fn special_py(key: &Bound<'_, PyAny>) -> PyResult { if let Ok(special) = key.extract::() { Ok(QueryToken::special(special)) @@ -248,9 +335,15 @@ impl QueryToken { } #[pyo3(name = "negate")] + #[doc(hidden)] pub fn negate_py(&self) -> PyResult { Ok(self.negate()) } + + /// Negates the query token. Would turn ``konosuba`` into ``-konosuba`` + pub fn __neg__(&self) -> PyResult { + Ok(self.negate()) + } } impl Display for QueryToken { @@ -273,7 +366,10 @@ impl ToQueryString for Vec { #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe named query tokens for use with [list_tags](crate::SzurubooruRequest::list_tags) pub enum TagNamedToken { /// having given name (accepts wildcards) @@ -321,7 +417,10 @@ impl<'py> FromPyObject<'py> for TagNamedToken { #[derive(Debug, AsRefStr, Eq, PartialEq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe sort query tokens for use with [list_tags](crate::SzurubooruRequest::list_tags) pub enum TagSortToken { /// as random as it can get @@ -357,7 +456,10 @@ impl SortableToken for TagSortToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe named query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts) pub enum PostNamedToken { /// having given post number @@ -392,8 +494,9 @@ pub enum PostNamedToken { RelationCount, /// having been featured given number of times FeatureCount, - /// given type of posts. `value` can be either `image`, `animation` (or `animated` or `anim`), - /// `flash` (or `swf`) or `video` (or `webm`). Use [models::PostType] for type-safe values + /// given type of posts. The value can be either `image`, `animation` (or `animated` or `anim`), + /// `flash` (or `swf`) or `video` (or `webm`). Use [PostType](crate::models::PostType) + /// for type-safe values Type, /// having given SHA1 checksum ContentChecksum, @@ -445,8 +548,8 @@ pub enum PostNamedToken { FeatureDate, /// alias of [PostNamedToken::FeatureDate] FeatureTime, - /// having given safety. can be either `safe`, `sketchy` (or `questionable`) or `unsafe` - /// Use [models::PostSafety] for the type-safe version + /// Post safety. Can be either `safe`, `sketchy` (or `questionable`) or `unsafe` + /// Use [PostSafety](crate::models::PostSafety) for the type-safe version Safety, /// alias of [PostNamedToken::Safety] Rating, @@ -455,7 +558,10 @@ impl NamedToken for PostNamedToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe sort query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts) pub enum PostSortToken { /// as random as it can get @@ -498,7 +604,7 @@ pub enum PostSortToken { Date, /// alias of [PostSortToken::CreationDate] Time, - /// like [PostSortToken::CreationDate], only looks at last edit time + /// like [PostSortToken::CreationDate], only looks at last edit time instead LastEditDate, /// alias of [PostSortToken::LastEditDate] LastEditTime, @@ -523,7 +629,10 @@ impl SortableToken for PostSortToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe special query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts) pub enum PostSpecialToken { /// posts liked by currently logged-in user @@ -539,7 +648,10 @@ impl SpecialToken for PostSpecialToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe named query tokens for use with [list_pools](crate::SzurubooruRequest::list_pools) pub enum PoolNamedToken { /// having given name (accepts wildcards) @@ -565,7 +677,10 @@ impl NamedToken for PoolNamedToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe sort query tokens for use with [list_pools](crate::SzurubooruRequest::list_pools) pub enum PoolSortToken { /// as random as it can get @@ -593,7 +708,10 @@ impl SortableToken for PoolSortToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe named query tokens for use with /// [list_comments](crate::SzurubooruRequest::list_comments) pub enum CommentNamedToken { @@ -624,7 +742,10 @@ impl NamedToken for CommentNamedToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe sort query tokens for use with /// [list_comments](crate::SzurubooruRequest::list_comments) pub enum CommentSortToken { @@ -653,7 +774,10 @@ impl SortableToken for CommentSortToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe named query tokens for use with [list_users](crate::SzurubooruRequest::list_users) pub enum UserNamedToken { /// having given name (accepts wildcards) @@ -675,7 +799,10 @@ impl NamedToken for UserNamedToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe sort query tokens for use with [list_users](crate::SzurubooruRequest::list_users) pub enum UserSortToken { /// as random as it can get @@ -699,7 +826,10 @@ impl SortableToken for UserNamedToken {} #[derive(Debug, AsRefStr, PartialEq, Eq, Clone)] #[strum(serialize_all = "kebab-case")] -#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))] +#[cfg_attr( + all(feature = "python"), + pyclass(eq, eq_int, module = "szurubooru_client.tokens") +)] /// Type-safe named query tokens for use with /// [list_snapshots](crate::SzurubooruRequest::list_snapshots) pub enum SnapshotNamedToken { diff --git a/szurubooru-client/szurubooru_client/__init__.py b/szurubooru-client/szurubooru_client/__init__.py index 86b8e12..4d3525d 100644 --- a/szurubooru-client/szurubooru_client/__init__.py +++ b/szurubooru-client/szurubooru_client/__init__.py @@ -1,4 +1,4 @@ from .szurubooru_client import * __doc__ = szurubooru_client.__doc__ -__all__ = ["SzurubooruSyncClient", "SzurubooruAsyncClient", "SzuruPyClientError"] +__all__ = ["SzurubooruSyncClient", "SzurubooruAsyncClient", "SzuruClientError", "PagedResult"] diff --git a/szurubooru-client/szurubooru_client/models.py b/szurubooru-client/szurubooru_client/models.py index 7836104..cad5d84 100644 --- a/szurubooru-client/szurubooru_client/models.py +++ b/szurubooru-client/szurubooru_client/models.py @@ -29,6 +29,6 @@ UserAvatarStyle = _models.UserAvatarStyle UserRank = _models.UserRank UserResource = _models.UserResource -__doc__ = szurubooru_client.models.__doc__ -if hasattr(szurubooru_client.models, "__all__"): - __all__ = szurubooru_client.models.__all__ +__doc__ = _models.__doc__ +#if hasattr(_models, "__all__"): +# __all__ = getattr(_models, "__all__") diff --git a/szurubooru-client/szurubooru_client/tokens.py b/szurubooru-client/szurubooru_client/tokens.py index 0cc5578..e988c4d 100644 --- a/szurubooru-client/szurubooru_client/tokens.py +++ b/szurubooru-client/szurubooru_client/tokens.py @@ -19,5 +19,5 @@ UserNamedToken = _tokens.UserNamedToken UserSortToken = _tokens.UserSortToken __doc__ = _tokens.__doc__ -if hasattr(_tokens, "__all__"): - __all__ = getattr(_tokens, "__all__") +#if hasattr(_tokens, "__all__"): +# __all__ = getattr(_tokens, "__all__")