use crate::{models::*, BuildQueryParametersExt};
pub mod filters {
use crate::models::Asset;
#[derive(Default, Clone)]
pub struct SponsorFilter(pub String);
#[derive(Default, Clone)]
pub struct NoSponsorFilter;
#[derive(Default, Clone)]
pub struct SignerFilter(pub String);
#[derive(Default, Clone)]
pub struct NoSignerFilter;
#[derive(Clone)]
pub struct AssetFilter<T>(pub Asset<T>);
#[derive(Default, Clone)]
pub struct NoAssetFilter;
#[derive(Default, Clone)]
pub struct LiquidityPoolFilter(pub String);
#[derive(Default, Clone)]
pub struct NoLiquidityPoolFilter;
}
use filters::*;
macro_rules! valid_account_request_impl {
($type:ty, $field:ident) => {
impl Request for $type {
fn get_query_parameters(&self) -> String {
let mut params = vec![
self.cursor.as_ref().map(|c| format!("cursor={}", c)),
self.limit.as_ref().map(|l| format!("limit={}", l)),
self.order.as_ref().map(|o| format!("order={}", o)),
];
params.push(Some(format!("{}={}", stringify!($field), self.$field.0)));
params.build_query_parameters()
}
fn build_url(&self, base_url: &str) -> String {
format!(
"{}/{}{}",
base_url,
super::ACCOUNTS_PATH,
self.get_query_parameters()
)
}
}
};
}
macro_rules! valid_generic_account_request_impl {
($type:ty, $field:ident, $generic:ident) => {
impl<$generic> Request for $type
where
Asset<T>: std::fmt::Display,
{
fn get_query_parameters(&self) -> String {
let mut params = vec![
self.cursor.as_ref().map(|c| format!("cursor={}", c)),
self.limit.as_ref().map(|l| format!("limit={}", l)),
self.order.as_ref().map(|o| format!("order={}", o)),
];
params.push(Some(format!("{}={}", stringify!($field), self.$field.0)));
params.build_query_parameters()
}
fn build_url(&self, base_url: &str) -> String {
format!(
"{}/{}{}",
base_url,
super::ACCOUNTS_PATH,
self.get_query_parameters()
)
}
}
};
}
pub trait ValidAccountsRequest: Request {}
impl ValidAccountsRequest
for AccountsRequest<SponsorFilter, NoSignerFilter, NoAssetFilter, NoLiquidityPoolFilter>
{
}
valid_account_request_impl!(AccountsRequest<SponsorFilter, NoSignerFilter, NoAssetFilter, NoLiquidityPoolFilter>, sponsor);
impl ValidAccountsRequest
for AccountsRequest<NoSponsorFilter, SignerFilter, NoAssetFilter, NoLiquidityPoolFilter>
{
}
valid_account_request_impl!(AccountsRequest<NoSponsorFilter, SignerFilter, NoAssetFilter, NoLiquidityPoolFilter>, signer);
impl<T> ValidAccountsRequest
for AccountsRequest<NoSponsorFilter, NoSignerFilter, AssetFilter<T>, NoLiquidityPoolFilter>
where
Asset<T>: std::fmt::Display,
{
}
valid_generic_account_request_impl!(AccountsRequest<NoSponsorFilter, NoSignerFilter, AssetFilter<T>, NoLiquidityPoolFilter>, asset, T);
impl ValidAccountsRequest
for AccountsRequest<NoSponsorFilter, NoSignerFilter, NoAssetFilter, LiquidityPoolFilter>
{
}
valid_account_request_impl!(AccountsRequest<NoSponsorFilter, NoSignerFilter, NoAssetFilter, LiquidityPoolFilter>, liquidity_pool);
#[derive(Default)]
pub struct AccountsRequest<Sp, Si, A, L> {
sponsor: Sp,
signer: Si,
asset: A,
liquidity_pool: L,
cursor: Option<u32>,
limit: Option<u32>,
order: Option<Order>,
}
impl<Sp, Si, A, L> AccountsRequest<Sp, Si, A, L> {
pub fn set_cursor(self, cursor: u32) -> Result<Self, String> {
if cursor < 1 {
return Err("cursor must be greater than or equal to 1".to_string());
}
Ok(Self {
cursor: Some(cursor),
..self
})
}
pub fn set_limit(self, limit: u32) -> Result<Self, String> {
if limit < 1 || limit > 200 {
return Err("limit must be between 1 and 200".to_string());
}
Ok(Self {
limit: Some(limit),
..self
})
}
pub fn set_order(self, order: Order) -> Self {
Self {
order: Some(order),
..self
}
}
}
impl AccountsRequest<NoSponsorFilter, NoSignerFilter, NoAssetFilter, NoLiquidityPoolFilter> {
pub fn new() -> Self {
AccountsRequest::default()
}
pub fn set_sponsor_filter(
self,
sponsor: impl Into<String>
) -> Result<
AccountsRequest<SponsorFilter, NoSignerFilter, NoAssetFilter, NoLiquidityPoolFilter>,
String,
> {
let sponsor = sponsor.into();
if let Err(e) = is_public_key(&sponsor) {
return Err(e.to_string());
}
Ok(AccountsRequest {
sponsor: SponsorFilter(sponsor.into()),
cursor: self.cursor,
limit: self.limit,
order: self.order,
..Default::default()
})
}
pub fn set_signer_filter(
self,
signer: &str,
) -> Result<
AccountsRequest<NoSponsorFilter, SignerFilter, NoAssetFilter, NoLiquidityPoolFilter>,
String,
> {
if let Err(e) = is_public_key(&signer) {
return Err(e.to_string());
}
Ok(AccountsRequest {
signer: SignerFilter(signer.to_string()),
cursor: self.cursor,
limit: self.limit,
order: self.order,
..Default::default()
})
}
pub fn set_asset_filter<T>(
self,
asset: Asset<T>,
) -> AccountsRequest<NoSponsorFilter, NoSignerFilter, AssetFilter<T>, NoLiquidityPoolFilter>
{
AccountsRequest {
sponsor: self.sponsor,
signer: self.signer,
asset: AssetFilter(asset),
liquidity_pool: self.liquidity_pool,
cursor: self.cursor,
limit: self.limit,
order: self.order,
}
}
pub fn set_liquidity_pool_filter(
self,
liquidity_pool: impl Into<String>,
) -> AccountsRequest<NoSponsorFilter, NoSignerFilter, NoAssetFilter, LiquidityPoolFilter> {
AccountsRequest {
liquidity_pool: LiquidityPoolFilter(liquidity_pool.into()),
cursor: self.cursor,
limit: self.limit,
order: self.order,
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_accounts_request_set_sponsor_filter() {
let request = AccountsRequest::new().set_sponsor_filter("sponsor");
assert!(request.is_err());
}
#[test]
fn test_accounts_set_sponsor_valid() {
let request = AccountsRequest::new()
.set_sponsor_filter("GDQJUTQYK2MQX2VGDR2FYWLIYAQIEGXTQVTFEMGH2BEWFG4BRUY4CKI7")
.unwrap();
assert_eq!(
request.sponsor.0,
"GDQJUTQYK2MQX2VGDR2FYWLIYAQIEGXTQVTFEMGH2BEWFG4BRUY4CKI7"
);
}
#[test]
fn test_set_cursor_valid() {
let request = AccountsRequest::new().set_cursor(12345).unwrap();
assert_eq!(request.cursor.unwrap(), 12345);
}
#[test]
fn test_set_cursor_invalid() {
let request = AccountsRequest::new().set_cursor(0);
assert_eq!(
request.err().unwrap(),
"cursor must be greater than or equal to 1".to_string()
);
}
#[test]
fn test_set_limit_valid() {
let request = AccountsRequest::new().set_limit(20).unwrap();
assert_eq!(request.limit.unwrap(), 20);
}
#[test]
fn test_set_limit_invalid_low() {
let request = AccountsRequest::new().set_limit(0);
assert_eq!(
request.err().unwrap(),
"limit must be between 1 and 200".to_string()
);
}
#[test]
fn test_set_limit_invalid_high() {
let request = AccountsRequest::new().set_limit(201);
assert_eq!(
request.err().unwrap(),
"limit must be between 1 and 200".to_string()
);
}
}