sentinel-core 0.1.3

The flow sentinel of your microservices.
Documentation
//! Result
//!
use super::{BlockError, SentinelRule, Snapshot};
use crate::{Error, Result};
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex};

type OtherBlockType = u8;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BlockType {
    Unknown,
    Flow,
    Isolation,
    CircuitBreaking,
    SystemFlow,
    HotSpotParamFlow,
    Other(OtherBlockType),
}

impl Default for BlockType {
    fn default() -> Self {
        Self::Unknown
    }
}

lazy_static! {
    static ref BLOCK_TYPE_MAP: Mutex<HashMap<OtherBlockType, &'static str>> =
        Mutex::new(HashMap::new());
}

const EXIST_BLOCK_ERROR: &str = "Block type existed!";

pub fn registry_block_type(other: BlockType, desc: &'static str) -> Result<()> {
    match other {
        BlockType::Other(id) => {
            if BLOCK_TYPE_MAP.lock().unwrap().contains_key(&id) {
                Err(Error::msg(EXIST_BLOCK_ERROR))
            } else {
                BLOCK_TYPE_MAP.lock().unwrap().insert(id, desc);
                Ok(())
            }
        }
        _ => Err(Error::msg(EXIST_BLOCK_ERROR)),
    }
}

impl fmt::Display for BlockType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let BlockType::Other(id) = self {
            match BLOCK_TYPE_MAP.lock().unwrap().get(id) {
                Some(&desc) => return write!(f, "{}", desc),
                None => return write!(f, "{}", id),
            }
        } else {
            return write!(f, "{:?}", self);
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum TokenResult {
    Pass,
    Blocked(BlockError),
    Wait(u64),
}

impl Default for TokenResult {
    fn default() -> Self {
        TokenResult::Pass
    }
}

impl TokenResult {
    pub fn new_pass() -> Self {
        Self::default()
    }

    pub fn new_should_wait(nanos_to_wait: u64) -> Self {
        Self::Wait(nanos_to_wait)
    }

    pub fn new_blocked(block_type: BlockType) -> Self {
        Self::Blocked(BlockError::new(block_type))
    }

    pub fn new_blocked_with_msg(block_type: BlockType, block_msg: String) -> Self {
        Self::Blocked(BlockError::new_with_msg(block_type, block_msg))
    }

    pub fn new_blocked_with_cause(
        block_type: BlockType,
        block_msg: String,
        rule: Arc<dyn SentinelRule>,
        snapshot_value: Arc<Snapshot>,
    ) -> Self {
        Self::Blocked(BlockError::new_with_cause(
            block_type,
            block_msg,
            rule,
            snapshot_value,
        ))
    }

    pub fn reset_to_pass(&mut self) {
        *self = Self::new_pass();
    }

    pub fn reset_to_blocked(&mut self, block_type: BlockType) {
        *self = Self::new_blocked(block_type);
    }

    pub fn reset_to_blocked_with_msg(&mut self, block_type: BlockType, block_msg: String) {
        *self = Self::new_blocked_with_msg(block_type, block_msg);
    }

    pub fn reset_to_blocked_with_cause(
        &mut self,
        block_type: BlockType,
        block_msg: String,
        rule: Arc<dyn SentinelRule>,
        snapshot_value: Arc<Snapshot>,
    ) {
        *self = Self::new_blocked_with_cause(block_type, block_msg, rule, snapshot_value);
    }

    pub fn is_pass(&self) -> bool {
        match self {
            Self::Pass => true,
            _ => false,
        }
    }

    pub fn is_blocked(&self) -> bool {
        match self {
            Self::Blocked(_) => true,
            _ => false,
        }
    }

    pub fn is_wait(&self) -> bool {
        match self {
            Self::Wait(_) => true,
            _ => false,
        }
    }

    pub fn block_err(&self) -> Option<BlockError> {
        match self {
            Self::Blocked(err) => Some(err.clone()),
            _ => None,
        }
    }

    pub fn nanos_to_wait(&self) -> u64 {
        match self {
            Self::Wait(nanos) => *nanos,
            _ => 0,
        }
    }
}

impl fmt::Display for TokenResult {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TokenResult::Pass => write!(f, "TokenResult::Pass"),
            TokenResult::Blocked(block_err) => write!(f, "TokenResult::Blocked: {:?}", block_err),
            TokenResult::Wait(nanos_to_wait) => {
                write!(f, "TokenResult::Wait: {} ns", nanos_to_wait)
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn register_block_new_type() {
        registry_block_type(BlockType::Other(100), "New").unwrap();
    }

    #[test]
    #[should_panic(expected = "Block type existed!")]
    fn register_block_exist_type() {
        registry_block_type(BlockType::HotSpotParamFlow, "BlockTypeHotSpotParamFlow").unwrap();
    }

    #[test]
    #[should_panic(expected = "Block type existed!")]
    fn register_block_new_type_twice() {
        registry_block_type(BlockType::Other(200), "New").unwrap();
        registry_block_type(BlockType::Other(200), "New").unwrap();
    }
}