use std::{cmp::Ordering, collections::BTreeMap, default::Default, fmt, net::SocketAddr};
use crate::{
proto::command::{
AddBackend, FilteredTimeSerie, LoadBalancingParams, PathRule, PathRuleKind,
RequestHttpFrontend, RequestTcpFrontend, Response, ResponseContent, ResponseStatus,
RulePosition, RunState,
},
state::ClusterId,
};
impl Response {
pub fn new(
status: ResponseStatus,
message: String,
content: Option<ResponseContent>,
) -> Response {
Response {
status: status as i32,
message,
content,
}
}
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct HttpFrontend {
pub cluster_id: Option<ClusterId>,
pub address: SocketAddr,
pub hostname: String,
#[serde(default)]
#[serde(skip_serializing_if = "is_default_path_rule")]
pub path: PathRule,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub method: Option<String>,
#[serde(default)]
pub position: RulePosition,
pub tags: Option<BTreeMap<String, String>>,
}
impl From<HttpFrontend> for RequestHttpFrontend {
fn from(val: HttpFrontend) -> Self {
let tags = match val.tags {
Some(tags) => tags,
None => BTreeMap::new(),
};
RequestHttpFrontend {
cluster_id: val.cluster_id,
address: val.address.to_string(),
hostname: val.hostname,
path: val.path,
method: val.method,
position: val.position.into(),
tags,
}
}
}
impl From<Backend> for AddBackend {
fn from(val: Backend) -> Self {
AddBackend {
cluster_id: val.cluster_id,
backend_id: val.backend_id,
address: val.address.to_string(),
sticky_id: val.sticky_id,
load_balancing_parameters: val.load_balancing_parameters,
backup: val.backup,
}
}
}
impl PathRule {
pub fn prefix<S>(value: S) -> Self
where
S: ToString,
{
Self {
kind: PathRuleKind::Prefix.into(),
value: value.to_string(),
}
}
pub fn regex<S>(value: S) -> Self
where
S: ToString,
{
Self {
kind: PathRuleKind::Regex.into(),
value: value.to_string(),
}
}
pub fn equals<S>(value: S) -> Self
where
S: ToString,
{
Self {
kind: PathRuleKind::Equals.into(),
value: value.to_string(),
}
}
pub fn from_cli_options(
path_prefix: Option<String>,
path_regex: Option<String>,
path_equals: Option<String>,
) -> Self {
match (path_prefix, path_regex, path_equals) {
(Some(prefix), _, _) => PathRule {
kind: PathRuleKind::Prefix as i32,
value: prefix,
},
(None, Some(regex), _) => PathRule {
kind: PathRuleKind::Regex as i32,
value: regex,
},
(None, None, Some(equals)) => PathRule {
kind: PathRuleKind::Equals as i32,
value: equals,
},
_ => PathRule::default(),
}
}
}
pub fn is_default_path_rule(p: &PathRule) -> bool {
PathRuleKind::try_from(p.kind) == Ok(PathRuleKind::Prefix) && p.value.is_empty()
}
impl std::fmt::Display for PathRule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match PathRuleKind::try_from(self.kind) {
Ok(PathRuleKind::Prefix) => write!(f, "prefix '{}'", self.value),
Ok(PathRuleKind::Regex) => write!(f, "regexp '{}'", self.value),
Ok(PathRuleKind::Equals) => write!(f, "equals '{}'", self.value),
Err(_) => write!(f, ""),
}
}
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TcpFrontend {
pub cluster_id: String,
pub address: SocketAddr,
pub tags: BTreeMap<String, String>,
}
impl From<TcpFrontend> for RequestTcpFrontend {
fn from(val: TcpFrontend) -> Self {
RequestTcpFrontend {
cluster_id: val.cluster_id,
address: val.address.to_string(),
tags: val.tags,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Backend {
pub cluster_id: String,
pub backend_id: String,
pub address: SocketAddr,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub sticky_id: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub load_balancing_parameters: Option<LoadBalancingParams>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub backup: Option<bool>,
}
impl Ord for Backend {
fn cmp(&self, o: &Backend) -> Ordering {
self.cluster_id
.cmp(&o.cluster_id)
.then(self.backend_id.cmp(&o.backend_id))
.then(self.sticky_id.cmp(&o.sticky_id))
.then(
self.load_balancing_parameters
.cmp(&o.load_balancing_parameters),
)
.then(self.backup.cmp(&o.backup))
.then(socketaddr_cmp(&self.address, &o.address))
}
}
impl PartialOrd for Backend {
fn partial_cmp(&self, other: &Backend) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Backend {
pub fn to_add_backend(self) -> AddBackend {
AddBackend {
cluster_id: self.cluster_id,
address: self.address.to_string(),
sticky_id: self.sticky_id,
backend_id: self.backend_id,
load_balancing_parameters: self.load_balancing_parameters,
backup: self.backup,
}
}
}
impl fmt::Display for RunState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Serialize)]
struct StatePath {
path: String,
}
pub type MessageId = String;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WorkerResponse {
pub id: MessageId,
pub status: ResponseStatus,
pub message: String,
pub content: Option<ResponseContent>,
}
impl WorkerResponse {
pub fn ok<T>(id: T) -> Self
where
T: ToString,
{
Self {
id: id.to_string(),
message: String::new(),
status: ResponseStatus::Ok,
content: None,
}
}
pub fn ok_with_content<T>(id: T, content: ResponseContent) -> Self
where
T: ToString,
{
Self {
id: id.to_string(),
status: ResponseStatus::Ok,
message: String::new(),
content: Some(content),
}
}
pub fn error<T, U>(id: T, error: U) -> Self
where
T: ToString,
U: ToString,
{
Self {
id: id.to_string(),
message: error.to_string(),
status: ResponseStatus::Failure,
content: None,
}
}
pub fn processing<T>(id: T) -> Self
where
T: ToString,
{
Self {
id: id.to_string(),
message: String::new(),
status: ResponseStatus::Processing,
content: None,
}
}
pub fn status<T>(id: T, status: ResponseStatus) -> Self
where
T: ToString,
{
Self {
id: id.to_string(),
message: String::new(),
status,
content: None,
}
}
pub fn is_failure(&self) -> bool {
self.status == ResponseStatus::Failure
}
}
impl fmt::Display for WorkerResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}-{:?}", self.id, self.status)
}
}
impl fmt::Display for FilteredTimeSerie {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"FilteredTimeSerie {{\nlast_second: {},\nlast_minute:\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}\nlast_hour:\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n}}",
self.last_second,
&self.last_minute[0..10], &self.last_minute[10..20], &self.last_minute[20..30], &self.last_minute[30..40], &self.last_minute[40..50], &self.last_minute[50..60],
&self.last_hour[0..10], &self.last_hour[10..20], &self.last_hour[20..30], &self.last_hour[30..40], &self.last_hour[40..50], &self.last_hour[50..60])
}
}
fn socketaddr_cmp(a: &SocketAddr, b: &SocketAddr) -> Ordering {
a.ip().cmp(&b.ip()).then(a.port().cmp(&b.port()))
}