Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ let value: String = client.get("foo").unwrap().unwrap();
assert_eq!(value, "foobarbaz");

// cas(check and set):
let (_, _, cas_token) = client.get("foo").unwrap().unwrap();
let cas_id = cas_id.unwrap();
let (value, _flags, cas_token): (String, u32, Option<u64>) = client.get("foo").unwrap().unwrap();
assert_eq!(value, "foobarbaz");
let cas_id = cas_token.unwrap();
client.cas("foo", "qux", 0, cas_id).unwrap();

// delete value:
Expand Down
7 changes: 3 additions & 4 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ impl Client {
/// ```rust
/// let client = memcache::Client::connect("memcache://localhost:12345").unwrap();
/// client.set("foo", "bar", 10).unwrap();
/// # client.flush().unwrap();
/// client.flush().unwrap();
/// ```
pub fn set<V: ToMemcacheValue<Stream>>(&self, key: &str, value: V, expiration: u32) -> Result<(), MemcacheError> {
check_key_len(key)?;
Expand All @@ -293,13 +293,12 @@ impl Client {
/// Example:
///
/// ```rust
/// use std::collections::HashMap;
/// let client = memcache::Client::connect("memcache://localhost:12345").unwrap();
/// client.set("foo", "bar", 10).unwrap();
/// let (_, _, cas) = client.get("foo").unwrap().unwrap();
/// let (_, _, cas): (String, u32, Option<u64>) = client.get("foo").unwrap().unwrap();
/// let cas = cas.unwrap();
/// assert_eq!(true, client.cas("foo", "bar2", 10, cas).unwrap());
/// # client.flush().unwrap();
/// client.flush().unwrap();
/// ```
pub fn cas<V: ToMemcacheValue<Stream>>(
&self,
Expand Down
12 changes: 9 additions & 3 deletions src/protocol/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
pub struct Options {
pub noreply: bool,
pub exptime: u32,
pub flags: u32,

Check warning on line 16 in src/protocol/ascii.rs

View workflow job for this annotation

GitHub Actions / ci (stable)

field `flags` is never read

Check warning on line 16 in src/protocol/ascii.rs

View workflow job for this annotation

GitHub Actions / ci (stable)

field `flags` is never read
pub cas: Option<u64>,
}

Expand Down Expand Up @@ -155,14 +155,20 @@
}

fn get<V: FromMemcacheValueExt>(&mut self, key: &str) -> Result<Option<V>, MemcacheError> {
write!(self.reader.get_mut(), "get {}\r\n", key)?;
let (command, has_cas) = if V::requires_cas() {
("gets", true)
} else {
("get", false)
};

write!(self.reader.get_mut(), "{} {}\r\n", command, key)?;

if let Some((k, v)) = self.parse_get_response(false)? {
if let Some((k, v)) = self.parse_get_response(has_cas)? {
if k != key {
Err(ServerError::BadResponse(Cow::Borrowed(
"key doesn't match in the response",
)))?
} else if self.parse_get_response::<V>(false)?.is_none() {
} else if self.parse_get_response::<V>(has_cas)?.is_none() {
Ok(Some(v))
} else {
Err(ServerError::BadResponse(Cow::Borrowed("Expected end of get response")))?
Expand Down
30 changes: 30 additions & 0 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ pub trait FromMemcacheValue: Sized {

pub trait FromMemcacheValueExt: Sized {
fn from_memcache_value(value: Vec<u8>, flags: u32, cas: Option<u64>) -> MemcacheValue<Self>;

/// Returns whether this type requires CAS token
fn requires_cas() -> bool {
false
}
}

impl<V: FromMemcacheValue> FromMemcacheValueExt for V {
Expand All @@ -134,6 +139,10 @@ impl FromMemcacheValueExt for (Vec<u8>, u32, Option<u64>) {
fn from_memcache_value(value: Vec<u8>, flags: u32, cas: Option<u64>) -> MemcacheValue<Self> {
return Ok((value, flags, cas));
}

fn requires_cas() -> bool {
true
}
}

impl FromMemcacheValue for (Vec<u8>, u32) {
Expand All @@ -148,6 +157,16 @@ impl FromMemcacheValue for Vec<u8> {
}
}

impl FromMemcacheValueExt for (String, u32, Option<u64>) {
fn from_memcache_value(value: Vec<u8>, flags: u32, cas: Option<u64>) -> MemcacheValue<Self> {
return Ok((String::from_utf8(value)?, flags, cas));
}

fn requires_cas() -> bool {
true
}
}

impl FromMemcacheValue for (String, u32) {
fn from_memcache_value(value: Vec<u8>, flags: u32) -> MemcacheValue<Self> {
return Ok((String::from_utf8(value)?, flags));
Expand Down Expand Up @@ -175,6 +194,17 @@ macro_rules! impl_from_memcache_value_for_number {
Ok(($ty::from_str(s.as_str())?, flags))
}
}

impl FromMemcacheValueExt for ($ty, u32, Option<u64>) {
fn from_memcache_value(value: Vec<u8>, flags: u32, cas: Option<u64>) -> MemcacheValue<Self> {
let s: String = FromMemcacheValue::from_memcache_value(value, 0)?;
Ok(($ty::from_str(s.as_str())?, flags, cas))
}

fn requires_cas() -> bool {
true
}
}
};
}

Expand Down
37 changes: 37 additions & 0 deletions tests/test_ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,43 @@ fn test_get_with_flags() {
// Test our new FromMemcacheValue implementation
let value: Option<(String, u32)> = client.get("test_key").unwrap();
assert_eq!(value, Some(("test_value".to_string(), 114514)));
}

#[test]
fn test_get_with_cas() {
let client = memcache::connect("memcache://localhost:12345?protocol=ascii").unwrap();
client.flush().unwrap();

// Set a value
client.set("test_key", "test_value", 0).unwrap();

// Test get with CAS token
let value: Option<(String, u32, Option<u64>)> = client.get("test_key").unwrap();
let (value_str, _flags, cas) = value.unwrap();
assert_eq!(value_str, "test_value");
assert!(cas.is_some(), "CAS token should be present");
}

#[test]
fn test_cas() {
let client = memcache::Client::connect("memcache://localhost:12345?protocol=ascii").unwrap();
client.flush().unwrap();

// Test using get with CAS token for cas operation
client.set("test_cas_key", "initial_value", 0).unwrap();
let cas_value: Option<(String, u32, Option<u64>)> = client.get("test_cas_key").unwrap();
let (_, _, cas_token) = cas_value.unwrap();
assert!(cas_token.is_some(), "CAS token should be present from get");

// Use the CAS token from get to update the value
assert_eq!(
true,
client
.cas("test_cas_key", "updated_value", 0, cas_token.unwrap())
.unwrap()
);

// Verify the update worked
let updated_value: Option<String> = client.get("test_cas_key").unwrap();
assert_eq!(updated_value, Some("updated_value".into()));
}
21 changes: 19 additions & 2 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,25 @@ fn test_cas() {
.cas("not_exists_key", "bar", 0, ascii_foo_value.2.unwrap())
.unwrap()
);

// Test using get with CAS token for cas operation
client.set("test_cas_key", "initial_value", 0).unwrap();
let cas_value: Option<(String, u32, Option<u64>)> = client.get("test_cas_key").unwrap();
let (_, _, cas_token) = cas_value.unwrap();
assert!(cas_token.is_some(), "CAS token should be present from get");

// Use the CAS token from get to update the value
assert_eq!(
true,
client
.cas("test_cas_key", "updated_value", 0, cas_token.unwrap())
.unwrap()
);

// Verify the update worked
let updated_value: Option<String> = client.get("test_cas_key").unwrap();
assert_eq!(updated_value, Some("updated_value".into()));

client.flush().unwrap();
}
}
Expand Down Expand Up @@ -291,6 +310,4 @@ fn test_get_with_flags() {
// Test our new FromMemcacheValue implementation
let value: Option<(String, u32)> = client.get("test_key").unwrap();
assert_eq!(value, Some(("test_value".to_string(), 114514)));

client.flush().unwrap();
}
Loading