Skip to content

Commit 03ff0e1

Browse files
committed
feat(here-now): add limit and offset configuration options
Add `limit` (default `1000`) and `offset` parameters for `here_now` to fetch presence in portions. fix(subscribe): fix duplicated channels and groups for `subscribe` Fix issue that allowed passing duplicated channels and groups for `subscribe`. fix(heartbeat): fix duplicated channels and groups for `heartbeat` Fix issue that allowed passing duplicated channels and groups for `heartbeat`.
1 parent 19b0a5a commit 03ff0e1

File tree

12 files changed

+260
-63
lines changed

12 files changed

+260
-63
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ build = "build.rs"
1717
full = ["publish", "subscribe", "presence", "access", "serde", "reqwest", "crypto", "parse_token", "blocking", "std", "tokio"]
1818

1919
# Enables all default features
20-
default = ["publish", "subscribe", "serde", "reqwest", "std", "blocking", "tokio"]
20+
default = ["publish", "subscribe", "serde", "reqwest", "std", "blocking", "tokio", "presence"]
2121

2222
# [PubNub features]
2323

@@ -79,7 +79,7 @@ spin = "0.9"
7979
phantom-type = { version = "0.4.2", default-features = false }
8080
percent-encoding = { version = "2.1", default-features = false }
8181
base64 = { version = "0.21", features = ["alloc"], default-features = false }
82-
derive_builder = { version = "0.12", default-features = false }
82+
derive_builder = { version = "0.20.2", default-features = false }
8383
uuid = { version = "1.3", features = ["v4"], default-features = false }
8484
snafu = { version = "0.7", features = ["rust_1_46"], default-features = false }
8585
rand = { version = "0.8.5", default-features = false }

