clashctl-interactive 0.3.3

Dependency for clashctl & clashctl-tui, do not use directly
use crate::{EndlessSelf, OrderBy, SortMethod, SortOrder};

use clashctl_core::model::Rule;
use serde::{Deserialize, Serialize};
use smart_default::SmartDefault;

#[derive(
    Debug,
    Clone,
    Copy,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Serialize,
    Deserialize,
    SmartDefault,
    strum::EnumString,
    strum::Display,
    strum::EnumVariantNames,
)]
#[strum(ascii_case_insensitive)]
#[serde(rename_all = "lowercase")]
pub enum RuleSortBy {
    #[default]
    Payload,
    Proxy,
    Type,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub struct RuleSort {
    by: RuleSortBy,
    order: SortOrder,
}

impl RuleSort {
    #[inline]
    pub fn new(by: RuleSortBy, order: SortOrder) -> Self {
        Self { by, order }
    }

    #[inline]
    pub fn by(&self) -> RuleSortBy {
        self.by
    }

    #[inline]
    pub fn order(&self) -> SortOrder {
        self.order
    }

    #[inline]
    pub fn by_type_asc() -> Self {
        Self::new(RuleSortBy::Type, SortOrder::Ascendant)
    }

    #[inline]
    pub fn by_type_dsc() -> Self {
        Self::new(RuleSortBy::Type, SortOrder::Descendant)
    }

    #[inline]
    pub fn by_payload_asc() -> Self {
        Self::new(RuleSortBy::Payload, SortOrder::Ascendant)
    }

    #[inline]
    pub fn by_payload_dsc() -> Self {
        Self::new(RuleSortBy::Payload, SortOrder::Descendant)
    }

    #[inline]
    pub fn by_proxy_name_asc() -> Self {
        Self::new(RuleSortBy::Proxy, SortOrder::Ascendant)
    }

    #[inline]
    pub fn by_proxy_name_dsc() -> Self {
        Self::new(RuleSortBy::Proxy, SortOrder::Descendant)
    }
}

impl EndlessSelf for RuleSort {
    fn next_self(&mut self) {
        use RuleSortBy::*;
        use SortOrder::*;

        *self = match (self.by, self.order) {
            (Payload, Ascendant) => Self::by_payload_dsc(),
            (Payload, Descendant) => Self::by_type_asc(),
            (Type, Ascendant) => Self::by_type_dsc(),
            (Type, Descendant) => Self::by_proxy_name_asc(),
            (Proxy, Ascendant) => Self::by_proxy_name_dsc(),
            (Proxy, Descendant) => Self::by_payload_asc(),
        }
    }
    fn prev_self(&mut self) {
        use RuleSortBy::*;
        use SortOrder::*;

        *self = match (self.by, self.order) {
            (Payload, Ascendant) => Self::by_proxy_name_dsc(),
            (Payload, Descendant) => Self::by_payload_asc(),
            (Type, Ascendant) => Self::by_payload_dsc(),
            (Type, Descendant) => Self::by_type_asc(),
            (Proxy, Ascendant) => Self::by_type_dsc(),
            (Proxy, Descendant) => Self::by_proxy_name_asc(),
        }
    }
}

impl ToString for RuleSort {
    fn to_string(&self) -> String {
        format!(
            "{} {}",
            self.by,
            match self.order {
                SortOrder::Ascendant => "",
                SortOrder::Descendant => "",
            }
        )
    }
}

impl SortMethod<Rule> for RuleSort {
    fn sort_fn(&self, a: &Rule, b: &Rule) -> std::cmp::Ordering {
        match self.by {
            RuleSortBy::Payload => a.payload.cmp(&b.payload),
            RuleSortBy::Proxy => a.proxy.cmp(&b.proxy),
            RuleSortBy::Type => a.rule_type.cmp(&b.rule_type),
        }
        .order_by(self.order)
    }
}