Skip to content

Commit c3b921f

Browse files
authored
Event listener interface and presence changes (#183)
feat(listener): change the real-time event handling interface feat(presence-state): state maintained after set state is used `user_id` state for specified channels will be maintained by the SDK. State with subscribe calls has been improved. feat(api): adding first-class citizens to access subscription Adding `Channel`, `ChannelGroup`, `ChannelMetadata` and `UuidMetadata` entities to be first-class citizens to access APIs related to them. Currently, access is provided only for subscription APIs. feat(auto-retry): added ability to exclude endpoints from retry Added ability to configure request retry policies to exclude specific endpoints from retry.
1 parent 18690e4 commit c3b921f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+9216
-2461
lines changed

.pubnub.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
name: rust
2-
version: 0.4.1
2+
version: 0.5.0
33
schema: 1
44
scm: github.com/pubnub/rust
55
files: []
66
changelog:
7+
- date: 2024-01-25
8+
version: 0.5.0
9+
changes:
10+
- type: feature
11+
text: "Change the real-time event handling interface."
12+
- type: feature
13+
text: "`user_id` state for specified channels will be maintained by the SDK. State with subscribe calls has been improved."
14+
- type: feature
15+
text: "Adding `Channel`, `ChannelGroup`, `ChannelMetadata` and `UuidMetadata` entities to be first-class citizens to access APIs related to them. Currently, access is provided only for subscription APIs."
16+
- type: feature
17+
text: "Added ability to configure request retry policies to exclude specific endpoints from retry."
718
- date: 2023-11-03
819
version: 0.4.1
920
changes:

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pubnub"
3-
version = "0.4.1"
3+
version = "0.5.0"
44
edition = "2021"
55
license-file = "LICENSE"
66
authors = ["PubNub <[email protected]>"]
@@ -63,7 +63,7 @@ std = ["derive_builder/std", "log/std", "uuid/std", "base64/std", "spin/std", "s
6363
extra_platforms = ["spin/portable_atomic", "dep:portable-atomic"]
6464

6565
# [Internal features] (not intended for use outside of the library)
66-
contract_test = ["parse_token", "publish", "access", "crypto"]
66+
contract_test = ["parse_token", "publish", "access", "crypto", "std", "subscribe", "presence", "tokio"]
6767
full_no_std = ["serde", "reqwest", "crypto", "parse_token", "blocking", "publish", "access", "subscribe", "tokio", "presence"]
6868
full_no_std_platform_independent = ["serde", "crypto", "parse_token", "blocking", "publish", "access", "subscribe", "presence"]
6969
pubnub_only = ["crypto", "parse_token", "blocking", "publish", "access", "subscribe", "presence"]
@@ -106,7 +106,7 @@ getrandom = { version = "0.2", optional = true }
106106
# parse_token
107107
ciborium = { version = "0.2.1", default-features = false, optional = true }
108108

109-
# subscribe
109+
# subscribe, presence
110110
futures = { version = "0.3.28", default-features = false, optional = true }
111111
tokio = { version = "1", optional = true, features = ["rt-multi-thread", "macros", "time"] }
112112
async-channel = { version = "1.8", optional = true }
@@ -122,7 +122,7 @@ async-trait = "0.1"
122122
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
123123
wiremock = "0.5"
124124
env_logger = "0.10"
125-
cucumber = { version = "0.20.0", features = ["output-junit"] }
125+
cucumber = { version = "0.20.2", features = ["output-junit"] }
126126
reqwest = { version = "0.11", features = ["json"] }
127127
test-case = "3.0"
128128
hashbrown = { version = "0.14.0", features = ["serde"] }
@@ -165,7 +165,7 @@ required-features = ["default"]
165165

166166
[[example]]
167167
name = "subscribe"
168-
required-features = ["default", "subscribe"]
168+
required-features = ["default", "subscribe", "presence"]
169169

170170
[[example]]
171171
name = "subscribe_raw"

README.md

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ Add `pubnub` to your Rust project in the `Cargo.toml` file:
3636
```toml
3737
# default features
3838
[dependencies]
39-
pubnub = "0.4.1"
39+
pubnub = "0.5.0"
4040

4141
# all features
4242
[dependencies]
43-
pubnub = { version = "0.4.1", features = ["full"] }
43+
pubnub = { version = "0.5.0", features = ["full"] }
4444
```
4545

4646
### Example
@@ -57,53 +57,53 @@ use serde_json;
5757

5858
#[tokio::main]
5959
async fn main() -> Result<(), Box<dyn std::error::Error>> {
60-
let publish_key = "my_publish_key";
60+
use pubnub::subscribe::{EventEmitter, SubscriptionParams};
61+
let publish_key = "my_publish_key";
6162
let subscribe_key = "my_subscribe_key";
6263
let client = PubNubClientBuilder::with_reqwest_transport()
63-
.with_keyset(Keyset {
64-
subscribe_key,
65-
publish_key: Some(publish_key),
66-
secret_key: None,
67-
})
68-
.with_user_id("user_id")
69-
.build()?;
70-
println!("PubNub instance created");
71-
72-
let subscription = client
73-
.subscribe()
74-
.channels(["my_channel".into()].to_vec())
75-
.execute()?;
76-
77-
println!("Subscribed to channel");
78-
79-
// Launch a new task to print out each received message
80-
tokio::spawn(subscription.stream().for_each(|event| async move {
81-
match event {
82-
SubscribeStreamEvent::Update(update) => {
83-
match update {
84-
Update::Message(message) | Update::Signal(message) => {
85-
// Silently log if UTF-8 conversion fails
86-
if let Ok(utf8_message) = String::from_utf8(message.data.clone()) {
87-
if let Ok(cleaned) = serde_json::from_str::<String>(&utf8_message) {
88-
println!("message: {}", cleaned);
89-
}
90-
}
91-
}
92-
Update::Presence(presence) => {
93-
println!("presence: {:?}", presence)
94-
}
95-
Update::Object(object) => {
96-
println!("object: {:?}", object)
97-
}
98-
Update::MessageAction(action) => {
99-
println!("message action: {:?}", action)
100-
}
101-
Update::File(file) => {
102-
println!("file: {:?}", file)
64+
.with_keyset(Keyset {
65+
subscribe_key,
66+
publish_key: Some(publish_key),
67+
secret_key: None,
68+
})
69+
.with_user_id("user_id")
70+
.build()?;
71+
println!("PubNub instance created");
72+
73+
let subscription = client.subscription(SubscriptionParams {
74+
channels: Some(&["my_channel"]),
75+
channel_groups: None,
76+
options: None
77+
});
78+
79+
println!("Subscribed to channel");
80+
81+
// Launch a new task to print out each received message
82+
tokio::spawn(client.status_stream().for_each(|status| async move {
83+
println!("\nStatus: {:?}", status)
84+
}));
85+
tokio::spawn(subscription.stream().for_each(|event| async move {
86+
match event {
87+
Update::Message(message) | Update::Signal(message) => {
88+
// Silently log if UTF-8 conversion fails
89+
if let Ok(utf8_message) = String::from_utf8(message.data.clone()) {
90+
if let Ok(cleaned) = serde_json::from_str::<String>(&utf8_message) {
91+
println!("message: {}", cleaned);
10392
}
10493
}
10594
}
106-
SubscribeStreamEvent::Status(status) => println!("\nstatus: {:?}", status),
95+
Update::Presence(presence) => {
96+
println!("presence: {:?}", presence)
97+
}
98+
Update::AppContext(object) => {
99+
println!("object: {:?}", object)
100+
}
101+
Update::MessageAction(action) => {
102+
println!("message action: {:?}", action)
103+
}
104+
Update::File(file) => {
105+
println!("file: {:?}", file)
106+
}
107107
}
108108
}));
109109

@@ -132,11 +132,11 @@ disable them in the `Cargo.toml` file, like so:
132132
```toml
133133
# only blocking and access + default features
134134
[dependencies]
135-
pubnub = { version = "0.4.1", features = ["blocking", "access"] }
135+
pubnub = { version = "0.5.0", features = ["blocking", "access"] }
136136

137137
# only parse_token + default features
138138
[dependencies]
139-
pubnub = { version = "0.4.1", features = ["parse_token"] }
139+
pubnub = { version = "0.5.0", features = ["parse_token"] }
140140
```
141141

142142
### Available features
@@ -175,7 +175,7 @@ you need, for example:
175175

176176
```toml
177177
[dependencies]
178-
pubnub = { version = "0.4.1", default-features = false, features = ["serde", "publish",
178+
pubnub = { version = "0.5.0", default-features = false, features = ["serde", "publish",
179179
"blocking"] }
180180
```
181181

examples/no_std/src/subscribe.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ getrandom::register_custom_getrandom!(custom_random);
3838
fn custom_random(buf: &mut [u8]) -> Result<(), getrandom::Error> {
3939
// We're using `42` as a random number, because it's the answer
4040
// to the Ultimate Question of Life, the Universe, and Everything.
41-
// In your program, you should use proper random number generator that is supported by your target.
41+
// In your program, you should use proper random number generator that is
42+
// supported by your target.
4243
for i in buf.iter_mut() {
4344
*i = 42;
4445
}
@@ -48,7 +49,8 @@ fn custom_random(buf: &mut [u8]) -> Result<(), getrandom::Error> {
4849

4950
// Many targets have very specific requirements for networking, so it's hard to
5051
// provide a generic implementation.
51-
// Depending on the target, you will probably need to implement `Transport` trait.
52+
// Depending on the target, you will probably need to implement `Transport`
53+
// trait.
5254
struct MyTransport;
5355

5456
impl Transport for MyTransport {
@@ -64,8 +66,8 @@ impl Transport for MyTransport {
6466
// As our target does not have `std` library, we need to provide custom
6567
// implementation of `GlobalAlloc` trait.
6668
//
67-
// In your program, you should use proper allocator that is supported by your target.
68-
// Here you have dummy implementation that does nothing.
69+
// In your program, you should use proper allocator that is supported by your
70+
// target. Here you have dummy implementation that does nothing.
6971
#[derive(Default)]
7072
pub struct Allocator;
7173

@@ -82,23 +84,23 @@ static GLOBAL_ALLOCATOR: Allocator = Allocator;
8284
// As our target does not have `std` library, we need to provide custom
8385
// implementation of `panic_handler`.
8486
//
85-
// In your program, you should use proper panic handler that is supported by your target.
86-
// Here you have dummy implementation that does nothing.
87+
// In your program, you should use proper panic handler that is supported by
88+
// your target. Here you have dummy implementation that does nothing.
8789
#[panic_handler]
8890
fn panicking(_: &PanicInfo) -> ! {
8991
loop {}
9092
}
9193

92-
// As we're using `no_main` attribute, we need to define `main` function manually.
93-
// For this example we're using `extern "C"` ABI to make it work.
94+
// As we're using `no_main` attribute, we need to define `main` function
95+
// manually. For this example we're using `extern "C"` ABI to make it work.
9496
#[no_mangle]
9597
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> usize {
9698
publish_example().map(|_| 0).unwrap()
9799
}
98100

99-
// In standard subscribe examples we use `println` macro to print the result of the operation
100-
// and it shows the idea of the example. `no_std` does not support `println` macro,
101-
// so we're using `do_a_thing` function instead.
101+
// In standard subscribe examples we use `println` macro to print the result of
102+
// the operation and it shows the idea of the example. `no_std` does not support
103+
// `println` macro, so we're using `do_a_thing` function instead.
102104
fn do_a_thing<T>(_: T) {}
103105

104106
// As `no_std` does not support `Error` trait, we use `PubNubError` instead.
@@ -133,7 +135,7 @@ fn publish_example() -> Result<(), PubNubError> {
133135
match update? {
134136
Update::Message(message) | Update::Signal(message) => do_a_thing(message),
135137
Update::Presence(presence) => do_a_thing(presence),
136-
Update::Object(object) => do_a_thing(object),
138+
Update::AppContext(object) => do_a_thing(object),
137139
Update::MessageAction(action) => do_a_thing(action),
138140
Update::File(file) => do_a_thing(file),
139141
};

examples/presence_state.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1+
use std::collections::HashMap;
2+
13
use pubnub::{Keyset, PubNubClientBuilder};
2-
use serde::Serialize;
3-
use std::env;
44

5-
#[derive(Debug, Serialize)]
5+
#[derive(Debug, serde::Serialize)]
66
struct State {
77
is_doing: String,
8+
flag: bool,
9+
}
10+
#[derive(Debug, serde::Serialize)]
11+
struct State2 {
12+
is_doing: String,
13+
business: String,
814
}
915

1016
#[tokio::main]
1117
async fn main() -> Result<(), Box<dyn snafu::Error>> {
12-
let publish_key = env::var("SDK_PUB_KEY")?;
13-
let subscribe_key = env::var("SDK_SUB_KEY")?;
18+
// let publish_key = env::var("SDK_PUB_KEY")?;
19+
// let subscribe_key = env::var("SDK_SUB_KEY")?;
20+
let publish_key = "demo";
21+
let subscribe_key = "demo";
1422

1523
let client = PubNubClientBuilder::with_reqwest_transport()
1624
.with_keyset(Keyset {
@@ -23,9 +31,32 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
2331

2432
println!("running!");
2533

34+
client
35+
.set_presence_state_with_heartbeat(HashMap::from([
36+
(
37+
"my_channel".to_string(),
38+
State {
39+
is_doing: "Something".to_string(),
40+
flag: true,
41+
},
42+
),
43+
(
44+
"other_channel".to_string(),
45+
State {
46+
is_doing: "Oh no".to_string(),
47+
flag: false,
48+
},
49+
),
50+
]))
51+
.channels(["my_channel".into(), "other_channel".into()].to_vec())
52+
.user_id("user_id")
53+
.execute()
54+
.await?;
55+
2656
client
2757
.set_presence_state(State {
2858
is_doing: "Nothing... Just hanging around...".into(),
59+
flag: false,
2960
})
3061
.channels(["my_channel".into(), "other_channel".into()].to_vec())
3162
.user_id("user_id")

0 commit comments

Comments
 (0)