use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::client::HttpClient;
use crate::error::Result;
use crate::types::{QueryMeta, QueryOptions, WriteMeta, WriteOptions};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SessionBehavior {
#[serde(rename = "release")]
Release,
#[serde(rename = "delete")]
Delete,
}
impl Default for SessionBehavior {
fn default() -> Self {
Self::Release
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SessionEntry {
#[serde(rename = "ID")]
pub id: String,
#[serde(default)]
pub name: String,
pub node: String,
#[serde(default)]
pub lock_delay: u64,
#[serde(default)]
pub behavior: Option<String>,
#[serde(rename = "TTL")]
#[serde(default)]
pub ttl: String,
#[serde(default)]
pub checks: Vec<String>,
#[serde(default)]
pub node_checks: Vec<String>,
#[serde(default)]
pub service_checks: Vec<ServiceCheck>,
pub create_index: u64,
pub modify_index: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ServiceCheck {
#[serde(rename = "ID")]
pub id: String,
#[serde(default)]
pub namespace: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SessionRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lock_delay: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub behavior: Option<String>,
#[serde(rename = "TTL")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ttl: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub checks: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_checks: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub service_checks: Option<Vec<ServiceCheck>>,
}
impl Default for SessionRequest {
fn default() -> Self {
Self::new()
}
}
impl SessionRequest {
pub fn new() -> Self {
Self {
name: None,
node: None,
lock_delay: None,
behavior: None,
ttl: None,
checks: None,
node_checks: None,
service_checks: None,
}
}
pub fn with_name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
pub fn with_node(mut self, node: &str) -> Self {
self.node = Some(node.to_string());
self
}
pub fn with_lock_delay(mut self, delay: &str) -> Self {
self.lock_delay = Some(delay.to_string());
self
}
pub fn with_ttl(mut self, ttl: &str) -> Self {
self.ttl = Some(ttl.to_string());
self
}
pub fn with_behavior(mut self, behavior: SessionBehavior) -> Self {
self.behavior = Some(match behavior {
SessionBehavior::Release => "release".to_string(),
SessionBehavior::Delete => "delete".to_string(),
});
self
}
pub fn with_checks(mut self, checks: Vec<String>) -> Self {
self.checks = Some(checks);
self
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SessionCreateResponse {
#[serde(rename = "ID")]
pub id: String,
}
pub struct Session {
client: Arc<HttpClient>,
}
impl Session {
pub fn new(client: Arc<HttpClient>) -> Self {
Self { client }
}
pub async fn create(&self, session: &SessionRequest, opts: Option<&WriteOptions>) -> Result<(String, WriteMeta)> {
let mut builder = self.client.put("/v1/session/create").json(session);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
let (response, meta): (SessionCreateResponse, WriteMeta) = self.client.write(builder).await?;
Ok((response.id, meta))
}
pub async fn create_named(&self, name: &str, opts: Option<&WriteOptions>) -> Result<(String, WriteMeta)> {
let session = SessionRequest::new().with_name(name);
self.create(&session, opts).await
}
pub async fn create_with_ttl(&self, name: &str, ttl: &str, opts: Option<&WriteOptions>) -> Result<(String, WriteMeta)> {
let session = SessionRequest::new()
.with_name(name)
.with_ttl(ttl);
self.create(&session, opts).await
}
pub async fn destroy(&self, id: &str, opts: Option<&WriteOptions>) -> Result<(bool, WriteMeta)> {
let path = format!("/v1/session/destroy/{}", id);
let mut builder = self.client.put(&path);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write_bool(builder).await
}
pub async fn info(&self, id: &str, opts: Option<&QueryOptions>) -> Result<(Option<SessionEntry>, QueryMeta)> {
let path = format!("/v1/session/info/{}", id);
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
let (entries, meta): (Vec<SessionEntry>, QueryMeta) = self.client.query(builder).await?;
Ok((entries.into_iter().next(), meta))
}
pub async fn node(&self, node: &str, opts: Option<&QueryOptions>) -> Result<(Vec<SessionEntry>, QueryMeta)> {
let path = format!("/v1/session/node/{}", node);
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 list(&self, opts: Option<&QueryOptions>) -> Result<(Vec<SessionEntry>, QueryMeta)> {
let mut builder = self.client.get("/v1/session/list");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn renew(&self, id: &str, opts: Option<&WriteOptions>) -> Result<(Option<SessionEntry>, WriteMeta)> {
let path = format!("/v1/session/renew/{}", id);
let mut builder = self.client.put(&path);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
let (entries, meta): (Vec<SessionEntry>, WriteMeta) = self.client.write(builder).await?;
Ok((entries.into_iter().next(), meta))
}
pub async fn renew_periodic(&self, id: &str, opts: Option<&WriteOptions>) -> Result<(SessionEntry, WriteMeta)> {
let (entry, meta) = self.renew(id, opts).await?;
match entry {
Some(e) => Ok((e, meta)),
None => Err(crate::error::ConsulError::SessionNotFound(id.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_session_request_builder() {
let session = SessionRequest::new()
.with_name("my-session")
.with_ttl("30s")
.with_lock_delay("15s")
.with_behavior(SessionBehavior::Delete);
assert_eq!(session.name, Some("my-session".to_string()));
assert_eq!(session.ttl, Some("30s".to_string()));
assert_eq!(session.lock_delay, Some("15s".to_string()));
assert_eq!(session.behavior, Some("delete".to_string()));
}
}