Skip to content

Commit 6c0ad12

Browse files
author
Meir Shpilraien (Spielrein)
authored
Fix string verbatim reply to work correctly. (#315)
Current behaviour is to append to format to the data. This is wrong because the format need to pass separately to Redis. Also, a wrong RedisModuleAPI was used, we need to use the API that allows to pass the format (otherwise Redis uses the default `txt` format).
1 parent 5add38e commit 6c0ad12

File tree

4 files changed

+52
-16
lines changed

4 files changed

+52
-16
lines changed

src/context/call_reply.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
ptr::NonNull,
88
};
99

10-
use crate::raw::*;
10+
use crate::{raw::*, RedisError};
1111

1212
pub struct StringCallReply<'root> {
1313
reply: NonNull<RedisModuleCallReply>,
@@ -531,13 +531,48 @@ pub struct VerbatimStringCallReply<'root> {
531531
_dummy: PhantomData<&'root ()>,
532532
}
533533

534+
/// RESP3 state that the verbatim string format must be of length 3.
535+
const VERBATIM_FORMAT_LENGTH: usize = 3;
536+
/// The string format of a verbatim string ([VerbatimStringCallReply]).
537+
#[repr(transparent)]
538+
#[derive(Debug, Default, Copy, Clone, PartialEq)]
539+
pub struct VerbatimStringFormat(pub [c_char; VERBATIM_FORMAT_LENGTH]);
540+
541+
impl TryFrom<&str> for VerbatimStringFormat {
542+
type Error = RedisError;
543+
544+
fn try_from(value: &str) -> Result<Self, Self::Error> {
545+
if value.len() != 3 {
546+
return Err(RedisError::String(format!(
547+
"Verbatim format length must be {VERBATIM_FORMAT_LENGTH}."
548+
)));
549+
}
550+
let mut res = VerbatimStringFormat::default();
551+
value
552+
.chars()
553+
.into_iter()
554+
.take(3)
555+
.enumerate()
556+
.try_for_each(|(i, c)| {
557+
if c as u32 >= 127 {
558+
return Err(RedisError::String(
559+
"Verbatim format must contains only ASCI values.".to_owned(),
560+
));
561+
}
562+
res.0[i] = c as c_char;
563+
Ok(())
564+
})?;
565+
Ok(res)
566+
}
567+
}
568+
534569
impl<'root> VerbatimStringCallReply<'root> {
535570
/// Return the verbatim string value of the [VerbatimStringCallReply] as a tuple.
536571
/// The first entry represents the format, the second entry represent the data.
537572
/// Return None if the format is not a valid utf8
538-
pub fn to_parts(&self) -> Option<(String, Vec<u8>)> {
539-
self.as_parts()
540-
.map(|(format, data)| (format.to_string(), data.to_vec()))
573+
pub fn to_parts(&self) -> Option<(VerbatimStringFormat, Vec<u8>)> {
574+
let (format, data) = self.as_parts()?;
575+
Some((format.try_into().ok()?, data.to_vec()))
541576
}
542577

543578
/// Borrow the verbatim string value of the [VerbatimStringCallReply] as a tuple.

src/context/mod.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -425,15 +425,12 @@ impl Context {
425425
raw::reply_with_big_number(self.ctx, s.as_ptr().cast::<c_char>(), s.len())
426426
}
427427

428-
Ok(RedisValue::VerbatimString((format, mut data))) => {
429-
let mut final_data = format.as_bytes().to_vec();
430-
final_data.append(&mut data);
431-
raw::reply_with_verbatim_string(
432-
self.ctx,
433-
final_data.as_ptr().cast::<c_char>(),
434-
final_data.len(),
435-
)
436-
}
428+
Ok(RedisValue::VerbatimString((format, data))) => raw::reply_with_verbatim_string(
429+
self.ctx,
430+
data.as_ptr().cast(),
431+
data.len(),
432+
format.0.as_ptr().cast(),
433+
),
437434

438435
Ok(RedisValue::BulkRedisString(s)) => raw::reply_with_string(self.ctx, s.inner),
439436

src/raw.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,9 @@ pub fn reply_with_verbatim_string(
435435
ctx: *mut RedisModuleCtx,
436436
s: *const c_char,
437437
len: size_t,
438+
format: *const c_char,
438439
) -> Status {
439-
unsafe { RedisModule_ReplyWithVerbatimString.unwrap()(ctx, s, len).into() }
440+
unsafe { RedisModule_ReplyWithVerbatimStringType.unwrap()(ctx, s, len, format).into() }
440441
}
441442

442443
// Sets the expiry on a key.

src/redisvalue.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::{context::call_reply::CallResult, CallReply, RedisError, RedisString};
1+
use crate::{
2+
context::call_reply::{CallResult, VerbatimStringFormat},
3+
CallReply, RedisError, RedisString,
4+
};
25
use std::{
36
collections::{HashMap, HashSet},
47
hash::Hash,
@@ -24,7 +27,7 @@ pub enum RedisValue {
2427
Bool(bool),
2528
Float(f64),
2629
BigNumber(String),
27-
VerbatimString((String, Vec<u8>)),
30+
VerbatimString((VerbatimStringFormat, Vec<u8>)),
2831
Array(Vec<RedisValue>),
2932
StaticError(&'static str),
3033
Map(HashMap<RedisValueKey, RedisValue>),

0 commit comments

Comments
 (0)