Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ keywords = ["memcache", "memcached", "driver", "cache", "database"]
edition = "2018"

[features]
default = ["tls"]
default = ["tls", "json"]
tls = ["openssl"]
json = ["dep:serde", "dep:serde_json"]

[dependencies]
byteorder = "1"
Expand All @@ -20,3 +21,5 @@ rand = "0.8"
enum_dispatch = "0.3"
openssl = { version = "^0.10", optional = true }
r2d2 = "^0.8"
serde = { version = "1.0.197", optional = true , features = ["derive"] }
serde_json = { version = "1.0.115", optional = true }
70 changes: 70 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use crate::stream::Stream;
use crate::value::{FromMemcacheValueExt, ToMemcacheValue};
use r2d2::Pool;

#[cfg(feature = "json")]
use serde::{Deserialize, Serialize};

pub type Stats = HashMap<String, String>;

pub trait Connectable {
Expand Down Expand Up @@ -266,6 +269,47 @@ impl Client {
return Ok(result);
}

/// Get a key from memcached server and deserialize it as JSON. This function is only available when the `json` feature is enabled.
/// The value type should implement `FromMemcacheValueExt` and `serde::Deserialize`.
///
/// Example:
/// ```rust
/// use serde::{Serialize, Deserialize};
///
/// #[derive(Debug, Serialize, Deserialize)]
/// struct MyStruct {
/// value: String,
/// number: i32,
/// flag: bool,
/// array: Vec<i32>,
/// }
///
/// let client = memcache::Client::connect("memcache://localhost:12345").unwrap();
/// let value = MyStruct {
/// value: "hello".to_string(),
/// number: 42,
/// flag: true,
/// array: vec![1, 2, 3],
/// };
/// client.set_json("foo", value, 10).unwrap();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests the set_json function below as well, ensuring that the value is actually set correctly

/// let result: MyStruct = client.get_json("foo").unwrap().unwrap();
///
/// assert_eq!(result.value, "hello");
/// assert_eq!(result.number, 42);
/// assert_eq!(result.flag, true);
/// assert_eq!(result.array, vec![1, 2, 3]);
/// ```
#[cfg(feature = "json")]
pub fn get_json<V: for<'a> Deserialize<'a>>(&self, key: &str) -> Result<Option<V>, MemcacheError> {
let value: Option<String> = self.get(key)?;
let value = match value {
Some(value) => value,
None => return Ok(None),
};

return serde_json::from_str(&value).map_err(|e| MemcacheError::JsonError(e));
}

/// Set a key with associate value into memcached server with expiration seconds.
///
/// Example:
Expand All @@ -280,6 +324,32 @@ impl Client {
return self.get_connection(key).get()?.set(key, value, expiration);
}

/// Set a key with associate value into memcached server with expiration seconds. The value will be serialized as JSON. This function is only available when the `json` feature is enabled.
///
/// Example:
/// ```rust
/// use serde::Serialize;
///
/// #[derive(Serialize)]
/// struct MyStruct {
/// value: String,
/// number: i32,
/// }
///
/// let client = memcache::Client::connect("memcache://localhost:12345").unwrap();
/// let value = MyStruct {
/// value: "hello".to_string(),
/// number: 42,
/// };
/// client.set_json("foo", value, 10).unwrap();
/// client.flush().unwrap();
/// ```
#[cfg(feature = "json")]
pub fn set_json<V: Serialize>(&self, key: &str, value: V, expiration: u32) -> Result<(), MemcacheError> {
let value = serde_json::to_string(&value).map_err(|e| MemcacheError::JsonError(e))?;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By doing it this way we can leverage the existing ToMemcacheValue<Stream> impl on String for storage. That'll make this easy to use as consumers won't have to worry about implementing the trait for their structs.

return self.set(key, value, expiration);
}

/// Compare and swap a key with the associate value into memcached server with expiration seconds.
/// `cas_id` should be obtained from a previous `gets` call.
///
Expand Down
7 changes: 7 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ pub enum MemcacheError {
ParseError(ParseError),
/// ConnectionPool errors
PoolError(r2d2::Error),
/// Json errors (only available with `json` feature) for invalid json strings
#[cfg(feature = "json")]
JsonError(serde_json::Error),
}

impl fmt::Display for MemcacheError {
Expand All @@ -253,6 +256,8 @@ impl fmt::Display for MemcacheError {
MemcacheError::ServerError(ref err) => err.fmt(f),
MemcacheError::CommandError(ref err) => err.fmt(f),
MemcacheError::PoolError(ref err) => err.fmt(f),
#[cfg(feature = "json")]
MemcacheError::JsonError(ref err) => err.fmt(f),
}
}
}
Expand All @@ -269,6 +274,8 @@ impl error::Error for MemcacheError {
MemcacheError::ServerError(_) => None,
MemcacheError::CommandError(_) => None,
MemcacheError::PoolError(ref p) => p.source(),
#[cfg(feature = "json")]
MemcacheError::JsonError(ref e) => e.source(),
}
}
}
Expand Down