use crate::errors::Result;
use tonic::transport::Channel;
mod api {
tonic::include_proto!("agones.dev.sdk.beta");
}
use api::sdk_client::SdkClient;
#[derive(Clone)]
pub struct Beta {
client: SdkClient<Channel>,
}
impl Beta {
pub(crate) fn new(ch: Channel) -> Self {
Self {
client: SdkClient::new(ch),
}
}
#[inline]
pub async fn get_counter_count(&mut self, key: &str) -> Result<i64> {
Ok(self
.client
.get_counter(api::GetCounterRequest { name: key.to_string() })
.await
.map(|c| c.into_inner().count)?)
}
#[inline]
pub async fn increment_counter(&mut self, key: &str, amount: i64) -> Result<()> {
Ok(self
.client
.update_counter(api::UpdateCounterRequest {
counter_update_request: Some(api::CounterUpdateRequest {
name: key.to_string(),
count: None,
capacity: None,
count_diff: amount,
}),
})
.await
.map(|_| ())?)
}
#[inline]
pub async fn decrement_counter(&mut self, key: &str, amount: i64) -> Result<()> {
Ok(self
.client
.update_counter(api::UpdateCounterRequest {
counter_update_request: Some(api::CounterUpdateRequest {
name: key.to_string(),
count: None,
capacity: None,
count_diff: -amount,
}),
})
.await
.map(|_| ())?)
}
#[inline]
pub async fn set_counter_count(&mut self, key: &str, amount: i64) -> Result<()> {
Ok(self
.client
.update_counter(api::UpdateCounterRequest {
counter_update_request: Some(api::CounterUpdateRequest {
name: key.to_string(),
count: Some(amount.into()),
capacity: None,
count_diff: 0,
}),
})
.await
.map(|_| ())?)
}
#[inline]
pub async fn get_counter_capacity(&mut self, key: &str) -> Result<i64> {
Ok(self
.client
.get_counter(api::GetCounterRequest { name: key.to_string() })
.await
.map(|c| c.into_inner().capacity)?)
}
#[inline]
pub async fn set_counter_capacity(&mut self, key: &str, amount: i64) -> Result<()> {
Ok(self
.client
.update_counter(api::UpdateCounterRequest {
counter_update_request: Some(api::CounterUpdateRequest {
name: key.to_string(),
count: None,
capacity: Some(amount.into()),
count_diff: 0,
}),
})
.await
.map(|_| ())?)
}
#[inline]
pub async fn get_list_capacity(&mut self, key: &str) -> Result<i64> {
Ok(self
.client
.get_list(api::GetListRequest { name: key.to_string() })
.await
.map(|l| l.into_inner().capacity)?)
}
#[inline]
pub async fn set_list_capacity(&mut self, key: &str, amount: i64) -> Result<()> {
Ok(self
.client
.update_list(api::UpdateListRequest {
list: Some(api::List {
name: key.to_string(),
capacity: amount,
values: vec![],
}),
update_mask: Some(prost_types::FieldMask { paths: vec!["capacity".to_string()] }),
})
.await
.map(|_| ())?)
}
#[inline]
pub async fn list_contains(&mut self, key: &str, value: &str) -> Result<bool> {
Ok(self
.client
.get_list(api::GetListRequest { name: key.to_string() })
.await
.map(|l| l.into_inner().values.contains(&value.to_string()))?)
}
#[inline]
pub async fn get_list_length(&mut self, key: &str) -> Result<usize> {
Ok(self
.client
.get_list(api::GetListRequest { name: key.to_string() })
.await
.map(|l| l.into_inner().values.len())?)
}
#[inline]
pub async fn get_list_values(&mut self, key: &str) -> Result<Vec<String>> {
Ok(self
.client
.get_list(api::GetListRequest { name: key.to_string() })
.await
.map(|l| l.into_inner().values)?)
}
#[inline]
pub async fn append_list_value(&mut self, key: &str, value: &str) -> Result<()> {
Ok(self
.client
.add_list_value(api::AddListValueRequest {
name: key.to_string(),
value: value.to_string(),
})
.await
.map(|_| ())?)
}
#[inline]
pub async fn delete_list_value(&mut self, key: &str, value: &str) -> Result<()> {
Ok(self
.client
.remove_list_value(api::RemoveListValueRequest {
name: key.to_string(),
value: value.to_string(),
})
.await
.map(|_| ())?)
}
}
#[cfg(test)]
mod tests {
type Result<T> = std::result::Result<T, String>;
use std::collections::HashMap;
#[derive(Debug, PartialEq)]
struct Counter {
name: String,
count: i64,
capacity: i64,
}
#[derive(Debug, PartialEq)]
struct List {
name: String,
values: Vec<String>,
capacity: i64,
}
struct MockBeta {
counters: HashMap<String, Counter>,
lists: HashMap<String, List>,
}
impl MockBeta {
fn new() -> Self {
Self {
counters: HashMap::new(),
lists: HashMap::new(),
}
}
async fn get_counter_count(&mut self, key: &str) -> Result<i64> {
self.counters.get(key)
.map(|c| c.count)
.ok_or_else::<String, _>(|| format!("counter not found: {}", key))
}
async fn get_counter_capacity(&mut self, key: &str) -> Result<i64> {
self.counters.get(key)
.map(|c| c.capacity)
.ok_or_else::<String, _>(|| format!("counter not found: {}", key))
}
async fn set_counter_capacity(&mut self, key: &str, amount: i64) -> Result<()> {
let counter = self.counters.get_mut(key)
.ok_or_else::<String, _>(|| format!("counter not found: {}", key))?;
if amount < 0 {
return Err("capacity must be >= 0".to_string());
}
counter.capacity = amount;
Ok(())
}
async fn set_counter_count(&mut self, key: &str, amount: i64) -> Result<()> {
let counter = self.counters.get_mut(key)
.ok_or_else::<String, _>(|| format!("counter not found: {}", key))?;
if amount < 0 || amount > counter.capacity {
return Err("count out of range".to_string());
}
counter.count = amount;
Ok(())
}
async fn increment_counter(&mut self, key: &str, amount: i64) -> Result<()> {
let counter = self.counters.get_mut(key)
.ok_or_else::<String, _>(|| format!("counter not found: {}", key))?;
let new_count = counter.count + amount;
if amount < 0 || new_count > counter.capacity {
return Err("increment out of range".to_string());
}
counter.count = new_count;
Ok(())
}
async fn decrement_counter(&mut self, key: &str, amount: i64) -> Result<()> {
let counter = self.counters.get_mut(key)
.ok_or_else::<String, _>(|| format!("counter not found: {}", key))?;
let new_count = counter.count - amount;
if amount < 0 || new_count < 0 {
return Err("decrement out of range".to_string());
}
counter.count = new_count;
Ok(())
}
async fn get_list_capacity(&mut self, key: &str) -> Result<i64> {
self.lists.get(key)
.map(|l| l.capacity)
.ok_or_else::<String, _>(|| format!("list not found: {}", key))
}
async fn set_list_capacity(&mut self, key: &str, amount: i64) -> Result<()> {
let list = self.lists.get_mut(key)
.ok_or_else::<String, _>(|| format!("list not found: {}", key))?;
if amount < 0 || amount > 1000 {
return Err("capacity out of range".to_string());
}
list.capacity = amount;
if list.values.len() > amount as usize {
list.values.truncate(amount as usize);
}
Ok(())
}
async fn get_list_length(&mut self, key: &str) -> Result<usize> {
self.lists.get(key)
.map(|l| l.values.len())
.ok_or_else::<String, _>(|| format!("list not found: {}", key))
}
async fn get_list_values(&mut self, key: &str) -> Result<Vec<String>> {
self.lists.get(key)
.map(|l| l.values.clone())
.ok_or_else::<String, _>(|| format!("list not found: {}", key))
}
async fn list_contains(&mut self, key: &str, value: &str) -> Result<bool> {
self.lists.get(key)
.map(|l| l.values.contains(&value.to_string()))
.ok_or_else::<String, _>(|| format!("list not found: {}", key))
}
async fn append_list_value(&mut self, key: &str, value: &str) -> Result<()> {
let list = self.lists.get_mut(key)
.ok_or_else::<String, _>(|| format!("list not found: {}", key))?;
if list.values.len() >= list.capacity as usize {
return Err("no available capacity".to_string());
}
if list.values.contains(&value.to_string()) {
return Err("already exists".to_string());
}
list.values.push(value.to_string());
Ok(())
}
async fn delete_list_value(&mut self, key: &str, value: &str) -> Result<()> {
let list = self.lists.get_mut(key)
.ok_or_else::<String, _>(|| format!("list not found: {}", key))?;
if let Some(pos) = list.values.iter().position(|v| v == value) {
list.values.remove(pos);
Ok(())
} else {
Err("not found".to_string())
}
}
}
#[tokio::test]
async fn test_beta_get_and_update_counter() {
let mut beta = MockBeta::new();
beta.counters.insert("sessions".to_string(), Counter { name: "sessions".to_string(), count: 21, capacity: 42 });
beta.counters.insert("games".to_string(), Counter { name: "games".to_string(), count: 12, capacity: 24 });
beta.counters.insert("gamers".to_string(), Counter { name: "gamers".to_string(), count: 263, capacity: 500 });
{
let count = beta.get_counter_count("sessions").await.unwrap();
assert_eq!(count, 21);
let capacity = beta.get_counter_capacity("sessions").await.unwrap();
assert_eq!(capacity, 42);
let want_capacity = 25;
beta.set_counter_capacity("sessions", want_capacity).await.unwrap();
let capacity = beta.get_counter_capacity("sessions").await.unwrap();
assert_eq!(capacity, want_capacity);
let want_count = 10;
beta.set_counter_count("sessions", want_count).await.unwrap();
let count = beta.get_counter_count("sessions").await.unwrap();
assert_eq!(count, want_count);
}
{
assert!(beta.get_counter_count("secessions").await.is_err());
assert!(beta.get_counter_capacity("secessions").await.is_err());
assert!(beta.set_counter_capacity("secessions", 100).await.is_err());
assert!(beta.set_counter_count("secessions", 0).await.is_err());
}
{
let count = beta.get_counter_count("games").await.unwrap();
assert_eq!(count, 12);
assert!(beta.decrement_counter("games", 21).await.is_err());
let count = beta.get_counter_count("games").await.unwrap();
assert_eq!(count, 12);
assert!(beta.decrement_counter("games", -12).await.is_err());
let count = beta.get_counter_count("games").await.unwrap();
assert_eq!(count, 12);
beta.decrement_counter("games", 12).await.unwrap();
let count = beta.get_counter_count("games").await.unwrap();
assert_eq!(count, 0);
}
}
#[tokio::test]
async fn test_beta_increment_counter_fails_then_success() {
let mut beta = MockBeta::new();
beta.counters.insert("gamers".to_string(), Counter { name: "gamers".to_string(), count: 263, capacity: 500 });
{
let count = beta.get_counter_count("gamers").await.unwrap();
assert_eq!(count, 263);
assert!(beta.increment_counter("gamers", 250).await.is_err());
let count = beta.get_counter_count("gamers").await.unwrap();
assert_eq!(count, 263);
assert!(beta.increment_counter("gamers", -237).await.is_err());
let count = beta.get_counter_count("gamers").await.unwrap();
assert_eq!(count, 263);
beta.increment_counter("gamers", 237).await.unwrap();
let count = beta.get_counter_count("gamers").await.unwrap();
assert_eq!(count, 500);
}
}
#[tokio::test]
async fn test_beta_get_and_update_list() {
let mut beta = MockBeta::new();
beta.lists.insert("foo".to_string(), List { name: "foo".to_string(), values: vec![], capacity: 2 });
beta.lists.insert("bar".to_string(), List { name: "bar".to_string(), values: vec!["abc".to_string(), "def".to_string()], capacity: 5 });
beta.lists.insert("baz".to_string(), List { name: "baz".to_string(), values: vec!["123".to_string(), "456".to_string(), "789".to_string()], capacity: 5 });
{
let capacity = beta.get_list_capacity("foo").await.unwrap();
assert_eq!(capacity, 2);
let want_capacity = 5;
beta.set_list_capacity("foo", want_capacity).await.unwrap();
let capacity = beta.get_list_capacity("foo").await.unwrap();
assert_eq!(capacity, want_capacity);
}
{
let length = beta.get_list_length("bar").await.unwrap();
assert_eq!(length, 2);
let values = beta.get_list_values("bar").await.unwrap();
assert_eq!(values, vec!["abc".to_string(), "def".to_string()]);
beta.append_list_value("bar", "ghi").await.unwrap();
let length = beta.get_list_length("bar").await.unwrap();
assert_eq!(length, 3);
let want_values = vec!["abc".to_string(), "def".to_string(), "ghi".to_string()];
let values = beta.get_list_values("bar").await.unwrap();
assert_eq!(values, want_values);
let contains = beta.list_contains("bar", "ghi").await.unwrap();
assert!(contains);
}
{
let length = beta.get_list_length("baz").await.unwrap();
assert_eq!(length, 3);
let values = beta.get_list_values("baz").await.unwrap();
assert_eq!(values, vec!["123".to_string(), "456".to_string(), "789".to_string()]);
beta.delete_list_value("baz", "456").await.unwrap();
let length = beta.get_list_length("baz").await.unwrap();
assert_eq!(length, 2);
let want_values = vec!["123".to_string(), "789".to_string()];
let values = beta.get_list_values("baz").await.unwrap();
assert_eq!(values, want_values);
let contains = beta.list_contains("baz", "456").await.unwrap();
assert!(!contains);
}
}
}