#![allow(clippy::struct_excessive_bools)]
use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use serde_json::Value;
fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Default,
{
Option::<T>::deserialize(deserializer).map(Option::unwrap_or_default)
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum HostSecurity {
#[default]
Same,
Tls,
None,
Reality,
Other(String),
}
impl HostSecurity {
pub fn as_str(&self) -> &str {
match self {
Self::Same => "same",
Self::Tls => "tls",
Self::None => "none",
Self::Reality => "reality",
Self::Other(value) => value,
}
}
}
impl Serialize for HostSecurity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for HostSecurity {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Ok(match value.as_str() {
"" | "same" => Self::Same,
"tls" => Self::Tls,
"none" => Self::None,
"reality" => Self::Reality,
_ => Self::Other(value),
})
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum MihomoIpVersion {
#[default]
Inherit,
Dual,
Ipv4,
Ipv6,
Ipv4Prefer,
Ipv6Prefer,
Other(String),
}
impl MihomoIpVersion {
pub fn as_str(&self) -> &str {
match self {
Self::Inherit => "",
Self::Dual => "dual",
Self::Ipv4 => "ipv4",
Self::Ipv6 => "ipv6",
Self::Ipv4Prefer => "ipv4-prefer",
Self::Ipv6Prefer => "ipv6-prefer",
Self::Other(value) => value,
}
}
}
impl Serialize for MihomoIpVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for MihomoIpVersion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Ok(match value.as_str() {
"" => Self::Inherit,
"dual" => Self::Dual,
"ipv4" => Self::Ipv4,
"ipv6" => Self::Ipv6,
"ipv4-prefer" => Self::Ipv4Prefer,
"ipv6-prefer" => Self::Ipv6Prefer,
_ => Self::Other(value),
})
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum SubscriptionFormat {
Raw,
Json,
Clash,
Other(String),
}
impl SubscriptionFormat {
pub fn as_str(&self) -> &str {
match self {
Self::Raw => "raw",
Self::Json => "json",
Self::Clash => "clash",
Self::Other(value) => value,
}
}
}
impl Serialize for SubscriptionFormat {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for SubscriptionFormat {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Ok(match value.as_str() {
"raw" => Self::Raw,
"json" => Self::Json,
"clash" => Self::Clash,
_ => Self::Other(value),
})
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct VlessRoute(Option<u16>);
impl VlessRoute {
pub const fn new(port: u16) -> Self {
Self(Some(port))
}
pub const fn port(self) -> Option<u16> {
self.0
}
}
impl From<u16> for VlessRoute {
fn from(port: u16) -> Self {
Self::new(port)
}
}
impl Serialize for VlessRoute {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self.0 {
Some(port) => serializer.serialize_str(&port.to_string()),
None => serializer.serialize_str(""),
}
}
}
impl<'de> Deserialize<'de> for VlessRoute {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor;
impl de::Visitor<'_> for Visitor {
type Value = VlessRoute;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an empty string or a port from 0 through 65535")
}
fn visit_none<E>(self) -> Result<Self::Value, E> {
Ok(VlessRoute::default())
}
fn visit_unit<E>(self) -> Result<Self::Value, E> {
Ok(VlessRoute::default())
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
u16::try_from(value)
.map(VlessRoute::new)
.map_err(|_| E::custom("VLESS route is outside the port range"))
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
u16::try_from(value)
.map(VlessRoute::new)
.map_err(|_| E::custom("VLESS route is outside the port range"))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
if value.is_empty() {
return Ok(VlessRoute::default());
}
value.parse::<u16>().map(VlessRoute::new).map_err(E::custom)
}
}
deserializer.deserialize_any(Visitor)
}
}
#[derive(Clone, Default, Eq, PartialEq)]
pub struct HostJsonOverride(String);
impl HostJsonOverride {
pub fn from_value(value: &Value) -> serde_json::Result<Self> {
serde_json::to_string(value).map(Self)
}
pub const fn empty() -> Self {
Self(String::new())
}
pub fn as_raw(&self) -> &str {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn value(&self) -> serde_json::Result<Option<Value>> {
if self.is_empty() {
Ok(None)
} else {
serde_json::from_str(&self.0).map(Some)
}
}
}
impl fmt::Debug for HostJsonOverride {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_tuple("HostJsonOverride")
.field(&if self.is_empty() {
"[EMPTY]"
} else {
"[CONFIGURED]"
})
.finish()
}
}
impl Serialize for HostJsonOverride {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.0)
}
}
impl<'de> Deserialize<'de> for HostJsonOverride {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Option::<String>::deserialize(deserializer).map(|value| Self(value.unwrap_or_default()))
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct HostOptions {
pub sort_order: i64,
pub remark: String,
pub server_description: String,
pub is_disabled: bool,
pub is_hidden: bool,
#[serde(deserialize_with = "deserialize_null_default")]
pub tags: Vec<String>,
pub port: u16,
pub security: HostSecurity,
pub sni: String,
pub host_header: String,
pub path: String,
#[serde(deserialize_with = "deserialize_null_default")]
pub alpn: Vec<String>,
pub fingerprint: String,
pub override_sni_from_address: bool,
pub keep_sni_blank: bool,
#[serde(deserialize_with = "deserialize_null_default")]
pub pinned_peer_cert_sha256: Vec<String>,
pub verify_peer_cert_by_name: String,
pub allow_insecure: bool,
pub ech_config_list: String,
pub mux_params: HostJsonOverride,
pub sockopt_params: HostJsonOverride,
pub final_mask: HostJsonOverride,
pub vless_route: VlessRoute,
#[serde(deserialize_with = "deserialize_null_default")]
pub exclude_from_sub_types: Vec<SubscriptionFormat>,
#[serde(deserialize_with = "deserialize_null_default")]
pub node_guids: Vec<String>,
#[serde(rename = "mihomoIpVersion")]
pub mihomo_ip_version: MihomoIpVersion,
pub mihomo_x25519: bool,
pub shuffle_host: bool,
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct HostGroup {
pub group_id: String,
#[serde(deserialize_with = "deserialize_null_default")]
pub inbound_ids: Vec<i64>,
#[serde(deserialize_with = "deserialize_null_default")]
pub hosts: Vec<String>,
#[serde(flatten)]
pub options: HostOptions,
}
impl HostGroup {
pub fn new(inbound_ids: Vec<i64>, remark: impl Into<String>) -> Self {
Self {
inbound_ids,
options: HostOptions {
remark: remark.into(),
..HostOptions::default()
},
..Self::default()
}
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct HostRow {
pub id: i64,
pub group_id: String,
pub inbound_id: i64,
pub address: String,
#[serde(flatten)]
pub options: HostOptions,
pub created_at: i64,
pub updated_at: i64,
}