Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try fit wasm partially #225

Merged
merged 11 commits into from
Nov 26, 2024
14 changes: 13 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,36 @@ jobs:
matrix:
include:
- triple: x86_64-unknown-linux-gnu
features: --all-features
os: ubuntu-latest
- triple: aarch64-unknown-linux-gnu
features: --all-features
os: ubuntu-latest
- triple: armv7-unknown-linux-gnueabihf
features: --all-features
os: ubuntu-latest
- triple: arm-unknown-linux-gnueabihf
features: --all-features
os: ubuntu-latest
- triple: riscv64gc-unknown-linux-gnu
features: --all-features
os: ubuntu-latest
- triple: wasm32-unknown-unknown
features: --no-default-features --features=async-http-client
os: ubuntu-latest

- triple: x86_64-apple-darwin
features: --all-features
os: macos-latest
- triple: aarch64-apple-darwin
features: --all-features
os: macos-latest

- triple: x86_64-pc-windows-msvc
features: --all-features
os: windows-latest
- triple: aarch64-pc-windows-msvc
features: --all-features
os: windows-latest
env:
RUSTFLAGS: --deny warnings
Expand All @@ -156,4 +168,4 @@ jobs:
key: release-${{ matrix.triple }}-${{ steps.rust.outputs.cachekey }}-${{ hashFiles('**/Cargo.*') }}
path: target/

- run: ${{ runner.os == 'Linux' && 'cross' || 'cargo' }} build --release --offline --all-features --target ${{ matrix.triple }}
- run: ${{ runner.os == 'Linux' && 'cross' || 'cargo' }} build --release --offline ${{ matrix.features }} --target ${{ matrix.triple }}
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,19 @@ default-features = false
features = ["client"]
optional = true

[dependencies.reqwest]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.reqwest]
version = "0.12"
default-features = false
features = ["multipart", "stream", "rustls-tls"]
optional = true

[dependencies.tokio]
[target.'cfg(target_arch = "wasm32")'.dependencies.reqwest]
version = "0.12"
default-features = false
features = ["multipart", "stream"]
optional = true

[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
version = "1"
features = ["fs"]
optional = true
Expand Down
109 changes: 63 additions & 46 deletions src/client_reqwest.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use std::path::PathBuf;
use std::time::Duration;

use async_trait::async_trait;
use bon::Builder;
use reqwest::multipart;
use serde_json::Value;
use tokio::fs::File;

use crate::trait_async::AsyncTelegramApi;
use crate::Error;
Expand All @@ -17,16 +13,21 @@ pub struct AsyncApi {
#[builder(into)]
pub api_url: String,

#[builder(
default = reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(500))
.build()
.unwrap()
)]
#[builder(default = default_client())]
pub client: reqwest::Client,
}

fn default_client() -> reqwest::Client {
EdJoPaTo marked this conversation as resolved.
Show resolved Hide resolved
let client_builder = reqwest::ClientBuilder::new();

#[cfg(not(target_arch = "wasm32"))]
let client_builder = client_builder
.connect_timeout(std::time::Duration::from_secs(10))
.timeout(std::time::Duration::from_secs(500));

client_builder.build().unwrap()
}

impl AsyncApi {
/// Create a new `AsyncApi`. You can use [`AsyncApi::new_url`] or [`AsyncApi::builder`] for more options.
pub fn new(api_key: &str) -> Self {
Expand Down Expand Up @@ -66,7 +67,9 @@ impl From<reqwest::Error> for Error {
}
}

#[async_trait]
// Wasm target need not be `Send` because it is single-threaded
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl AsyncTelegramApi for AsyncApi {
type Error = Error;

Expand Down Expand Up @@ -102,44 +105,58 @@ impl AsyncTelegramApi for AsyncApi {
Params: serde::ser::Serialize + std::fmt::Debug + std::marker::Send,
Output: serde::de::DeserializeOwned,
{
let json_string = crate::json::encode(&params)?;
let json_struct: Value = serde_json::from_str(&json_string).unwrap();
let file_keys: Vec<&str> = files.iter().map(|(key, _)| *key).collect();
let files_with_paths: Vec<(String, &str, String)> = files
.iter()
.map(|(key, path)| {
(
(*key).to_string(),
path.to_str().unwrap(),
path.file_name().unwrap().to_str().unwrap().to_string(),
)
})
.collect();

let mut form = multipart::Form::new();
for (key, val) in json_struct.as_object().unwrap() {
if !file_keys.contains(&key.as_str()) {
let val = match val {
Value::String(val) => val.to_string(),
other => other.to_string(),
};

form = form.text(key.clone(), val);
#[cfg(not(target_arch = "wasm32"))]
{
use reqwest::multipart;
use serde_json::Value;

let json_string = crate::json::encode(&params)?;
let json_struct: Value = serde_json::from_str(&json_string).unwrap();

let file_keys: Vec<&str> = files.iter().map(|(key, _)| *key).collect();
let files_with_paths: Vec<(String, &str, String)> = files
.iter()
.map(|(key, path)| {
(
(*key).to_string(),
path.to_str().unwrap(),
path.file_name().unwrap().to_str().unwrap().to_string(),
)
})
.collect();

let mut form = multipart::Form::new();
for (key, val) in json_struct.as_object().unwrap() {
if !file_keys.contains(&key.as_str()) {
let val = match val {
Value::String(val) => val.to_string(),
other => other.to_string(),
};

form = form.text(key.clone(), val);
}
}
}

for (parameter_name, file_path, file_name) in files_with_paths {
let file = File::open(file_path)
.await
.map_err(|error| Error::Encode(error.to_string()))?;
let part = multipart::Part::stream(file).file_name(file_name);
form = form.part(parameter_name, part);
}
for (parameter_name, file_path, file_name) in files_with_paths {
let file = tokio::fs::File::open(file_path)
.await
.map_err(|error| Error::Encode(error.to_string()))?;
let part = multipart::Part::stream(file).file_name(file_name);
form = form.part(parameter_name, part);
}

let url = format!("{}/{method}", self.api_url);
let url = format!("{}/{method}", self.api_url);

let response = self.client.post(url).multipart(form).send().await?;
Self::decode_response(response).await
let response = self.client.post(url).multipart(form).send().await?;
Self::decode_response(response).await
}

#[cfg(target_arch = "wasm32")]
{
Err(Error::Encode(format!(
"calling {method:?} with files is currently unsupported in WASM due to missing form_data / attachment support. Was called with params {params:?} and files {files:?}",
)))
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/trait_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ macro_rules! request_nb {
}
}

#[async_trait::async_trait]
// Wasm target need not be `Send` because it is single-threaded
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
pub trait AsyncTelegramApi
where
Self: Sync,
Expand Down
Loading