1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use clashctl_interactive::RuleSort;
use tui::{
    style::{Color, Modifier, Style},
    text::{Span, Spans},
    widgets::Widget,
};

use crate::{
    components::{MovableList, MovableListItem, MovableListState},
    define_widget,
    model::{Rule, RuleType, Rules},
    AsColor,
};

define_widget!(RulePage);

impl<'a> Widget for RulePage<'a> {
    fn render(self, area: tui::layout::Rect, buf: &mut tui::buffer::Buffer) {
        MovableList::new("Rules", &self.state.rule_state).render(area, buf);
    }
}

impl AsColor for RuleType {
    fn as_color(&self) -> tui::style::Color {
        match self {
            RuleType::Domain => Color::Green,
            RuleType::DomainSuffix => Color::Green,
            RuleType::DomainKeyword => Color::Green,
            RuleType::GeoIP => Color::Yellow,
            RuleType::IPCIDR => Color::Yellow,
            RuleType::SrcIPCIDR => Color::Yellow,
            RuleType::SrcPort => Color::Yellow,
            RuleType::DstPort => Color::Yellow,
            RuleType::Process => Color::Yellow,
            RuleType::Match => Color::Blue,
            RuleType::Direct => Color::Blue,
            RuleType::Reject => Color::Red,
            RuleType::Unknown => Color::DarkGray,
        }
    }
}

impl<'a> From<Rules> for MovableListState<'a, Rule, RuleSort> {
    fn from(val: Rules) -> Self {
        Self::new_with_sort(val.rules, RuleSort::default())
    }
}

impl<'a> MovableListItem<'a> for Rule {
    fn to_spans(&self) -> Spans<'a> {
        let type_color = self.rule_type.as_color();
        let name_color = if self.proxy == "DIRECT" || self.proxy == "REJECT" {
            Color::DarkGray
        } else {
            Color::Yellow
        };
        let gray = Style::default().fg(Color::DarkGray);
        let r_type: &'static str = self.rule_type.into();
        let payload = if self.payload.is_empty() {
            "*"
        } else {
            &self.payload
        }
        .to_owned();
        let dash: String = "─".repeat(35_usize.saturating_sub(payload.len()) + 2) + " ";
        vec![
            Span::styled(format!("{:16}", r_type), Style::default().fg(type_color)),
            Span::styled(payload + " ", Style::default().add_modifier(Modifier::BOLD)),
            Span::styled(dash, gray),
            Span::styled(self.proxy.to_owned(), Style::default().fg(name_color)),
        ]
        .into()
    }
}