feat(all): adding python library wrapper
Adding a Python library wrapper using pyo3 BREAKING CHANGE:
This commit is contained in:
parent
e46b858758
commit
f4ba4a9c39
12 changed files with 2566 additions and 703 deletions
175
Cargo.lock
generated
175
Cargo.lock
generated
|
|
@ -150,6 +150,28 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono-tz"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"chrono-tz-build",
|
||||||
|
"phf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono-tz-build"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1"
|
||||||
|
dependencies = [
|
||||||
|
"parse-zoneinfo",
|
||||||
|
"phf",
|
||||||
|
"phf_codegen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colored"
|
name = "colored"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
|
@ -632,6 +654,12 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
|
|
@ -702,6 +730,15 @@ version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
|
@ -887,12 +924,59 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parse-zoneinfo"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||||
|
dependencies = [
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_codegen"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.5"
|
version = "1.1.5"
|
||||||
|
|
@ -931,6 +1015,12 @@ version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
|
|
@ -949,6 +1039,72 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"chrono",
|
||||||
|
"chrono-tz",
|
||||||
|
"indoc",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
"once_cell",
|
||||||
|
"portable-atomic",
|
||||||
|
"pyo3-build-config",
|
||||||
|
"pyo3-ffi",
|
||||||
|
"pyo3-macros",
|
||||||
|
"serde",
|
||||||
|
"unindent",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-build-config"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"target-lexicon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-ffi"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"pyo3-build-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-macros"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"pyo3-macros-backend",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-macros-backend"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"pyo3-build-config",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.36"
|
version = "1.0.36"
|
||||||
|
|
@ -1290,6 +1446,12 @@ version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
|
@ -1404,6 +1566,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hex",
|
"hex",
|
||||||
"mockito",
|
"mockito",
|
||||||
|
"pyo3",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -1430,6 +1593,12 @@ dependencies = [
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "target-lexicon"
|
||||||
|
version = "0.12.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.12.0"
|
version = "3.12.0"
|
||||||
|
|
@ -1682,6 +1851,12 @@ dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unindent"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
derive_builder = "0.20.0"
|
derive_builder = "0.20.0"
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
pyo3 = { version="0.22.0", optional=true, features=["chrono-tz", "chrono", "extension-module", "serde", "experimental-async"] }
|
||||||
reqwest = { version = "0.12.5", features = ["json", "multipart", "stream"] }
|
reqwest = { version = "0.12.5", features = ["json", "multipart", "stream"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = "1.0.120"
|
serde_json = "1.0.120"
|
||||||
|
|
@ -23,6 +24,7 @@ sha1 = "0.10.6"
|
||||||
strum = { version = "0.26.3", features = ["derive", "strum_macros"] }
|
strum = { version = "0.26.3", features = ["derive", "strum_macros"] }
|
||||||
strum_macros = "0.26.4"
|
strum_macros = "0.26.4"
|
||||||
thiserror = "1.0.63"
|
thiserror = "1.0.63"
|
||||||
|
tokio = { version = "1.39.2", features = ["rt", "sync"], optional = true }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
url = "2.5.2"
|
url = "2.5.2"
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
|
|
@ -30,3 +32,10 @@ urlencoding = "2.1.3"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
mockito = "1.4.0"
|
mockito = "1.4.0"
|
||||||
tokio = { version = "1.39.2", features = ["full"] }
|
tokio = { version = "1.39.2", features = ["full"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
python = ["dep:pyo3", "dep:tokio"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "szurubooru_client"
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
|
||||||
15
szurubooru-client/pyproject.toml
Normal file
15
szurubooru-client/pyproject.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["maturin>=1.7,<2.0"]
|
||||||
|
build-backend = "maturin"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "szurubooru-client"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Rust",
|
||||||
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
|
]
|
||||||
|
dynamic = ["version"]
|
||||||
|
[tool.maturin]
|
||||||
|
features = ["pyo3/extension-module"]
|
||||||
|
|
@ -165,10 +165,15 @@ impl SzurubooruClient {
|
||||||
/// # };
|
/// # };
|
||||||
/// # ()
|
/// # ()
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_fields<'a>(&'a self, fields: Vec<&'a str>) -> SzurubooruRequest {
|
pub fn with_fields(&self, fields: Vec<String>) -> SzurubooruRequest {
|
||||||
self.request().with_fields(fields)
|
self.request().with_fields(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as (with_fields)[SzurubooruClient::with_fields], but accepts an Option type instead
|
||||||
|
pub fn with_optional_fields(&self, fields: Option<Vec<String>>) -> SzurubooruRequest {
|
||||||
|
self.request().with_optional_fields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct a new request with the given limit
|
/// Construct a new request with the given limit
|
||||||
/// The Szurubooru API supports limiting the number of resources returned for Paginated
|
/// The Szurubooru API supports limiting the number of resources returned for Paginated
|
||||||
/// API endpoints.
|
/// API endpoints.
|
||||||
|
|
@ -190,6 +195,11 @@ impl SzurubooruClient {
|
||||||
self.request().with_limit(limit)
|
self.request().with_limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as (with_limit)[SzurubooruClient::with_limit], but accepts an Option type instead
|
||||||
|
pub fn with_optional_limit(&self, limit: Option<u32>) -> SzurubooruRequest {
|
||||||
|
self.request().with_optional_limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct a new request starting at the given offset
|
/// Construct a new request starting at the given offset
|
||||||
/// The Szurubooru API supports offsetting the results returned from Paginated API
|
/// The Szurubooru API supports offsetting the results returned from Paginated API
|
||||||
/// endpoints. Use this offset in combination with the limit to page through
|
/// endpoints. Use this offset in combination with the limit to page through
|
||||||
|
|
@ -211,12 +221,17 @@ impl SzurubooruClient {
|
||||||
pub fn with_offset(&self, offset: u32) -> SzurubooruRequest {
|
pub fn with_offset(&self, offset: u32) -> SzurubooruRequest {
|
||||||
self.request().with_offset(offset)
|
self.request().with_offset(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as (with_offset)[SzurubooruClient::with_offset], but accepts an Option type instead
|
||||||
|
pub fn with_optional_offset(&self, offset: Option<u32>) -> SzurubooruRequest {
|
||||||
|
self.request().with_optional_offset(offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// A type that represents a single Szurubooru request.
|
/// A type that represents a single Szurubooru request.
|
||||||
pub struct SzurubooruRequest<'a> {
|
pub struct SzurubooruRequest<'a> {
|
||||||
fields: Option<Vec<&'a str>>,
|
fields: Option<Vec<String>>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
client: &'a SzurubooruClient,
|
client: &'a SzurubooruClient,
|
||||||
|
|
@ -250,11 +265,19 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
/// # };
|
/// # };
|
||||||
/// # ()
|
/// # ()
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_fields(mut self, fields: Vec<&'a str>) -> Self {
|
pub fn with_fields(mut self, fields: Vec<String>) -> Self {
|
||||||
self.fields = Some(fields);
|
self.fields = Some(fields);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as (with_fields)[SzurubooruRequest::with_fields], but accepts an Option type instead
|
||||||
|
pub fn with_optional_fields(mut self, val: Option<Vec<String>>) -> Self {
|
||||||
|
match val {
|
||||||
|
Some(f) => self.with_fields(f),
|
||||||
|
None => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Limit the number of returned results
|
/// Limit the number of returned results
|
||||||
/// The Szurubooru API supports limiting the number of resources returned for Paginated
|
/// The Szurubooru API supports limiting the number of resources returned for Paginated
|
||||||
/// API endpoints.
|
/// API endpoints.
|
||||||
|
|
@ -277,6 +300,14 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as (with_limit)[SzurubooruRequest::with_limit], but accepts an Option type instead
|
||||||
|
pub fn with_optional_limit(mut self, val: Option<u32>) -> Self {
|
||||||
|
match val {
|
||||||
|
Some(f) => self.with_limit(f),
|
||||||
|
None => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Skip a certain number of records
|
/// Skip a certain number of records
|
||||||
/// The Szurubooru API supports offsetting the results returned from Paginated API
|
/// The Szurubooru API supports offsetting the results returned from Paginated API
|
||||||
/// endpoints. Use this offset in combination with the limit to page through
|
/// endpoints. Use this offset in combination with the limit to page through
|
||||||
|
|
@ -300,6 +331,14 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as (with_offset)[SzurubooruRequest::with_offset], but accepts an Option type instead
|
||||||
|
pub fn with_optional_offset(self, val: Option<u32>) -> Self {
|
||||||
|
match val {
|
||||||
|
Some(f) => self.with_offset(f),
|
||||||
|
None => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn prep_request<T>(
|
fn prep_request<T>(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -406,8 +445,6 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
let response = self
|
let response = self
|
||||||
.handle_response(response.map_err(SzurubooruClientError::RequestError)?)
|
.handle_response(response.map_err(SzurubooruClientError::RequestError)?)
|
||||||
.await?;
|
.await?;
|
||||||
//.error_for_status()
|
|
||||||
//.map_err(SzurubooruClientError::RequestError)?;
|
|
||||||
|
|
||||||
let response_text = response
|
let response_text = response
|
||||||
.text()
|
.text()
|
||||||
|
|
@ -639,6 +676,23 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
.map(|pr| self.propagate_urls(pr))
|
.map(|pr| self.propagate_urls(pr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update an existing post from a given URL
|
||||||
|
/// See [SzurubooruRequest::create_post_from_url] for more details about the fields in
|
||||||
|
/// (CreateUpdatePost)[models::CreateUpdatePost]
|
||||||
|
pub async fn update_post_from_url(
|
||||||
|
&self,
|
||||||
|
post_id: u32,
|
||||||
|
update_post: &CreateUpdatePost,
|
||||||
|
) -> SzurubooruResult<PostResource> {
|
||||||
|
assert!(update_post.content_url.is_some());
|
||||||
|
let path = format!("/api/post/{post_id}");
|
||||||
|
self.create_update_post_from_url(&path, Method::PUT, update_post)
|
||||||
|
.await
|
||||||
|
.map(|pr| self.propagate_urls(pr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create function to upload by byte array in the future
|
||||||
|
|
||||||
fn part_from_file(&self, file: &mut File) -> SzurubooruResult<Part> {
|
fn part_from_file(&self, file: &mut File) -> SzurubooruResult<Part> {
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
file.read_to_end(&mut bytes)
|
file.read_to_end(&mut bytes)
|
||||||
|
|
@ -825,6 +879,27 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
.map(|pr| self.propagate_urls(pr))
|
.map(|pr| self.propagate_urls(pr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update a post from a token previously generated by
|
||||||
|
/// (upload_temporary_file_from_path)[SzurubooruRequest::upload_temporary_file_from_path]
|
||||||
|
pub async fn update_post_from_token(
|
||||||
|
&self,
|
||||||
|
post_id: u32,
|
||||||
|
update_post: &CreateUpdatePost,
|
||||||
|
) -> SzurubooruResult<PostResource> {
|
||||||
|
assert!(update_post.content_token.is_some());
|
||||||
|
let url = format!("/api/post/{post_id}");
|
||||||
|
self.create_update_post_from_file(
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None::<String>,
|
||||||
|
&url,
|
||||||
|
Method::PUT,
|
||||||
|
update_post,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|pr| self.propagate_urls(pr))
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_post_content(
|
async fn get_post_content(
|
||||||
&self,
|
&self,
|
||||||
post_id: u32,
|
post_id: u32,
|
||||||
|
|
@ -989,32 +1064,38 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
.map(|isr| self.propagate_urls(isr))
|
.map(|isr| self.propagate_urls(isr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Need to add a reverse search for bytes
|
||||||
|
|
||||||
/// Searches for an exact match of a file based on the SHA1 checksum
|
/// Searches for an exact match of a file based on the SHA1 checksum
|
||||||
pub async fn posts_for_file(
|
pub async fn posts_for_file(
|
||||||
&self,
|
&self,
|
||||||
mut file: &mut File,
|
mut file: &mut File,
|
||||||
) -> SzurubooruResult<PagedSearchResult<PostResource>> {
|
) -> SzurubooruResult<Option<PostResource>> {
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
std::io::copy(&mut file, &mut hasher).map_err(SzurubooruClientError::IOError)?;
|
std::io::copy(&mut file, &mut hasher).map_err(SzurubooruClientError::IOError)?;
|
||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
let hex_string = hex::encode(hash);
|
let hex_string = hex::encode(hash);
|
||||||
|
|
||||||
let qt = QueryToken::token(PostNamedToken::ContentChecksum, hex_string);
|
let qt = QueryToken::token(PostNamedToken::ContentChecksum, hex_string);
|
||||||
self.list_posts(Some(&vec![qt]))
|
let mut psr = self
|
||||||
|
.list_posts(Some(&vec![qt]))
|
||||||
.await
|
.await
|
||||||
.map(|psr| self.propagate_urls(psr))
|
.map(|psr| self.propagate_urls(psr))?;
|
||||||
|
Ok(if psr.total > 1 {
|
||||||
|
Some(psr.results.swap_remove(0))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Searches for an exact match of a file path based on the SHA1 checksum
|
/// Searches for an exact match of a file path based on the SHA1 checksum
|
||||||
pub async fn posts_for_file_path(
|
pub async fn posts_for_file_path(
|
||||||
&self,
|
&self,
|
||||||
file_path: impl AsRef<Path>,
|
file_path: impl AsRef<Path>,
|
||||||
) -> SzurubooruResult<PagedSearchResult<PostResource>> {
|
) -> SzurubooruResult<Option<PostResource>> {
|
||||||
let mut file = File::open(file_path).map_err(SzurubooruClientError::IOError)?;
|
let mut file = File::open(file_path).map_err(SzurubooruClientError::IOError)?;
|
||||||
|
|
||||||
self.posts_for_file(&mut file)
|
self.posts_for_file(&mut file).await
|
||||||
.await
|
|
||||||
.map(|psr| self.propagate_urls(psr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves information about an existing post.
|
/// Retrieves information about an existing post.
|
||||||
|
|
@ -1516,13 +1597,13 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
/// instead of a password.
|
/// instead of a password.
|
||||||
pub async fn create_user_token<T>(
|
pub async fn create_user_token<T>(
|
||||||
&self,
|
&self,
|
||||||
name: T,
|
user_name: T,
|
||||||
create_token: &CreateUpdateUserAuthToken,
|
create_token: &CreateUpdateUserAuthToken,
|
||||||
) -> SzurubooruResult<UserAuthTokenResource>
|
) -> SzurubooruResult<UserAuthTokenResource>
|
||||||
where
|
where
|
||||||
T: AsRef<str> + Display,
|
T: AsRef<str> + Display,
|
||||||
{
|
{
|
||||||
let path = format!("/api/user-token/{name}");
|
let path = format!("/api/user-token/{user_name}");
|
||||||
self.do_request(Method::POST, &path, None, Some(create_token))
|
self.do_request(Method::POST, &path, None, Some(create_token))
|
||||||
.await
|
.await
|
||||||
.map(|r| self.propagate_urls(r))
|
.map(|r| self.propagate_urls(r))
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
use crate::models::SzuruEither;
|
use crate::models::SzuruEither;
|
||||||
use base64::EncodeSliceError;
|
use base64::EncodeSliceError;
|
||||||
|
use derive_builder::UninitializedFieldError;
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
use pyo3::{exceptions::PyRuntimeError, prelude::*};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use url::ParseError as UParseError;
|
use url::ParseError as UParseError;
|
||||||
|
|
@ -47,6 +50,9 @@ pub enum SzurubooruClientError {
|
||||||
/// Error serializing an object as JSON
|
/// Error serializing an object as JSON
|
||||||
#[error("JSON Serialization error: {0}")]
|
#[error("JSON Serialization error: {0}")]
|
||||||
JSONSerializationError(#[source] serde_json::Error),
|
JSONSerializationError(#[source] serde_json::Error),
|
||||||
|
/// Error when validation fails for one of the Builder types
|
||||||
|
#[error("Builder validation error: {0}")]
|
||||||
|
BuilderValidationError(String),
|
||||||
/// Error occurred when reading a file
|
/// Error occurred when reading a file
|
||||||
#[error("IO Error: {0}")]
|
#[error("IO Error: {0}")]
|
||||||
IOError(#[source] std::io::Error),
|
IOError(#[source] std::io::Error),
|
||||||
|
|
@ -61,6 +67,19 @@ impl From<SzurubooruServerError> for SzurubooruClientError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<UninitializedFieldError> for SzurubooruClientError {
|
||||||
|
fn from(value: UninitializedFieldError) -> Self {
|
||||||
|
SzurubooruClientError::BuilderValidationError(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
impl std::convert::From<SzurubooruClientError> for PyErr {
|
||||||
|
fn from(value: SzurubooruClientError) -> Self {
|
||||||
|
PyRuntimeError::new_err(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Type used to represent success or a failure of some kind
|
/// Type used to represent success or a failure of some kind
|
||||||
pub type SzurubooruResult<T> = Result<T, SzurubooruClientError>;
|
pub type SzurubooruResult<T> = Result<T, SzurubooruClientError>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,42 @@ pub mod errors;
|
||||||
pub use errors::SzurubooruResult;
|
pub use errors::SzurubooruResult;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
pub mod pyclient;
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
pub mod py;
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
#[cfg_attr(feature = "python", pymodule)]
|
||||||
|
mod szurubooru_client {
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pymodule_export]
|
||||||
|
pub use crate::{
|
||||||
|
models::{
|
||||||
|
AroundPostResult, CommentResource, CreateUpdateComment, CreateUpdatePool,
|
||||||
|
CreateUpdatePoolCategory, CreateUpdatePost, CreateUpdateTag, CreateUpdateTagCategory,
|
||||||
|
CreateUpdateUser, CreateUpdateUserAuthToken, GlobalInfo, ImageSearchResult,
|
||||||
|
ImageSearchSimilarPost, MergePool, MergePost, MergeTags, MicroPoolResource,
|
||||||
|
MicroPostResource, MicroTagResource, MicroUserResource, NoteResource,
|
||||||
|
PoolCategoryResource, PoolResource, PostResource, PostSafety, PostType,
|
||||||
|
SnapshotCreationDeletionData, SnapshotData, SnapshotModificationData,
|
||||||
|
SnapshotOperationType, SnapshotResource, SnapshotResourceType, TagCategoryResource,
|
||||||
|
TagResource, TagSibling, TemporaryFileUpload, UserAuthTokenResource, UserAvatarStyle,
|
||||||
|
UserRank, UserResource,
|
||||||
|
},
|
||||||
|
py::asynchronous::PythonAsyncClient,
|
||||||
|
py::synchronous::PythonSyncClient,
|
||||||
|
tokens::{
|
||||||
|
anonymous_token, named_token, sort_token, special_token, CommentNamedToken,
|
||||||
|
CommentSortToken, PoolNamedToken, PoolSortToken, PostNamedToken, PostSortToken,
|
||||||
|
PostSpecialToken, SnapshotNamedToken, TagNamedToken, TagSortToken, UserNamedToken,
|
||||||
|
UserSortToken,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
1147
szurubooru-client/src/py/asynchronous.rs
Normal file
1147
szurubooru-client/src/py/asynchronous.rs
Normal file
File diff suppressed because it is too large
Load diff
34
szurubooru-client/src/py/mod.rs
Normal file
34
szurubooru-client/src/py/mod.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::models::PagedSearchResult;
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
pub mod asynchronous;
|
||||||
|
pub mod synchronous;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[pyclass(name = "PagedSearchResult", get_all)]
|
||||||
|
pub struct PyPagedSearchResult {
|
||||||
|
pub query: String,
|
||||||
|
pub offset: u32,
|
||||||
|
pub limit: u32,
|
||||||
|
pub total: u32,
|
||||||
|
pub results: PyObject,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(all(feature = "python"), pymethods)]
|
||||||
|
impl PyPagedSearchResult {
|
||||||
|
fn __repr__(&self) -> String {
|
||||||
|
format!("{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IntoPy<PyObject>> From<PagedSearchResult<T>> for PyPagedSearchResult {
|
||||||
|
fn from(value: PagedSearchResult<T>) -> Self {
|
||||||
|
Python::with_gil(|py| PyPagedSearchResult {
|
||||||
|
query: value.query,
|
||||||
|
offset: value.offset,
|
||||||
|
limit: value.limit,
|
||||||
|
total: value.total,
|
||||||
|
results: value.results.into_py(py),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
709
szurubooru-client/src/py/synchronous.rs
Normal file
709
szurubooru-client/src/py/synchronous.rs
Normal file
|
|
@ -0,0 +1,709 @@
|
||||||
|
use crate::models::*;
|
||||||
|
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 tokio::runtime::{Builder, Runtime};
|
||||||
|
|
||||||
|
#[pyclass(name = "SzurubooruSyncClient")]
|
||||||
|
pub struct PythonSyncClient {
|
||||||
|
client: PythonAsyncClient,
|
||||||
|
runtime: Runtime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PythonSyncClient {
|
||||||
|
#[new]
|
||||||
|
#[pyo3(signature = (host, username=None, token=None, password=None, allow_insecure=None))]
|
||||||
|
pub fn new(
|
||||||
|
host: String,
|
||||||
|
username: Option<String>,
|
||||||
|
token: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
allow_insecure: Option<bool>,
|
||||||
|
) -> PyResult<Self> {
|
||||||
|
let runtime = Builder::new_current_thread().enable_all().build()?;
|
||||||
|
let client = PythonAsyncClient::new(host, username, token, password, allow_insecure)?;
|
||||||
|
Ok(Self { client, runtime })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (fields=None))]
|
||||||
|
pub fn list_tag_categories(
|
||||||
|
&self,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<Vec<TagCategoryResource>> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_tag_categories(fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, version, color=None, order=None, fields=None))]
|
||||||
|
pub fn update_tag_category(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
version: u32,
|
||||||
|
color: Option<String>,
|
||||||
|
order: Option<u32>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<TagCategoryResource> {
|
||||||
|
self.runtime.block_on(
|
||||||
|
self.client
|
||||||
|
.update_tag_category(name, version, color, order, fields),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, fields=None))]
|
||||||
|
pub fn get_tag_category(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<TagCategoryResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.get_tag_category(name, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_tag_category(&self, name: String, version: u32) -> PyResult<()> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.delete_tag_category(name, version))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))]
|
||||||
|
pub fn list_tags(
|
||||||
|
&self,
|
||||||
|
query: Option<Vec<QueryToken>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
offset: Option<u32>,
|
||||||
|
) -> PyResult<PyPagedSearchResult> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_tags(query, fields, limit, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (names, category=None, description=None, implications=None, suggestions=None, fields=None))]
|
||||||
|
pub fn create_tag(
|
||||||
|
&self,
|
||||||
|
names: Vec<String>,
|
||||||
|
category: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
implications: Option<Vec<String>>,
|
||||||
|
suggestions: Option<Vec<String>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<TagResource> {
|
||||||
|
self.runtime.block_on(self.client.create_tag(
|
||||||
|
names,
|
||||||
|
category,
|
||||||
|
description,
|
||||||
|
implications,
|
||||||
|
suggestions,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, names, category=None, description=None, implications=None, suggestions=None, fields=None))]
|
||||||
|
pub fn update_tag(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
names: Option<Vec<String>>,
|
||||||
|
category: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
implications: Option<Vec<String>>,
|
||||||
|
suggestions: Option<Vec<String>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<TagResource> {
|
||||||
|
self.runtime.block_on(self.client.update_tag(
|
||||||
|
name,
|
||||||
|
names,
|
||||||
|
category,
|
||||||
|
description,
|
||||||
|
implications,
|
||||||
|
suggestions,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, fields=None))]
|
||||||
|
pub fn get_tag(&self, name: String, fields: Option<Vec<String>>) -> PyResult<TagResource> {
|
||||||
|
self.runtime.block_on(self.client.get_tag(name, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))]
|
||||||
|
pub fn merge_tag(
|
||||||
|
&self,
|
||||||
|
remove_tag: String,
|
||||||
|
remove_tag_version: u32,
|
||||||
|
merge_to_tag: String,
|
||||||
|
merge_to_version: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<TagResource> {
|
||||||
|
self.runtime.block_on(self.client.merge_tag(
|
||||||
|
remove_tag,
|
||||||
|
remove_tag_version,
|
||||||
|
merge_to_tag,
|
||||||
|
merge_to_version,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tag_siblings(&self, name: String) -> PyResult<Vec<TagSibling>> {
|
||||||
|
self.runtime.block_on(self.client.get_tag_siblings(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (query=None, fields=None, limit=None, offset=None))]
|
||||||
|
pub fn list_posts(
|
||||||
|
&self,
|
||||||
|
query: Option<Vec<QueryToken>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
offset: Option<u32>,
|
||||||
|
) -> PyResult<PyPagedSearchResult> {
|
||||||
|
self.runtime
|
||||||
|
.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,
|
||||||
|
relations=None, notes=None, flags=None, fields=None))]
|
||||||
|
pub fn create_post(
|
||||||
|
&self,
|
||||||
|
url: Option<String>,
|
||||||
|
token: Option<String>,
|
||||||
|
file_path: Option<PathBuf>,
|
||||||
|
thumbnail_path: Option<PathBuf>,
|
||||||
|
tags: Option<Vec<String>>,
|
||||||
|
safety: Option<PostSafety>,
|
||||||
|
source: Option<String>,
|
||||||
|
relations: Option<Vec<u32>>,
|
||||||
|
notes: Option<Vec<NoteResource>>,
|
||||||
|
flags: Option<Vec<String>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PostResource> {
|
||||||
|
self.runtime.block_on(self.client.create_post(
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
file_path,
|
||||||
|
thumbnail_path,
|
||||||
|
tags,
|
||||||
|
safety,
|
||||||
|
source,
|
||||||
|
relations,
|
||||||
|
notes,
|
||||||
|
flags,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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))]
|
||||||
|
pub fn update_post(
|
||||||
|
&self,
|
||||||
|
post_id: u32,
|
||||||
|
post_version: u32,
|
||||||
|
url: Option<String>,
|
||||||
|
token: Option<String>,
|
||||||
|
file_path: Option<PathBuf>,
|
||||||
|
thumbnail_path: Option<PathBuf>,
|
||||||
|
tags: Option<Vec<String>>,
|
||||||
|
safety: Option<PostSafety>,
|
||||||
|
source: Option<String>,
|
||||||
|
relations: Option<Vec<u32>>,
|
||||||
|
notes: Option<Vec<NoteResource>>,
|
||||||
|
flags: Option<Vec<String>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PostResource> {
|
||||||
|
self.runtime.block_on(self.client.update_post(
|
||||||
|
post_id,
|
||||||
|
post_version,
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
file_path,
|
||||||
|
thumbnail_path,
|
||||||
|
tags,
|
||||||
|
safety,
|
||||||
|
source,
|
||||||
|
relations,
|
||||||
|
notes,
|
||||||
|
flags,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_image_bytes<'py>(&self, post_id: u32) -> PyResult<Vec<u8>> {
|
||||||
|
self.runtime.block_on(self.client.get_image_bytes(post_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Vec<u8>> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.get_thumbnail_bytes(post_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reverse_search_image(&self, image_path: PathBuf) -> PyResult<ImageSearchResult> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.reverse_search_image(image_path))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_for_image(&self, image_path: PathBuf) -> PyResult<Option<PostResource>> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.post_for_image(image_path))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (post_id, fields=None))]
|
||||||
|
pub fn get_post(&self, post_id: u32, fields: Option<Vec<String>>) -> PyResult<PostResource> {
|
||||||
|
self.runtime.block_on(self.client.get_post(post_id, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_around_post(&self, post_id: u32) -> PyResult<AroundPostResult> {
|
||||||
|
self.runtime.block_on(self.client.get_around_post(post_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_post(&self, post_id: u32, version: u32) -> PyResult<()> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.delete_post(post_id, version))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (remove_post, remove_post_version, merge_to_post,
|
||||||
|
merge_to_version, fields=None))]
|
||||||
|
pub fn merge_post(
|
||||||
|
&self,
|
||||||
|
remove_post: u32,
|
||||||
|
remove_post_version: u32,
|
||||||
|
merge_to_post: u32,
|
||||||
|
merge_to_version: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PostResource> {
|
||||||
|
self.runtime.block_on(self.client.merge_post(
|
||||||
|
remove_post,
|
||||||
|
remove_post_version,
|
||||||
|
merge_to_post,
|
||||||
|
merge_to_version,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (post_id, rating, fields=None))]
|
||||||
|
pub fn rate_post(
|
||||||
|
&self,
|
||||||
|
post_id: u32,
|
||||||
|
rating: i8,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PostResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.rate_post(post_id, rating, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (post_id, fields=None))]
|
||||||
|
pub fn favorite_post(
|
||||||
|
&self,
|
||||||
|
post_id: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PostResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.favorite_post(post_id, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (post_id, fields=None))]
|
||||||
|
pub fn unfavorite_post(
|
||||||
|
&self,
|
||||||
|
post_id: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PostResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.unfavorite_post(post_id, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (fields=None))]
|
||||||
|
pub fn get_featured_post(&self, fields: Option<Vec<String>>) -> PyResult<Option<PostResource>> {
|
||||||
|
self.runtime.block_on(self.client.get_featured_post(fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (post_id, fields=None))]
|
||||||
|
pub fn set_featured_post(
|
||||||
|
&self,
|
||||||
|
post_id: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PostResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.set_featured_post(post_id, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (fields=None))]
|
||||||
|
pub fn list_pool_categories(
|
||||||
|
&self,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<Vec<PoolCategoryResource>> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_pool_categories(fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, color=None, fields=None))]
|
||||||
|
pub fn create_pool_category(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
color: Option<String>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PoolCategoryResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.create_pool_category(name, color, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, version, new_name=None, color=None, fields=None))]
|
||||||
|
pub fn update_pool_category(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
version: u32,
|
||||||
|
new_name: Option<String>,
|
||||||
|
color: Option<String>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PoolCategoryResource> {
|
||||||
|
self.runtime.block_on(
|
||||||
|
self.client
|
||||||
|
.update_pool_category(name, version, new_name, color, fields),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, fields=None))]
|
||||||
|
pub fn get_pool_category(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PoolCategoryResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.get_pool_category(name, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))]
|
||||||
|
pub fn set_default_pool_category(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PoolCategoryResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.set_default_pool_category(name, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (query=None, fields=None, limit=None, offset=None))]
|
||||||
|
pub fn list_pools(
|
||||||
|
&self,
|
||||||
|
query: Option<Vec<QueryToken>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
offset: Option<u32>,
|
||||||
|
) -> PyResult<PyPagedSearchResult> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_pools(query, fields, limit, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (names, category=None, description=None, posts=None, fields=None))]
|
||||||
|
pub fn create_pool(
|
||||||
|
&self,
|
||||||
|
names: Vec<String>,
|
||||||
|
category: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
posts: Option<Vec<u32>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PoolResource> {
|
||||||
|
self.runtime.block_on(
|
||||||
|
self.client
|
||||||
|
.create_pool(names, category, description, posts, fields),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (pool_id, version, names=None, category=None, description=None,
|
||||||
|
posts=None, fields=None))]
|
||||||
|
pub fn update_pool(
|
||||||
|
&self,
|
||||||
|
pool_id: u32,
|
||||||
|
version: u32,
|
||||||
|
names: Option<Vec<String>>,
|
||||||
|
category: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
posts: Option<Vec<u32>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PoolResource> {
|
||||||
|
self.runtime.block_on(self.client.update_pool(
|
||||||
|
pool_id,
|
||||||
|
version,
|
||||||
|
names,
|
||||||
|
category,
|
||||||
|
description,
|
||||||
|
posts,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (pool_id, fields=None))]
|
||||||
|
pub fn get_pool(&self, pool_id: u32, fields: Option<Vec<String>>) -> PyResult<PoolResource> {
|
||||||
|
self.runtime.block_on(self.client.get_pool(pool_id, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))]
|
||||||
|
pub fn merge_pools(
|
||||||
|
&self,
|
||||||
|
remove_pool: u32,
|
||||||
|
remove_pool_version: u32,
|
||||||
|
merge_to_pool: u32,
|
||||||
|
merge_to_version: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<PoolResource> {
|
||||||
|
self.runtime.block_on(self.client.merge_pools(
|
||||||
|
remove_pool,
|
||||||
|
remove_pool_version,
|
||||||
|
merge_to_pool,
|
||||||
|
merge_to_version,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (query=None, fields=None, limit=None, offset=None))]
|
||||||
|
pub fn list_comments(
|
||||||
|
&self,
|
||||||
|
query: Option<Vec<QueryToken>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
offset: Option<u32>,
|
||||||
|
) -> PyResult<PyPagedSearchResult> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_comments(query, fields, limit, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (text, post_id, fields=None))]
|
||||||
|
pub fn create_comment(
|
||||||
|
&self,
|
||||||
|
text: String,
|
||||||
|
post_id: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<CommentResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.create_comment(text, post_id, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (comment_id, version, text, fields=None))]
|
||||||
|
pub fn update_comment(
|
||||||
|
&self,
|
||||||
|
comment_id: u32,
|
||||||
|
version: u32,
|
||||||
|
text: String,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<CommentResource> {
|
||||||
|
self.runtime.block_on(
|
||||||
|
self.client
|
||||||
|
.update_comment(comment_id, version, text, fields),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (comment_id, fields=None))]
|
||||||
|
pub fn get_comment(
|
||||||
|
&self,
|
||||||
|
comment_id: u32,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<CommentResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.get_comment(comment_id, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))]
|
||||||
|
pub fn rate_comment(
|
||||||
|
&self,
|
||||||
|
comment_id: u32,
|
||||||
|
rating: i8,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<CommentResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.rate_comment(comment_id, rating, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (query=None, fields=None, limit=None, offset=None))]
|
||||||
|
pub fn list_users(
|
||||||
|
&self,
|
||||||
|
query: Option<Vec<QueryToken>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
offset: Option<u32>,
|
||||||
|
) -> PyResult<PyPagedSearchResult> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_users(query, fields, limit, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, password, rank=None, avatar_path=None, fields=None))]
|
||||||
|
pub fn create_user(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
password: String,
|
||||||
|
rank: Option<UserRank>,
|
||||||
|
avatar_path: Option<PathBuf>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<UserResource> {
|
||||||
|
self.runtime.block_on(
|
||||||
|
self.client
|
||||||
|
.create_user(name, password, rank, avatar_path, fields),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (name, version, new_name=None, password=None, rank=None, avatar_path=None, fields=None))]
|
||||||
|
pub fn update_user(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
version: u32,
|
||||||
|
new_name: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
rank: Option<UserRank>,
|
||||||
|
avatar_path: Option<PathBuf>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<UserResource> {
|
||||||
|
self.runtime.block_on(self.client.update_user(
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
new_name,
|
||||||
|
password,
|
||||||
|
rank,
|
||||||
|
avatar_path,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (user_name, fields=None))]
|
||||||
|
pub fn get_user(
|
||||||
|
&self,
|
||||||
|
user_name: String,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<UserResource> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.get_user(user_name, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))]
|
||||||
|
pub fn list_user_tokens(
|
||||||
|
&self,
|
||||||
|
user_name: String,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<Vec<UserAuthTokenResource>> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_user_tokens(user_name, fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (user_name, note=None, expiration_time=None, fields=None))]
|
||||||
|
pub fn create_user_token(
|
||||||
|
&self,
|
||||||
|
user_name: String,
|
||||||
|
note: Option<String>,
|
||||||
|
expiration_time: Option<DateTime<Utc>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<UserAuthTokenResource> {
|
||||||
|
self.runtime.block_on(self.client.create_user_token(
|
||||||
|
user_name,
|
||||||
|
note,
|
||||||
|
expiration_time,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (user_name, token, version, enabled=None, note=None, expiration_time=None, fields=None))]
|
||||||
|
pub fn update_user_token(
|
||||||
|
&self,
|
||||||
|
user_name: String,
|
||||||
|
token: String,
|
||||||
|
version: u32,
|
||||||
|
enabled: Option<bool>,
|
||||||
|
note: Option<String>,
|
||||||
|
expiration_time: Option<DateTime<Utc>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
) -> PyResult<UserAuthTokenResource> {
|
||||||
|
self.runtime.block_on(self.client.update_user_token(
|
||||||
|
user_name,
|
||||||
|
token,
|
||||||
|
version,
|
||||||
|
enabled,
|
||||||
|
note,
|
||||||
|
expiration_time,
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_user_token(
|
||||||
|
&self,
|
||||||
|
user_name: String,
|
||||||
|
token: String,
|
||||||
|
version: u32,
|
||||||
|
) -> PyResult<()> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.delete_user_token(user_name, token, version))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn password_reset_request(&self, email_or_name: String) -> PyResult<()> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.password_reset_request(email_or_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn password_reset_confirm(
|
||||||
|
&self,
|
||||||
|
email_or_name: String,
|
||||||
|
reset_token: String,
|
||||||
|
) -> PyResult<String> {
|
||||||
|
self.runtime.block_on(
|
||||||
|
self.client
|
||||||
|
.password_reset_confirm(email_or_name, reset_token),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (query=None, fields=None, limit=None, offset=None))]
|
||||||
|
pub fn list_snapshots(
|
||||||
|
&self,
|
||||||
|
query: Option<Vec<QueryToken>>,
|
||||||
|
fields: Option<Vec<String>>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
offset: Option<u32>,
|
||||||
|
) -> PyResult<PyPagedSearchResult> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.list_snapshots(query, fields, limit, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_info(&self) -> PyResult<GlobalInfo> {
|
||||||
|
self.runtime.block_on(self.client.global_info())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upload_temporary_file(&self, file_path: PathBuf) -> PyResult<TemporaryFileUpload> {
|
||||||
|
self.runtime
|
||||||
|
.block_on(self.client.upload_temporary_file(file_path))
|
||||||
|
}
|
||||||
|
}
|
||||||
1
szurubooru-client/src/pyclient.rs
Normal file
1
szurubooru-client/src/pyclient.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
//! warned that the types here help with the Type safety for the Tag names only. It does
|
//! warned that the types here help with the Type safety for the Tag names only. It does
|
||||||
//! not guarantee that a given API endpoint will support the given tag.
|
//! not guarantee that a given API endpoint will support the given tag.
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
use pyo3::{exceptions::PyValueError, prelude::*, types::*};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
use strum_macros::AsRefStr;
|
use strum_macros::AsRefStr;
|
||||||
|
|
||||||
/// A named token such as `foo:bar`
|
/// A named token such as `foo:bar`
|
||||||
|
|
@ -22,7 +25,8 @@ pub trait ToQueryString {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A query token using for searching posts, tags and pools
|
/// A query token using for searching posts, tags and pools
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass)]
|
||||||
pub struct QueryToken {
|
pub struct QueryToken {
|
||||||
/// The key for this token. For `foo:bar` this would be `foo`
|
/// The key for this token. For `foo:bar` this would be `foo`
|
||||||
pub key: String,
|
pub key: String,
|
||||||
|
|
@ -116,7 +120,7 @@ impl QueryToken {
|
||||||
/// let liked_posts = QueryToken::special(PostSpecialToken::Liked);
|
/// let liked_posts = QueryToken::special(PostSpecialToken::Liked);
|
||||||
/// client.request().list_posts(Some(&vec![liked_posts]));
|
/// client.request().list_posts(Some(&vec![liked_posts]));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn special(key: impl SpecialToken) -> Self {
|
pub fn special(key: impl AsRef<str>) -> Self {
|
||||||
QueryToken::anonymous(key)
|
QueryToken::anonymous(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,6 +143,112 @@ impl QueryToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyfunction)]
|
||||||
|
pub fn named_token(key: &Bound<'_, PyAny>, value: &Bound<'_, PyAny>) -> PyResult<QueryToken> {
|
||||||
|
QueryToken::token_py(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyfunction)]
|
||||||
|
pub fn sort_token(key: &Bound<'_, PyAny>) -> PyResult<QueryToken> {
|
||||||
|
QueryToken::sort_py(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyfunction)]
|
||||||
|
pub fn anonymous_token(key: &Bound<'_, PyString>) -> PyResult<QueryToken> {
|
||||||
|
QueryToken::anonymous_py(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyfunction)]
|
||||||
|
pub fn special_token(key: &Bound<'_, PyAny>) -> PyResult<QueryToken> {
|
||||||
|
QueryToken::special_py(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pymethods)]
|
||||||
|
impl QueryToken {
|
||||||
|
#[pyo3(name = "__str__")]
|
||||||
|
pub fn to_python_string(&self) -> PyResult<String> {
|
||||||
|
Ok(format!("QueryToken(\"{}\", \"{}\")", self.key, self.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(name = "__repr__")]
|
||||||
|
pub fn to_python_repr(&self) -> PyResult<String> {
|
||||||
|
self.to_python_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(name = "token")]
|
||||||
|
#[staticmethod]
|
||||||
|
pub fn token_py(key: &Bound<'_, PyAny>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||||
|
let value = value.extract::<String>()?;
|
||||||
|
|
||||||
|
if let Ok(tnt) = key.extract::<TagNamedToken>() {
|
||||||
|
Ok(QueryToken::token(tnt, value))
|
||||||
|
} else if let Ok(pnt) = key.extract::<PostNamedToken>() {
|
||||||
|
Ok(QueryToken::token(pnt, value))
|
||||||
|
} else if let Ok(pnt) = key.extract::<PoolNamedToken>() {
|
||||||
|
Ok(QueryToken::token(pnt, value))
|
||||||
|
} else if let Ok(comment) = key.extract::<CommentNamedToken>() {
|
||||||
|
Ok(QueryToken::token(comment, value))
|
||||||
|
} else if let Ok(user) = key.extract::<UserNamedToken>() {
|
||||||
|
Ok(QueryToken::token(user, value))
|
||||||
|
} else if let Ok(x) = key.extract::<SnapshotNamedToken>() {
|
||||||
|
Ok(QueryToken::token(x, value))
|
||||||
|
} else if let Ok(strvalue) = key.extract::<String>() {
|
||||||
|
Ok(QueryToken::token(strvalue, value))
|
||||||
|
} else {
|
||||||
|
Err(PyErr::new::<PyValueError, _>("Invalid value type for key"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(name = "sort")]
|
||||||
|
#[staticmethod]
|
||||||
|
pub fn sort_py(key: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||||
|
if let Ok(tnt) = key.extract::<TagSortToken>() {
|
||||||
|
Ok(QueryToken::sort(tnt))
|
||||||
|
} else if let Ok(pnt) = key.extract::<PostSortToken>() {
|
||||||
|
Ok(QueryToken::sort(pnt))
|
||||||
|
} else if let Ok(pnt) = key.extract::<PoolSortToken>() {
|
||||||
|
Ok(QueryToken::sort(pnt))
|
||||||
|
} else if let Ok(comment) = key.extract::<CommentSortToken>() {
|
||||||
|
Ok(QueryToken::sort(comment))
|
||||||
|
} else if let Ok(user) = key.extract::<UserSortToken>() {
|
||||||
|
Ok(QueryToken::sort(user))
|
||||||
|
} else if let Ok(strvalue) = key.extract::<String>() {
|
||||||
|
Ok(QueryToken::sort(strvalue))
|
||||||
|
} else {
|
||||||
|
Err(PyErr::new::<PyValueError, _>("Invalid value type for key"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(name = "anonymous")]
|
||||||
|
#[staticmethod]
|
||||||
|
pub fn anonymous_py(key: &Bound<'_, PyString>) -> PyResult<Self> {
|
||||||
|
let key = key.extract::<String>()?;
|
||||||
|
Ok(QueryToken::anonymous(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(name = "special")]
|
||||||
|
#[staticmethod]
|
||||||
|
pub fn special_py(key: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||||
|
if let Ok(special) = key.extract::<PostSpecialToken>() {
|
||||||
|
Ok(QueryToken::special(special))
|
||||||
|
} else if let Ok(strvalue) = key.extract::<String>() {
|
||||||
|
Ok(QueryToken::special(strvalue))
|
||||||
|
} else {
|
||||||
|
Err(PyErr::new::<PyValueError, _>("Invalid value type for key"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(name = "negate")]
|
||||||
|
pub fn negate_py(&self) -> PyResult<Self> {
|
||||||
|
Ok(self.negate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for QueryToken {
|
impl Display for QueryToken {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let suffix = if !self.value.is_empty() {
|
let suffix = if !self.value.is_empty() {
|
||||||
|
|
@ -157,8 +267,9 @@ impl ToQueryString for Vec<QueryToken> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe named query tokens for use with [list_tags](crate::SzurubooruRequest::list_tags)
|
/// Type-safe named query tokens for use with [list_tags](crate::SzurubooruRequest::list_tags)
|
||||||
pub enum TagNamedToken {
|
pub enum TagNamedToken {
|
||||||
/// having given name (accepts wildcards)
|
/// having given name (accepts wildcards)
|
||||||
|
|
@ -188,8 +299,25 @@ pub enum TagNamedToken {
|
||||||
}
|
}
|
||||||
impl NamedToken for TagNamedToken {}
|
impl NamedToken for TagNamedToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
/*#[cfg(feature="python")]
|
||||||
|
impl<'py> FromPyObject<'py> for TagNamedToken {
|
||||||
|
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||||
|
/*use pyo3::exceptions::PyTypeError;
|
||||||
|
if ob.is_instance_of::<TagNamedToken>() {
|
||||||
|
Ok()
|
||||||
|
}
|
||||||
|
let strvalue = ob.extract::<String>()?;
|
||||||
|
match TagNamedToken::from_str(&strvalue) {
|
||||||
|
Ok(tnt) => Ok(tnt),
|
||||||
|
Err(_) => Err(PyTypeError::new_err("Invalid variant"))
|
||||||
|
}*/
|
||||||
|
Ok(ob.downcast_into_exact::<Self>()?.)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[derive(Debug, AsRefStr, Eq, PartialEq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe sort query tokens for use with [list_tags](crate::SzurubooruRequest::list_tags)
|
/// Type-safe sort query tokens for use with [list_tags](crate::SzurubooruRequest::list_tags)
|
||||||
pub enum TagSortToken {
|
pub enum TagSortToken {
|
||||||
/// as random as it can get
|
/// as random as it can get
|
||||||
|
|
@ -223,8 +351,9 @@ pub enum TagSortToken {
|
||||||
}
|
}
|
||||||
impl SortableToken for TagSortToken {}
|
impl SortableToken for TagSortToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe named query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts)
|
/// Type-safe named query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts)
|
||||||
pub enum PostNamedToken {
|
pub enum PostNamedToken {
|
||||||
/// having given post number
|
/// having given post number
|
||||||
|
|
@ -320,8 +449,9 @@ pub enum PostNamedToken {
|
||||||
}
|
}
|
||||||
impl NamedToken for PostNamedToken {}
|
impl NamedToken for PostNamedToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe sort query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts)
|
/// Type-safe sort query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts)
|
||||||
pub enum PostSortToken {
|
pub enum PostSortToken {
|
||||||
/// as random as it can get
|
/// as random as it can get
|
||||||
|
|
@ -387,8 +517,9 @@ pub enum PostSortToken {
|
||||||
}
|
}
|
||||||
impl SortableToken for PostSortToken {}
|
impl SortableToken for PostSortToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe special query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts)
|
/// Type-safe special query tokens for use with [list_posts](crate::SzurubooruRequest::list_posts)
|
||||||
pub enum PostSpecialToken {
|
pub enum PostSpecialToken {
|
||||||
/// posts liked by currently logged-in user
|
/// posts liked by currently logged-in user
|
||||||
|
|
@ -402,8 +533,9 @@ pub enum PostSpecialToken {
|
||||||
}
|
}
|
||||||
impl SpecialToken for PostSpecialToken {}
|
impl SpecialToken for PostSpecialToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe named query tokens for use with [list_pools](crate::SzurubooruRequest::list_pools)
|
/// Type-safe named query tokens for use with [list_pools](crate::SzurubooruRequest::list_pools)
|
||||||
pub enum PoolNamedToken {
|
pub enum PoolNamedToken {
|
||||||
/// having given name (accepts wildcards)
|
/// having given name (accepts wildcards)
|
||||||
|
|
@ -427,8 +559,9 @@ pub enum PoolNamedToken {
|
||||||
}
|
}
|
||||||
impl NamedToken for PoolNamedToken {}
|
impl NamedToken for PoolNamedToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe sort query tokens for use with [list_pools](crate::SzurubooruRequest::list_pools)
|
/// Type-safe sort query tokens for use with [list_pools](crate::SzurubooruRequest::list_pools)
|
||||||
pub enum PoolSortToken {
|
pub enum PoolSortToken {
|
||||||
/// as random as it can get
|
/// as random as it can get
|
||||||
|
|
@ -454,8 +587,9 @@ pub enum PoolSortToken {
|
||||||
}
|
}
|
||||||
impl SortableToken for PoolSortToken {}
|
impl SortableToken for PoolSortToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe named query tokens for use with
|
/// Type-safe named query tokens for use with
|
||||||
/// [list_comments](crate::SzurubooruRequest::list_comments)
|
/// [list_comments](crate::SzurubooruRequest::list_comments)
|
||||||
pub enum CommentNamedToken {
|
pub enum CommentNamedToken {
|
||||||
|
|
@ -484,8 +618,9 @@ pub enum CommentNamedToken {
|
||||||
}
|
}
|
||||||
impl NamedToken for CommentNamedToken {}
|
impl NamedToken for CommentNamedToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe sort query tokens for use with
|
/// Type-safe sort query tokens for use with
|
||||||
/// [list_comments](crate::SzurubooruRequest::list_comments)
|
/// [list_comments](crate::SzurubooruRequest::list_comments)
|
||||||
pub enum CommentSortToken {
|
pub enum CommentSortToken {
|
||||||
|
|
@ -512,8 +647,9 @@ pub enum CommentSortToken {
|
||||||
}
|
}
|
||||||
impl SortableToken for CommentSortToken {}
|
impl SortableToken for CommentSortToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe named query tokens for use with [list_users](crate::SzurubooruRequest::list_users)
|
/// Type-safe named query tokens for use with [list_users](crate::SzurubooruRequest::list_users)
|
||||||
pub enum UserNamedToken {
|
pub enum UserNamedToken {
|
||||||
/// having given name (accepts wildcards)
|
/// having given name (accepts wildcards)
|
||||||
|
|
@ -533,8 +669,9 @@ pub enum UserNamedToken {
|
||||||
}
|
}
|
||||||
impl NamedToken for UserNamedToken {}
|
impl NamedToken for UserNamedToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe sort query tokens for use with [list_users](crate::SzurubooruRequest::list_users)
|
/// Type-safe sort query tokens for use with [list_users](crate::SzurubooruRequest::list_users)
|
||||||
pub enum UserSortToken {
|
pub enum UserSortToken {
|
||||||
/// as random as it can get
|
/// as random as it can get
|
||||||
|
|
@ -556,8 +693,9 @@ pub enum UserSortToken {
|
||||||
}
|
}
|
||||||
impl SortableToken for UserNamedToken {}
|
impl SortableToken for UserNamedToken {}
|
||||||
|
|
||||||
#[derive(Debug, AsRefStr)]
|
#[derive(Debug, AsRefStr, PartialEq, Eq, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
|
#[cfg_attr(all(feature = "python"), pyclass(eq, eq_int))]
|
||||||
/// Type-safe named query tokens for use with
|
/// Type-safe named query tokens for use with
|
||||||
/// [list_snapshots](crate::SzurubooruRequest::list_snapshots)
|
/// [list_snapshots](crate::SzurubooruRequest::list_snapshots)
|
||||||
pub enum SnapshotNamedToken {
|
pub enum SnapshotNamedToken {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue