use std::cmp::Ordering;
use std::sync::atomic::{self, AtomicU8};
use std::sync::Arc;
use std::time::Instant;
use futures_util::lock::Mutex;
use proto::op::Edns;
pub(crate) struct NameServerState {
conn_state: AtomicU8,
remote_edns: Mutex<Arc<Option<Edns>>>,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[repr(u8)]
enum NameServerStateInner {
Failed = 0,
Init = 1,
Established = 2,
}
impl From<NameServerStateInner> for u8 {
fn from(val: NameServerStateInner) -> Self {
val as Self
}
}
impl From<u8> for NameServerStateInner {
fn from(val: u8) -> Self {
match val {
2 => Self::Established,
1 => Self::Init,
_ => Self::Failed,
}
}
}
impl NameServerState {
fn store(&self, conn_state: NameServerStateInner) {
self.conn_state
.store(conn_state.into(), atomic::Ordering::Release);
}
fn load(&self) -> NameServerStateInner {
NameServerStateInner::from(self.conn_state.load(atomic::Ordering::Acquire))
}
pub(crate) fn init(_send_edns: Option<Edns>) -> Self {
Self {
conn_state: AtomicU8::new(NameServerStateInner::Init.into()),
remote_edns: Mutex::new(Arc::new(None)),
}
}
pub(crate) fn reinit(&self, _send_edns: Option<Edns>) {
self.store(NameServerStateInner::Init);
}
pub(crate) fn establish(&self, remote_edns: Option<Edns>) {
if remote_edns.is_some() {
if let Some(mut current_edns) = self.remote_edns.try_lock() {
*current_edns = Arc::new(remote_edns)
}
}
self.store(NameServerStateInner::Established);
}
pub(crate) fn fail(&self, _when: Instant) {
self.store(NameServerStateInner::Failed);
}
pub(crate) fn is_failed(&self) -> bool {
NameServerStateInner::Failed == self.load()
}
}
impl Ord for NameServerStateInner {
fn cmp(&self, other: &Self) -> Ordering {
let (self_num, other_num) = (u8::from(*self), u8::from(*other));
self_num.cmp(&other_num)
}
}
impl PartialOrd for NameServerStateInner {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for NameServerState {
fn cmp(&self, other: &Self) -> Ordering {
let other = other.load();
self.load().cmp(&other)
}
}
impl PartialOrd for NameServerState {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for NameServerState {
fn eq(&self, other: &Self) -> bool {
self.load() == other.load()
}
}
impl Eq for NameServerState {}
#[cfg(test)]
mod tests {
use super::*;
use crate::name_server::NameServerState;
#[test]
fn test_state_cmp() {
let init = NameServerState::init(None);
let established = NameServerState::init(None);
established.establish(None);
let failed = NameServerState::init(None);
failed.fail(Instant::now());
assert_eq!(init.cmp(&init), Ordering::Equal);
assert_eq!(init.cmp(&established), Ordering::Less);
assert_eq!(init.cmp(&failed), Ordering::Greater);
assert_eq!(established.cmp(&established), Ordering::Equal);
assert_eq!(established.cmp(&failed), Ordering::Greater);
assert_eq!(failed.cmp(&failed), Ordering::Equal);
}
}