Skip to content

Commit a0ae2ac

Browse files
authored
Merge pull request #41 from hseeberger/docs/readme
docs: tighten README
2 parents e63b957 + aef6f2e commit a0ae2ac

3 files changed

Lines changed: 84 additions & 61 deletions

File tree

.envrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
source_up_if_exists
2+
13
if [[ -f "$PWD/.envrc.local" ]]; then
24
echo direnv: using .envrc.local
35
source "$PWD/.envrc.local"

README.md

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@
1414
[docs-badge]: https://img.shields.io/docsrs/pub-sub-client/latest
1515
[docs-url]: https://docs.rs/pub-sub-client/latest/pub_sub_client/
1616

17-
Google Cloud Pub/Sub client library in [Rust](https://www.rust-lang.org/). Currently publishing, pulling and acknowledging are supported, as well as creating topics and subscriptions; other management tasks (like deleting or listing) are not yet supported.
17+
A [Google Cloud Pub/Sub](https://cloud.google.com/pubsub) client library for
18+
[Rust](https://www.rust-lang.org/). It supports publishing, pulling and
19+
acknowledging messages, as well as creating topics and subscriptions; other
20+
management tasks (like deleting or listing) are not yet supported.
1821

19-
Messages can either be published/pulled as raw or, if the payload is JSON data, serialized from/deserialized into domain messages (structs or enums) via [Serde](https://serde.rs/) and [Serde JSON](https://docs.rs/serde_json). Both raw `RawPulledMessageEnvelope`s and "typed" `PulledMessage`s expose metadata like message ID, acknowledge ID, attributes, etc.
20-
21-
Aside from straightforward deserialization it is also possible to first transform the pulled JSON values before deserializing into domain messages which allows for generally adjusting the JSON structure as well as schema evolution.
22+
Messages can be published and pulled as raw payloads or – when the payload is
23+
JSON – serialized from and deserialized into domain messages (structs or enums)
24+
via [Serde](https://serde.rs/). When pulling, the raw JSON can optionally be
25+
transformed before deserialization, which is handy for schema evolution.
2226

2327
## Features
2428

@@ -36,78 +40,95 @@ cargo add pub-sub-client
3640

3741
## Usage
3842

39-
Typically we want to use domain message:
43+
All operations are provided by `PubSubClient`. The example below publishes a few
44+
typed messages and then pulls and acknowledges them:
4045

4146
``` rust
47+
use pub_sub_client::{Error, PubSubClient};
48+
use serde::{Deserialize, Serialize};
49+
use std::time::Duration;
50+
51+
const TOPIC_ID: &str = "test";
52+
const SUBSCRIPTION_ID: &str = "test";
53+
4254
#[derive(Debug, Serialize, Deserialize)]
4355
struct Message {
4456
text: String,
4557
}
46-
```
47-
48-
To publish `Message` we need to derive `Serialize` and to pull it we need to derive `Deserialize`.
49-
50-
First create a `PubSubClient`, giving the path to a service account key file and the duration to refresh access tokens before they expire:
51-
52-
``` rust
53-
let pub_sub_client = PubSubClient::new(
54-
"secrets/cryptic-hawk-336616-e228f9680cbc.json",
55-
Duration::from_secs(30),
56-
)?;
57-
```
5858

59-
Things could go wrong, e.g. if the service account key file does not exist or is malformed, hence a `Result` is returned.
60-
61-
Then we call `publish` to publish some `messages` using the given `TOPIC_ID` and – if all is good – get back the message IDs; we do not use an ordering key nor a request timeout here and below for simplicity:
59+
async fn run() -> Result<(), Error> {
60+
// Create a client from a service account key file, refreshing access tokens
61+
// 30s before they expire. Fails if the key file is missing or malformed.
62+
let pub_sub_client = PubSubClient::new(
63+
"secrets/service-account.json",
64+
Duration::from_secs(30),
65+
)?;
66+
67+
// Publish some messages, getting back their IDs.
68+
let messages = ["Hello", "from pub-sub-client"]
69+
.into_iter()
70+
.map(|text| Message { text: text.to_string() })
71+
.collect::<Vec<_>>();
72+
let message_ids = pub_sub_client
73+
.publish(TOPIC_ID, messages, None, None)
74+
.await?;
75+
println!("published messages with IDs: {}", message_ids.join(", "));
6276

63-
``` rust
64-
let messages = vec!["Hello", "from pub-sub-client"]
65-
.iter()
66-
.map(|s| s.to_string())
67-
.map(|text| Message { text })
68-
.collect::<Vec<_>>();
69-
let message_ids = pub_sub_client
70-
.publish(TOPIC_ID, messages, None, None)
71-
.await?;
72-
let message_ids = message_ids.join(", ");
73-
println!("Published messages with IDs: {message_ids}");
74-
```
77+
// Pull at most 42 messages, deserializing each into a `Message`.
78+
let pulled_messages = pub_sub_client
79+
.pull::<Message>(SUBSCRIPTION_ID, 42, None)
80+
.await?;
7581

76-
Next we call `pull` to get at most the given `42` messages from the given `SUBSCRIPTION_ID`:
82+
for pulled_message in pulled_messages {
83+
match pulled_message.message {
84+
Ok(m) => println!("pulled message with text \"{}\"", m.text),
85+
Err(e) => eprintln!("ERROR: {e}"),
86+
}
87+
pub_sub_client
88+
.acknowledge(SUBSCRIPTION_ID, vec![&pulled_message.ack_id], None)
89+
.await?;
90+
println!("acknowledged message with ID {}", pulled_message.id);
91+
}
7792

78-
``` rust
79-
let pulled_messages = pub_sub_client
80-
.pull::<Message>(SUBSCRIPTION_ID, 42, None)
81-
.await?;
93+
Ok(())
94+
}
8295
```
8396

84-
Of course pulling which happens via HTTP could fail, hence we get back another `Result`.
97+
Notes:
8598

86-
Finally we handle the pulled messages; for simplicity we only deal with the happy path here, i.e. when the deserialization was successful:
99+
- `PubSubClient::new` takes the path to a service account key file and a token
100+
refresh buffer, and returns a `Result` (it fails if the key is missing or
101+
malformed).
102+
- `publish` and `pull` accept an optional ordering key and per-request timeout
103+
(both `None` above for brevity).
104+
- Each `PulledMessage.message` is itself a `Result`, since decoding or
105+
deserializing an individual message may fail; acknowledge it via its
106+
`ack_id`.
87107

88-
``` rust
89-
for pulled_message in pulled_messages {
90-
match pulled_message.message {
91-
Ok(m) => println!("Pulled message with text \"{}\"", m.text),
92-
Err(e) => eprintln!("ERROR: {e}"),
93-
}
108+
### Raw messages and transformations
94109

95-
pub_sub_client
96-
.acknowledge(SUBSCRIPTION_ID, vec![&pulled_message.ack_id], None)
97-
.await?;
98-
println!("Acknowledged message with ID {}", pulled_message.id);
99-
}
100-
```
110+
Messages can also be published and pulled raw via `publish_raw` and `pull_raw`,
111+
giving direct access to the Base64 encoded payload and all metadata. When
112+
pulling, `pull_with_transform` lets you adjust the raw JSON value before it is
113+
deserialized into a domain message, which is useful for schema evolution. See
114+
[`examples/transform.rs`](examples/transform.rs).
101115

102-
For successfully deserialized messages we call `acknowledge` with the acknowledge ID taken from the envelope.
116+
### Examples and local testing
103117

104-
### Raw messages and transformations
118+
The [`examples`](examples) directory contains complete programs:
119+
[`simple`](examples/simple.rs) for typed publishing and pulling and
120+
[`transform`](examples/transform.rs) for transformations. They read the service
121+
account key path from the `SERVICE_ACCOUNT_PATH` environment variable:
105122

106-
The walkthrough above uses typed domain messages, but messages can also be published and pulled raw via `publish_raw` and `pull_raw`, giving direct access to the Base64 encoded payload and all metadata. When pulling, `pull_with_transform` lets you adjust the raw JSON value before it is deserialized into a domain message, which is handy for schema evolution.
123+
``` shell
124+
SERVICE_ACCOUNT_PATH=secrets/service-account.json cargo run --example simple
125+
```
107126

108-
See the [`examples`](examples) directory for complete programs: [`simple`](examples/simple.rs) for typed publishing and pulling and [`transform`](examples/transform.rs) for transformations.
127+
By default the client talks to `https://pubsub.googleapis.com`. Set the
128+
`PUB_SUB_BASE_URL` environment variable to point it at a different endpoint,
129+
such as a local [Pub/Sub emulator](https://cloud.google.com/pubsub/docs/emulator).
109130

110-
## Contribution policy ##
131+
## Contribution policy
111132

112133
Contributions via GitHub pull requests are gladly accepted from their original author. Along with
113134
any pull requests, please state that the contribution is your original work and that you license the
@@ -116,8 +137,7 @@ explicitly, by submitting any copyrighted material via pull request, email, or o
116137
to license the material under the project's open source license and warrant that you have the legal
117138
authority to do so.
118139

119-
## License ##
140+
## License
120141

121142
This code is open source software licensed under the
122143
[Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0.html).
123-

examples/simple.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ async fn run() -> Result<(), Error> {
3939
)?;
4040

4141
let messages = ["Hello", "from pub-sub-client"]
42-
.iter()
43-
.map(|s| s.to_string())
44-
.map(|text| Message { text })
42+
.into_iter()
43+
.map(|text| Message {
44+
text: text.to_string(),
45+
})
4546
.collect::<Vec<_>>();
4647
let message_ids = pub_sub_client
4748
.publish(TOPIC_ID, messages, None, None)

0 commit comments

Comments
 (0)