1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! Cyberdrop API client.
//!
//! This crate provides a small, async wrapper around a subset of Cyberdrop's HTTP API.
//! It is built on [`reqwest`] and is intended to be copy-paste friendly in CLI tools and
//! simple services.
//!
//! ## Quickstart
//!
//! Authenticate, then call endpoints that require a token:
//!
//! ```no_run
//! use cyberdrop_client::CyberdropClient;
//! use std::path::Path;
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), cyberdrop_client::CyberdropError> {
//! // 1) Create an unauthenticated client.
//! let client = CyberdropClient::builder().build()?;
//!
//! // 2) Exchange credentials for a token.
//! let token = client.login("username", "password").await?;
//!
//! // 3) Use a cloned client that includes the token on authenticated requests.
//! let authed = client.with_auth_token(token.into_string());
//! let albums = authed.list_albums().await?;
//! println!("albums: {}", albums.albums.len());
//!
//! // 4) Create an album and upload a file into it.
//! let album_id = authed
//! .create_album("my uploads", Some("created by cyberdrop-client"))
//! .await?;
//! let uploaded = authed
//! .upload_file(Path::new("path/to/file.jpg"), Some(album_id))
//! .await?;
//! println!("uploaded {} -> {}", uploaded.name, uploaded.url);
//! # Ok(())
//! # }
//! ```
//!
//! Note: `upload_file` streams smaller files and reads larger files in chunks.
//!
//! ## Authentication
//!
//! Authenticated endpoints use an HTTP header named `token` (not an `Authorization: Bearer ...`
//! header). Methods that *require* authentication return [`CyberdropError::MissingAuthToken`]
//! if no token is configured.
//!
//! ## Timeouts, Retries, Polling
//!
//! - **Timeouts:** The client uses a single *request* timeout configured via
//! [`CyberdropClientBuilder::timeout`]. The default is 30 seconds. Timeout failures surface as
//! [`CyberdropError::Http`] (from `reqwest`).
//! - **Retries:** This crate does not implement retries, backoff, or idempotency safeguards.
//! If you need retries, add them at the call site.
//! - **Polling:** This crate does not poll for eventual consistency. Methods return once the HTTP
//! request/response completes.
//!
//! ## Error Model
//!
//! Higher-level API methods (for example, [`CyberdropClient::list_albums`]) treat non-2xx HTTP
//! responses as errors:
//! - `401`/`403` become [`CyberdropError::AuthenticationFailed`]
//! - other non-2xx statuses become [`CyberdropError::RequestFailed`]
//!
//! In contrast, [`CyberdropClient::get`] is low-level and returns the raw response even for
//! non-2xx statuses.
//!
//! External system failures are surfaced as:
//! - [`CyberdropError::Io`] when reading local files (for example, in [`CyberdropClient::upload_file`])
//! - [`CyberdropError::Http`] for network/transport failures (DNS, TLS, connection errors, timeouts)
//!
//! ## Base URL Semantics
//!
//! The base URL is joined with relative paths via [`Url::join`]. If you supply a custom base URL,
//! prefer including a trailing slash (for example, `https://example.test/`), so relative joins
//! behave as expected.
//!
//! ## Low-Level Requests
//!
//! [`CyberdropClient::get`] is intentionally low-level: it returns the raw [`reqwest::Response`]
//! and does **not** treat non-2xx status codes as errors.
pub use ChunkFields;
pub use ;
pub use CyberdropError;
pub use ;