examples/no_std/src/here_now.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,23 @@ use pubnub::{
2828
fn custom_random(buf: &mut [u8]) -> Result<(), getrandom::Error> {
2929
// We're using `42` as a random number, because it's the answer
3030
// to the Ultimate Question of Life, the Universe, and Everything.
31-
// In your program, you should use proper random number generator that is supported by your target.
31+
// In your program, you should use proper random number generator that is
32+
// supported by your target.
3233
for i in buf.iter_mut() {
3334
*i = 42;
3435
}
3536

3637
Ok(())
3738
}
3839

39-
// This function is used to register the custom implementation of `getrandom` function.
40+
// This function is used to register the custom implementation of `getrandom`
41+
// function.
4042
#[no_mangle]
4143
unsafe extern "Rust" fn __getrandom_v03_custom(
4244
dest: *mut u8,
4345
len: usize,
4446
) -> Result<(), getrandom::Error> {
47+
#[allow(unsafe_code)]
4548
let buf = unsafe {
4649
// fill the buffer with zeros
4750
core::ptr::write_bytes(dest, 0, len);
@@ -53,7 +56,8 @@ unsafe extern "Rust" fn __getrandom_v03_custom(
5356

5457
// Many targets have very specific requirements for networking, so it's hard to
5558
// provide a generic implementation.
56-
// Depending on the target, you will probably need to implement `Transport` trait.
59+
// Depending on the target, you will probably need to implement `Transport`
60+
// trait.
5761
struct MyTransport;
5862

5963
impl Transport for MyTransport {
@@ -69,8 +73,8 @@ impl Transport for MyTransport {
6973
// As our target does not have `std` library, we need to provide custom
7074
// implementation of `GlobalAlloc` trait.
7175
//
72-
// In your program, you should use proper allocator that is supported by your target.
73-
// Here you have dummy implementation that does nothing.
76+
// In your program, you should use proper allocator that is supported by your
77+
// target. Here you have dummy implementation that does nothing.
7478
#[derive(Default)]
7579
pub struct Allocator;
7680

@@ -87,23 +91,23 @@ static GLOBAL_ALLOCATOR: Allocator = Allocator;
8791
// As our target does not have `std` library, we need to provide custom
8892
// implementation of `panic_handler`.
8993
//
90-
// In your program, you should use proper panic handler that is supported by your target.
91-
// Here you have dummy implementation that does nothing.
94+
// In your program, you should use proper panic handler that is supported by
95+
// your target. Here you have dummy implementation that does nothing.
9296
#[panic_handler]
9397
fn panicking(_: &PanicInfo) -> ! {
9498
loop {}
9599
}
96100

97-
// As we're using `no_main` attribute, we need to define `main` function manually.
98-
// For this example we're using `extern "C"` ABI to make it work.
101+
// As we're using `no_main` attribute, we need to define `main` function
102+
// manually. For this example we're using `extern "C"` ABI to make it work.
99103
#[no_mangle]
100104
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> usize {
101105
publish_example().map(|_| 0).unwrap()
102106
}
103107

104-
// In standard subscribe examples we use `println` macro to print the result of the operation
105-
// and it shows the idea of the example. `no_std` does not support `println` macro,
106-
// so we're using `do_a_thing` function instead.
108+
// In standard subscribe examples we use `println` macro to print the result of
109+
// the operation and it shows the idea of the example. `no_std` does not support
110+
// `println` macro, so we're using `do_a_thing` function instead.
107111
fn do_a_thing<T>(_: &T) {}
108112

109113
// As `no_std` does not support `Error` trait, we use `PubNubError` instead.
@@ -129,8 +133,8 @@ fn publish_example() -> Result<(), PubNubError> {
129133
.include_user_id(true)
130134
.execute_blocking()?;
131135

132-
// As `no_std` does not support `println` macro, we're using `do_a_thing` function instead
133-
// to show possible usage of the result.
136+
// As `no_std` does not support `println` macro, we're using `do_a_thing`
137+
// function instead to show possible usage of the result.
134138

135139
channels_now.iter().for_each(|channel| {
136140
do_a_thing(&channel.name);

examples/no_std/src/presence_state.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,23 @@ struct State {
3535
fn custom_random(buf: &mut [u8]) -> Result<(), getrandom::Error> {
3636
// We're using `42` as a random number, because it's the answer
3737
// to the Ultimate Question of Life, the Universe, and Everything.
38-
// In your program, you should use proper random number generator that is supported by your target.
38+
// In your program, you should use proper random number generator that is
39+
// supported by your target.
3940
for i in buf.iter_mut() {
4041
*i = 42;
4142
}
4243

4344
Ok(())
4445
}
4546

46-
// This function is used to register the custom implementation of `getrandom` function.
47+
// This function is used to register the custom implementation of `getrandom`
48+
// function.
4749
#[no_mangle]
4850
unsafe extern "Rust" fn __getrandom_v03_custom(
4951
dest: *mut u8,
5052
len: usize,
5153
) -> Result<(), getrandom::Error> {
54+
#[allow(unsafe_code)]
5255
let buf = unsafe {
5356
// fill the buffer with zeros
5457
core::ptr::write_bytes(dest, 0, len);
@@ -60,7 +63,8 @@ unsafe extern "Rust" fn __getrandom_v03_custom(
6063

6164
// Many targets have very specific requirements for networking, so it's hard to
6265
// provide a generic implementation.
63-
// Depending on the target, you will probably need to implement `Transport` trait.
66+
// Depending on the target, you will probably need to implement `Transport`
67+
// trait.
6468
struct MyTransport;
6569

6670
impl Transport for MyTransport {
@@ -76,8 +80,8 @@ impl Transport for MyTransport {
7680
// As our target does not have `std` library, we need to provide custom
7781
// implementation of `GlobalAlloc` trait.
7882
//
79-
// In your program, you should use proper allocator that is supported by your target.
80-
// Here you have dummy implementation that does nothing.
83+
// In your program, you should use proper allocator that is supported by your
84+
// target. Here you have dummy implementation that does nothing.
8185
#[derive(Default)]
8286
pub struct Allocator;
8387

@@ -94,23 +98,23 @@ static GLOBAL_ALLOCATOR: Allocator = Allocator;
9498
// As our target does not have `std` library, we need to provide custom
9599
// implementation of `panic_handler`.
96100
//
97-
// In your program, you should use proper panic handler that is supported by your target.
98-
// Here you have dummy implementation that does nothing.
101+
// In your program, you should use proper panic handler that is supported by
102+
// your target. Here you have dummy implementation that does nothing.
99103
#[panic_handler]
100104
fn panicking(_: &PanicInfo) -> ! {
101105
loop {}
102106
}
103107

104-
// As we're using `no_main` attribute, we need to define `main` function manually.
105-
// For this example we're using `extern "C"` ABI to make it work.
108+
// As we're using `no_main` attribute, we need to define `main` function
109+
// manually. For this example we're using `extern "C"` ABI to make it work.
106110
#[no_mangle]
107111
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> usize {
108112
publish_example().map(|_| 0).unwrap()
109113
}
110114

111-
// In standard subscribe examples we use `println` macro to print the result of the operation
112-
// and it shows the idea of the example. `no_std` does not support `println` macro,
113-
// so we're using `do_a_thing` function instead.
115+
// In standard subscribe examples we use `println` macro to print the result of
116+
// the operation and it shows the idea of the example. `no_std` does not support
117+
// `println` macro, so we're using `do_a_thing` function instead.
114118
fn do_a_thing<T>(_: &T) {}
115119

116120
// As `no_std` does not support `Error` trait, we use `PubNubError` instead.
@@ -129,8 +133,8 @@ fn publish_example() -> Result<(), PubNubError> {
129133
.with_user_id("user_id")
130134
.build()?;
131135

132-
// As `no_std` does not support `println` macro, we're using `do_a_thing` function instead
133-
// to show possible usage of the result.
136+
// As `no_std` does not support `println` macro, we're using `do_a_thing`
137+
// function instead to show possible usage of the result.
134138

135139
client
136140
.set_presence_state(State {

examples/no_std/src/publish.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,23 @@ struct Message {
3636
fn custom_random(buf: &mut [u8]) -> Result<(), getrandom::Error> {
3737
// We're using `42` as a random number, because it's the answer
3838
// to the Ultimate Question of Life, the Universe, and Everything.
39-
// In your program, you should use proper random number generator that is supported by your target.
39+
// In your program, you should use proper random number generator that is
40+
// supported by your target.
4041
for i in buf.iter_mut() {
4142
*i = 42;
4243
}
4344

4445
Ok(())
4546
}
4647

47-
// This function is used to register the custom implementation of `getrandom` function.
48+
// This function is used to register the custom implementation of `getrandom`
49+
// function.
4850
#[no_mangle]
4951
unsafe extern "Rust" fn __getrandom_v03_custom(
5052
dest: *mut u8,
5153
len: usize,
5254
) -> Result<(), getrandom::Error> {
55+
#[allow(unsafe_code)]
5356
let buf = unsafe {
5457
// fill the buffer with zeros
5558
core::ptr::write_bytes(dest, 0, len);
@@ -61,7 +64,8 @@ unsafe extern "Rust" fn __getrandom_v03_custom(
6164

6265
// Many targets have very specific requirements for networking, so it's hard to
6366
// provide a generic implementation.
64-
// Depending on the target, you will probably need to implement `Transport` trait.
67+
// Depending on the target, you will probably need to implement `Transport`
68+
// trait.
6569
struct MyTransport;
6670

6771
impl Transport for MyTransport {
@@ -77,8 +81,8 @@ impl Transport for MyTransport {
7781
// As our target does not have `std` library, we need to provide custom
7882
// implementation of `GlobalAlloc` trait.
7983
//
80-
// In your program, you should use proper allocator that is supported by your target.
81-
// Here you have dummy implementation that does nothing.
84+
// In your program, you should use proper allocator that is supported by your
85+
// target. Here you have dummy implementation that does nothing.
8286
#[derive(Default)]
8387
pub struct Allocator;
8488

@@ -95,15 +99,15 @@ static GLOBAL_ALLOCATOR: Allocator = Allocator;
9599
// As our target does not have `std` library, we need to provide custom
96100
// implementation of `panic_handler`.
97101
//
98-
// In your program, you should use proper panic handler that is supported by your target.
99-
// Here you have dummy implementation that does nothing.
102+
// In your program, you should use proper panic handler that is supported by
103+
// your target. Here you have dummy implementation that does nothing.
100104
#[panic_handler]
101105
fn panicking(_: &PanicInfo) -> ! {
102106
loop {}
103107
}
104108

105-
// As we're using `no_main` attribute, we need to define `main` function manually.
106-
// For this example we're using `extern "C"` ABI to make it work.
109+
// As we're using `no_main` attribute, we need to define `main` function
110+
// manually. For this example we're using `extern "C"` ABI to make it work.
107111
#[no_mangle]
108112
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> usize {
109113
publish_example().map(|_| 0).unwrap()

examples/no_std/src/subscribe.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,23 @@ struct Message {
3737
fn custom_random(buf: &mut [u8]) -> Result<(), getrandom::Error> {
3838
// We're using `42` as a random number, because it's the answer
3939
// to the Ultimate Question of Life, the Universe, and Everything.
40-
// In your program, you should use proper random number generator that is supported by your target.
40+
// In your program, you should use proper random number generator that is
41+
// supported by your target.
4142
for i in buf.iter_mut() {
4243
*i = 42;
4344
}
4445

4546
Ok(())
4647
}
4748

48-
// This function is used to register the custom implementation of `getrandom` function.
49+
// This function is used to register the custom implementation of `getrandom`
50+
// function.
4951
#[no_mangle]
5052
unsafe extern "Rust" fn __getrandom_v03_custom(
5153
dest: *mut u8,
5254
len: usize,
5355
) -> Result<(), getrandom::Error> {
56+
#[allow(unsafe_code)]
5457
let buf = unsafe {
5558
// fill the buffer with zeros
5659
core::ptr::write_bytes(dest, 0, len);

src/core/utils/encoding.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::lib::alloc::{
2+
collections::HashSet,
23
string::{String, ToString},
34
vec::Vec,
45
};
@@ -76,10 +77,19 @@ pub fn join_url_encoded(strings: &[&str], sep: &str) -> Option<String> {
7677
/// Channels list used as part of URL path and therefore required.
7778
#[cfg(any(feature = "subscribe", feature = "presence"))]
7879
pub(crate) fn url_encoded_channels(channels: &[String]) -> String {
80+
let mut seen_channels = HashSet::new();
81+
7982
join_url_encoded(
8083
channels
8184
.iter()
82-
.map(|v| v.as_str())
85+
.filter_map(|channel| {
86+
let channel_string = channel.as_str();
87+
if seen_channels.insert(channel_string) {
88+
Some(channel_string)
89+
} else {
90+
None
91+
}
92+
})
8393
.collect::<Vec<&str>>()
8494
.as_slice(),
8595
",",
@@ -90,10 +100,19 @@ pub(crate) fn url_encoded_channels(channels: &[String]) -> String {
90100
/// URL-encode channel groups list.
91101
#[cfg(any(feature = "subscribe", feature = "presence"))]
92102
pub(crate) fn url_encoded_channel_groups(channel_groups: &[String]) -> Option<String> {
103+
let mut seen_groups = HashSet::new();
104+
93105
join_url_encoded(
94106
channel_groups
95107
.iter()
96-
.map(|v| v.as_str())
108+
.filter_map(|group| {
109+
let group_string = group.as_str();
110+
if seen_groups.insert(group_string) {
111+
Some(group_string)
112+
} else {
113+
None
114+
}
115+
})
97116
.collect::<Vec<&str>>()
98117
.as_slice(),
99118
",",

src/dx/presence/builders/here_now.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,24 @@ pub struct HereNowRequest<T, D> {
8181
default = "false"
8282
)]
8383
pub(in crate::dx::presence) include_state: bool,
84+
85+
/// Maximum number of occupants to return per channel.
86+
///
87+
/// > **Important**: Maximum and default value is `1000` users per request.
88+
#[builder(
89+
field(vis = "pub(in crate::dx::presence)"),
90+
setter(strip_option),
91+
default = "1000"
92+
)]
93+
pub(in crate::dx::presence) limit: usize,
94+
95+
/// Zero-based starting index for pagination.
96+
#[builder(
97+
field(vis = "pub(in crate::dx::presence)"),
98+
setter(strip_option),
99+
default
100+
)]
101+
pub(in crate::dx::presence) offset: Option<usize>,
84102
}
85103

86104
impl<T, D> HereNowRequestBuilder<T, D> {
@@ -127,6 +145,12 @@ impl<T, D> HereNowRequest<T, D> {
127145
query.insert("disable_uuids".into(), "1".into());
128146
});
129147

148+
query.insert("limit".into(), self.limit.to_string());
149+
150+
if let Some(offset) = self.offset {
151+
(offset > 0).then(|| query.insert("offset".into(), offset.to_string()));
152+
}
153+
130154
Ok(TransportRequest {
131155
path: format!(
132156
"/v2/presence/sub-key/{}/channel/{}",

0 commit comments

Comments
 (0)