Repository: sugyan/atrium (language: rust)

Issue:
`app.bsky.feed.getPosts` fails with more than a certain number of arguments `uris`
## Versions

- `atrium-api`: 0.21.0
- `atrium-xrpc`: 0.10.4
- `atrium-xrpc-client`: 0.5.2

## How to reproduce

```rust
use atrium_api::client::AtpServiceClient;
use atrium_api::types::string::AtIdentifier;
use atrium_xrpc_client::reqwest::ReqwestClient;
use itertools::Itertools;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = AtpServiceClient::new(ReqwestClient::new("https://api.bsky.app"));
    let uris = client
        .service
        .app
        .bsky
        .feed
        .get_author_feed(atrium_api::app::bsky::feed::get_author_feed::Parameters {
            actor: AtIdentifier::Handle("atproto.com".parse()?),
            cursor: None,
            filter: None,
            limit: 30.try_into().ok(),
        })
        .await?
        .feed
        .iter()
        .map(|post| post.post.uri.clone())
        .unique()
        .collect_vec();
    client
        .service
        .app
        .bsky
        .feed
        .get_posts(atrium_api::app::bsky::feed::get_posts::Parameters {
            uris: uris.into_iter().take(22).collect(),
        })
        .await?;
    Ok(())
}
```

Output:
```
Error: XrpcResponse(XrpcError { status: 400, error: Some(Undefined(ErrorResponseBody { error: Some("InvalidRequest"), message: Some("Error: uris/0 must be a valid at-uri") })) })
```

If `.take(22)` is less than 21, no error occurs.


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 42ae8e5b..c295a4fa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -171,8 +171,8 @@ dependencies = [
  "async-trait",
  "http 1.1.0",
  "serde",
+ "serde_html_form",
  "serde_json",
- "serde_qs",
  "thiserror",
  "tokio",
  "wasm-bindgen-test",
@@ -1623,6 +1623,19 @@ dependencies = [
  "syn 2.0.50",
 ]
 
+[[package]]
+name = "serde_html_form"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5"
+dependencies = [
+ "form_urlencoded",
+ "indexmap",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
 [[package]]
 name = "serde_ipld_dagcbor"
 version = "0.6.0"
@@ -1646,17 +1659,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "serde_qs"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c"
-dependencies = [
- "percent-encoding",
- "serde",
- "thiserror",
-]
-
 [[package]]
 name = "serde_urlencoded"
 version = "0.7.1"
diff --git a/Cargo.toml b/Cargo.toml
index 361b41f5..e9d2f3c3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -40,7 +40,7 @@ regex = "1"
 serde = "1.0.160"
 serde_bytes = "0.11.9"
 serde_json = "1.0.96"
-serde_qs = "0.12"
+serde_html_form = "0.2.6"
 
 # Networking
 futures = { version = "0.3.30", default-features = false, features = ["alloc"] }
diff --git a/atrium-xrpc/Cargo.toml b/atrium-xrpc/Cargo.toml
index 8279f6c2..e7f51bb6 100644
--- a/atrium-xrpc/Cargo.toml
+++ b/atrium-xrpc/Cargo.toml
@@ -15,8 +15,8 @@ keywords.workspace = true
 async-trait.workspace = true
 http.workspace = true
 serde = { workspace = true, features = ["derive"] }
+serde_html_form.workspace = true
 serde_json.workspace = true
-serde_qs.workspace = true
 thiserror.workspace = true
 
 [dev-dependencies]
diff --git a/atrium-xrpc/src/error.rs b/atrium-xrpc/src/error.rs
index 649fbf26..d76daf9f 100644
--- a/atrium-xrpc/src/error.rs
+++ b/atrium-xrpc/src/error.rs
@@ -78,8 +78,8 @@ pub enum Error<E> {
     HttpClient(Box<dyn std::error::Error + Send + Sync + 'static>),
     #[error("serde_json error: {0}")]
     SerdeJson(#[from] serde_json::Error),
-    #[error("serde_qs error: {0}")]
-    SerdeQs(#[from] serde_qs::Error),
+    #[error("serde_html_form error: {0}")]
+    SerdeHtmlForm(#[from] serde_html_form::ser::Error),
     #[error("unexpected response type")]
     UnexpectedResponseType,
 }
diff --git a/atrium-xrpc/src/lib.rs b/atrium-xrpc/src/lib.rs
index 78b6ec2d..a131544d 100644
--- a/atrium-xrpc/src/lib.rs
+++ b/atrium-xrpc/src/lib.rs
@@ -73,7 +73,7 @@ pub trait XrpcClient: HttpClient {
     {
         let mut uri = format!("{}/xrpc/{}", self.base_uri(), request.path);
         if let Some(p) = &request.parameters {
-            serde_qs::to_string(p).map(|qs| {
+            serde_html_form::to_string(p).map(|qs| {
                 uri += "?";
                 uri += &qs;
             })?;


Test patch: