1use crate::error::ProxyError;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(tag = "type", rename_all = "snake_case")]
7pub enum UpstreamConfig {
8 Direct,
10 Socks5 {
12 host: String,
13 port: u16,
14 credentials: Option<Socks5Credentials>,
16 },
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct Socks5Credentials {
21 pub username: String,
22 pub password: String,
23}
24
25#[derive(Debug, Clone)]
27pub enum RouteDecision {
28 Upstream(UpstreamConfig),
29 Block,
31}
32
33pub trait ProxyRouter: Send + Sync {
36 fn route(&self, host: &str, port: u16) -> Result<RouteDecision, ProxyError>;
38}
39
40pub struct FixedRouter(pub UpstreamConfig);
44
45impl ProxyRouter for FixedRouter {
46 fn route(&self, _host: &str, _port: u16) -> Result<RouteDecision, ProxyError> {
47 Ok(RouteDecision::Upstream(self.0.clone()))
48 }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct Profile {
54 pub name: String,
55 pub upstream: UpstreamConfig,
56}
57
58pub struct ProfileRouter {
61 profiles: Vec<Profile>,
62 active: Option<String>,
63}
64
65impl ProfileRouter {
66 pub fn new(profiles: Vec<Profile>) -> Self {
67 Self {
68 profiles,
69 active: None,
70 }
71 }
72
73 pub fn activate(&mut self, name: &str) -> bool {
74 let found = self.profiles.iter().any(|p| p.name == name);
75 if found {
76 self.active = Some(name.to_owned());
77 }
78 found
79 }
80
81 pub fn deactivate(&mut self) {
82 self.active = None;
83 }
84}
85
86impl ProxyRouter for ProfileRouter {
87 fn route(&self, _host: &str, _port: u16) -> Result<RouteDecision, ProxyError> {
88 let upstream = self
89 .active
90 .as_deref()
91 .and_then(|name| self.profiles.iter().find(|p| p.name == name))
92 .map(|p| p.upstream.clone())
93 .unwrap_or(UpstreamConfig::Direct);
94 Ok(RouteDecision::Upstream(upstream))
95 }
96}