use std::collections::HashMap;
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::client::HttpClient;
use crate::error::Result;
use crate::types::{CatalogService, Node, QueryMeta, QueryOptions, WriteMeta, WriteOptions};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CatalogRegistration {
#[serde(rename = "ID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub node: String,
pub address: String,
#[serde(default)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub tagged_addresses: HashMap<String, String>,
#[serde(default)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub node_meta: HashMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub datacenter: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub service: Option<CatalogServiceRegistration>,
#[serde(skip_serializing_if = "Option::is_none")]
pub check: Option<CatalogCheckRegistration>,
#[serde(default)]
pub skip_node_update: bool,
}
impl CatalogRegistration {
pub fn new(node: &str, address: &str) -> Self {
Self {
id: None,
node: node.to_string(),
address: address.to_string(),
tagged_addresses: HashMap::new(),
node_meta: HashMap::new(),
datacenter: None,
service: None,
check: None,
skip_node_update: false,
}
}
pub fn with_service(mut self, service: CatalogServiceRegistration) -> Self {
self.service = Some(service);
self
}
pub fn with_check(mut self, check: CatalogCheckRegistration) -> Self {
self.check = Some(check);
self
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CatalogServiceRegistration {
#[serde(rename = "ID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub service: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
#[serde(default)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub meta: HashMap<String, String>,
}
impl CatalogServiceRegistration {
pub fn new(name: &str) -> Self {
Self {
id: None,
service: name.to_string(),
tags: Vec::new(),
address: None,
port: None,
meta: HashMap::new(),
}
}
pub fn with_id(mut self, id: &str) -> Self {
self.id = Some(id.to_string());
self
}
pub fn with_address(mut self, address: &str) -> Self {
self.address = Some(address.to_string());
self
}
pub fn with_port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
pub fn with_tags(mut self, tags: Vec<String>) -> Self {
self.tags = tags;
self
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CatalogCheckRegistration {
pub node: String,
#[serde(rename = "CheckID")]
pub check_id: String,
pub name: String,
pub status: String,
#[serde(rename = "ServiceID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub service_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CatalogDeregistration {
pub node: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub datacenter: Option<String>,
#[serde(rename = "ServiceID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub service_id: Option<String>,
#[serde(rename = "CheckID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub check_id: Option<String>,
}
impl CatalogDeregistration {
pub fn node(name: &str) -> Self {
Self {
node: name.to_string(),
datacenter: None,
service_id: None,
check_id: None,
}
}
pub fn service(node: &str, service_id: &str) -> Self {
Self {
node: node.to_string(),
datacenter: None,
service_id: Some(service_id.to_string()),
check_id: None,
}
}
pub fn check(node: &str, check_id: &str) -> Self {
Self {
node: node.to_string(),
datacenter: None,
service_id: None,
check_id: Some(check_id.to_string()),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CatalogNode {
pub node: Node,
pub services: HashMap<String, CatalogService>,
}
pub struct Catalog {
client: Arc<HttpClient>,
}
impl Catalog {
pub fn new(client: Arc<HttpClient>) -> Self {
Self { client }
}
pub async fn register(&self, registration: &CatalogRegistration, opts: Option<&WriteOptions>) -> Result<(bool, WriteMeta)> {
let mut builder = self.client.put("/v1/catalog/register").json(registration);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write_bool(builder).await
}
pub async fn deregister(&self, deregistration: &CatalogDeregistration, opts: Option<&WriteOptions>) -> Result<(bool, WriteMeta)> {
let mut builder = self.client.put("/v1/catalog/deregister").json(deregistration);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write_bool(builder).await
}
pub async fn datacenters(&self) -> Result<Vec<String>> {
let builder = self.client.get("/v1/catalog/datacenters");
self.client.execute_json(builder).await
}
pub async fn nodes(&self, opts: Option<&QueryOptions>) -> Result<(Vec<Node>, QueryMeta)> {
let mut builder = self.client.get("/v1/catalog/nodes");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn node(&self, name: &str, opts: Option<&QueryOptions>) -> Result<(Option<CatalogNode>, QueryMeta)> {
let path = format!("/v1/catalog/node/{}", name);
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
let response = self.client.execute(builder).await?;
let meta = QueryMeta::default();
let status = response.status();
if status.as_u16() == 404 {
return Ok((None, meta));
}
if status.is_success() {
let node: CatalogNode = response.json().await.map_err(crate::error::ConsulError::HttpError)?;
Ok((Some(node), meta))
} else {
let text = response.text().await.unwrap_or_default();
Err(crate::error::ConsulError::api_error(status.as_u16(), text))
}
}
pub async fn services(&self, opts: Option<&QueryOptions>) -> Result<(HashMap<String, Vec<String>>, QueryMeta)> {
let mut builder = self.client.get("/v1/catalog/services");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn service(
&self,
service: &str,
tag: Option<&str>,
opts: Option<&QueryOptions>,
) -> Result<(Vec<CatalogService>, QueryMeta)> {
let mut path = format!("/v1/catalog/service/{}", service);
if let Some(t) = tag {
path.push_str(&format!("?tag={}", t));
}
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn node_services(&self, node: &str, opts: Option<&QueryOptions>) -> Result<(Option<CatalogNode>, QueryMeta)> {
self.node(node, opts).await
}
}