jupiter_swap_api_client/
lib.rs1use std::collections::HashMap;
2
3use quote::{InternalQuoteRequest, QuoteRequest, QuoteResponse};
4use reqwest::{Client, Response};
5use serde::de::DeserializeOwned;
6use swap::{SwapInstructionsResponse, SwapInstructionsResponseInternal, SwapRequest, SwapResponse};
7use thiserror::Error;
8
9pub mod quote;
10pub mod route_plan_with_metadata;
11pub mod serde_helpers;
12pub mod swap;
13pub mod transaction_config;
14
15#[derive(Clone)]
16pub struct JupiterSwapApiClient {
17 pub base_path: String,
18}
19
20#[derive(Debug, Error)]
21pub enum ClientError {
22 #[error("Request failed with status {status}: {body}")]
23 RequestFailed {
24 status: reqwest::StatusCode,
25 body: String,
26 },
27 #[error("Failed to deserialize response: {0}")]
28 DeserializationError(#[from] reqwest::Error),
29}
30
31async fn check_is_success(response: Response) -> Result<Response, ClientError> {
32 if !response.status().is_success() {
33 let status = response.status();
34 let body = response.text().await.unwrap_or_default();
35 return Err(ClientError::RequestFailed { status, body });
36 }
37 Ok(response)
38}
39
40async fn check_status_code_and_deserialize<T: DeserializeOwned>(
41 response: Response,
42) -> Result<T, ClientError> {
43 let response = check_is_success(response).await?;
44 response
45 .json::<T>()
46 .await
47 .map_err(ClientError::DeserializationError)
48}
49
50impl JupiterSwapApiClient {
51 pub fn new(base_path: String) -> Self {
52 Self { base_path }
53 }
54
55 pub async fn quote(&self, quote_request: &QuoteRequest) -> Result<QuoteResponse, ClientError> {
56 let url = format!("{}/quote", self.base_path);
57 let extra_args = quote_request.quote_args.clone();
58 let internal_quote_request = InternalQuoteRequest::from(quote_request.clone());
59 let response = Client::new()
60 .get(url)
61 .query(&internal_quote_request)
62 .query(&extra_args)
63 .send()
64 .await?;
65 check_status_code_and_deserialize(response).await
66 }
67
68 pub async fn swap(
69 &self,
70 swap_request: &SwapRequest,
71 extra_args: Option<HashMap<String, String>>,
72 ) -> Result<SwapResponse, ClientError> {
73 let response = Client::new()
74 .post(format!("{}/swap", self.base_path))
75 .query(&extra_args)
76 .json(swap_request)
77 .send()
78 .await?;
79 check_status_code_and_deserialize(response).await
80 }
81
82 pub async fn swap_instructions(
83 &self,
84 swap_request: &SwapRequest,
85 ) -> Result<SwapInstructionsResponse, ClientError> {
86 let response = Client::new()
87 .post(format!("{}/swap-instructions", self.base_path))
88 .json(swap_request)
89 .send()
90 .await?;
91 check_status_code_and_deserialize::<SwapInstructionsResponseInternal>(response)
92 .await
93 .map(Into::into)
94 }
95}