use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tower::Service;
use crate::client::McClient;
use crate::error::McError;
use crate::models::{ServerEdition, ServerStatus};
#[derive(Debug, Clone)]
pub struct PingRequestTower {
pub address: String,
pub edition: ServerEdition,
}
impl PingRequestTower {
pub fn java(address: impl Into<String>) -> Self {
Self { address: address.into(), edition: ServerEdition::Java }
}
pub fn bedrock(address: impl Into<String>) -> Self {
Self { address: address.into(), edition: ServerEdition::Bedrock }
}
}
#[derive(Debug)]
pub struct PingResponseTower {
pub status: ServerStatus,
}
#[derive(Clone)]
pub struct McService {
client: McClient,
}
impl McService {
pub fn new(client: McClient) -> Self {
Self { client }
}
}
impl Service<PingRequestTower> for McService {
type Response = PingResponseTower;
type Error = McError;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: PingRequestTower) -> Self::Future {
let client = self.client.clone();
Box::pin(async move {
let status = match req.edition {
ServerEdition::Java => client.ping_java_inner(&req.address).await.map(|s| s.0),
ServerEdition::Bedrock => client.ping_bedrock_inner(&req.address).await.map(|s| s.0),
}?;
Ok(PingResponseTower { status })
})
}
}
#[derive(Clone, Debug)]
pub struct McRetryPolicy {
remaining: usize,
}
impl McRetryPolicy {
pub fn new(attempts: usize) -> Self {
Self { remaining: attempts.saturating_sub(1) }
}
}
impl tower::retry::Policy<PingRequestTower, PingResponseTower, McError> for McRetryPolicy {
type Future = std::future::Ready<()>;
fn retry(
&mut self,
_req: &mut PingRequestTower,
result: &mut Result<PingResponseTower, McError>,
) -> Option<Self::Future> {
if self.remaining == 0 {
return None;
}
let should_retry = match result {
Err(McError::Network(crate::error::NetworkError::Timeout)) => true,
Err(McError::Network(crate::error::NetworkError::Connection(_))) => true,
Err(McError::Network(crate::error::NetworkError::Dns(_))) => false, _ => false,
};
if should_retry {
self.remaining -= 1;
Some(std::future::ready(()))
} else {
None
}
}
fn clone_request(&mut self, req: &PingRequestTower) -> Option<PingRequestTower> {
Some(req.clone())
}
}