Skip to content

Commit da05e2c

Browse files
committed
feat: add Rust and Go hello examples
Signed-off-by: Roman Volosatovs <[email protected]>
1 parent be26b60 commit da05e2c

File tree

19 files changed

+464
-1
lines changed

19 files changed

+464
-1
lines changed

Cargo.lock

Lines changed: 90 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ license = "Apache-2.0 WITH LLVM-exception"
1717
repository = "https://github.com/rvolosatovs/wrpc"
1818

1919
[workspace]
20-
members = ["crates/*"]
20+
members = ["crates/*", "examples/rust/*"]
2121

2222
[features]
2323
default = ["nats", "wasmtime"]
@@ -86,6 +86,7 @@ tokio-stream = { version = "0.1", default-features = false }
8686
tower = { version = "0.4", default-features = false }
8787
tracing = { version = "0.1", default-features = false }
8888
tracing-subscriber = { version = "0.3", default-features = false }
89+
url = { version = "2", default-features = false }
8990
wasmtime = { version = "19", default-features = false }
9091
wasmtime-wasi = { version = "19", default-features = false }
9192
wasmtime-wasi-http = { version = "19", default-features = false }

examples/go/hello-nats-server/go.mod

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module github.com/wrpc/wrpc/examples/go/hello-nats-server
2+
3+
go 1.22.1
4+
5+
require github.com/nats-io/nats.go v1.34.1
6+
7+
require github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834
8+
9+
require (
10+
github.com/klauspost/compress v1.17.2 // indirect
11+
github.com/nats-io/nkeys v0.4.7 // indirect
12+
github.com/nats-io/nuid v1.0.1 // indirect
13+
// indirect
14+
golang.org/x/crypto v0.18.0 // indirect
15+
golang.org/x/sys v0.16.0 // indirect
16+
)

examples/go/hello-nats-server/go.sum

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
4+
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
5+
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
6+
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
7+
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
8+
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
9+
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
10+
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
11+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
12+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
13+
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
14+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
15+
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q=
16+
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk=
17+
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
18+
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
19+
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
20+
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
21+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
22+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

examples/go/hello-nats-server/main.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"math"
7+
"os"
8+
9+
"github.com/nats-io/nats.go"
10+
"github.com/tetratelabs/wabin/leb128"
11+
)
12+
13+
func encode_string(s string) ([]byte, error) {
14+
n := len(s)
15+
if n > math.MaxUint32 {
16+
return nil, fmt.Errorf("response UTF-8 string byte length of %d overflows u32", n)
17+
}
18+
return append(leb128.EncodeUint32(uint32(n)), s...), nil
19+
}
20+
21+
func handle_hello(nc *nats.Conn, results string) (err error) {
22+
b, err := encode_string("hello from Go")
23+
if err != nil {
24+
return fmt.Errorf("failed to encode `hello`: %w", err)
25+
}
26+
n := len(b)
27+
maxPayload := nc.MaxPayload()
28+
if int64(n) > maxPayload {
29+
return fmt.Errorf("response length of %d exceeds NATS.io max payload of %d", n, maxPayload)
30+
}
31+
if err := nc.Publish(results, b); err != nil {
32+
return fmt.Errorf("failed to publish response on result subject `%s`: %w", results, err)
33+
}
34+
return nil
35+
}
36+
37+
func run() error {
38+
// Connect to a server
39+
nc, err := nats.Connect(nats.DefaultURL)
40+
if err != nil {
41+
return fmt.Errorf("failed to connect to NATS.io: %w", err)
42+
}
43+
defer func() {
44+
if err := nc.Drain(); err != nil {
45+
log.Printf("failed to drain NATS.io connection: %s", err)
46+
}
47+
}()
48+
defer nc.Close()
49+
50+
hello := make(chan *nats.Msg)
51+
helloSub, err := nc.ChanSubscribe("go.wrpc.0.0.1.wrpc-examples:hello/handler.hello", hello)
52+
if err != nil {
53+
return fmt.Errorf("failed to subscribe to `hello` invocations: %w", err)
54+
}
55+
defer func() {
56+
if err := helloSub.Unsubscribe(); err != nil {
57+
log.Printf("failed to unsubscribe from `hello`: %s", err)
58+
return
59+
}
60+
close(hello)
61+
}()
62+
for msg := range hello {
63+
if err := msg.Respond([]byte{}); err != nil {
64+
log.Printf("failed to complete handshake on `%s` subject", msg.Reply)
65+
continue
66+
}
67+
68+
if err := handle_hello(nc, fmt.Sprintf("%s.results", msg.Reply)); err != nil {
69+
log.Printf("failed to handle `hello`: %s", err)
70+
b, err := encode_string(fmt.Sprintf("%s", err))
71+
if err != nil {
72+
log.Printf("failed to encode `hello` handling error: %s", err)
73+
// Encoding the error failed, let's try encoding the encoding error, shall we?
74+
b, err = encode_string(fmt.Sprintf("failed to encode error: %s", err))
75+
if err != nil {
76+
log.Printf("failed to encode `hello` handling error encoding error: %s", err)
77+
// Well, we're out of luck at this point, let's just send an empty string
78+
b = []byte{0}
79+
}
80+
}
81+
if len(b) > int(nc.MaxPayload()) {
82+
// TODO: split the payload into multiple chunks
83+
b = []byte{0}
84+
}
85+
if err = nc.Publish(fmt.Sprintf("%s.error", msg.Reply), b); err != nil {
86+
log.Printf("failed to send error to client: %s", err)
87+
}
88+
}
89+
}
90+
return nil
91+
}
92+
93+
func main() {
94+
log.SetFlags(0)
95+
log.SetOutput(os.Stderr)
96+
97+
if err := run(); err != nil {
98+
log.Fatal(err)
99+
}
100+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "hello-nats-client"
3+
version = "0.1.0"
4+
5+
authors.workspace = true
6+
categories.workspace = true
7+
edition.workspace = true
8+
license.workspace = true
9+
repository.workspace = true
10+
11+
[dependencies]
12+
anyhow = { workspace = true }
13+
async-nats = { workspace = true }
14+
clap = { workspace = true, features = [
15+
"color",
16+
"derive",
17+
"error-context",
18+
"help",
19+
"std",
20+
"suggestions",
21+
"usage",
22+
] }
23+
tokio = { workspace = true, features = ["rt-multi-thread"] }
24+
tracing-subscriber = { workspace = true, features = ["ansi", "fmt"] }
25+
url = { workspace = true }
26+
wit-bindgen-wrpc = { workspace = true }
27+
wrpc-transport-nats = { workspace = true }
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use anyhow::Context as _;
2+
use clap::Parser;
3+
use tokio::sync::mpsc;
4+
use url::Url;
5+
6+
mod bindings {
7+
wit_bindgen_wrpc::generate!();
8+
}
9+
10+
#[derive(Parser, Debug)]
11+
#[command(author, version, about, long_about = None)]
12+
struct Args {
13+
/// NATS.io URL to connect to
14+
#[arg(short, long, default_value = "nats://127.0.0.1:4222")]
15+
nats: Url,
16+
17+
/// Prefixes to invoke `hello` on
18+
prefixes: Vec<String>,
19+
}
20+
21+
#[tokio::main]
22+
async fn main() -> anyhow::Result<()> {
23+
tracing_subscriber::fmt().init();
24+
25+
let Args { nats, prefixes } = Args::parse();
26+
27+
let nats = connect(nats)
28+
.await
29+
.context("failed to connect to NATS.io")?;
30+
for prefix in prefixes {
31+
let wrpc = wrpc_transport_nats::Client::new(nats.clone(), prefix.clone());
32+
let hello = bindings::wrpc_examples::hello::handler::hello(&wrpc)
33+
.await
34+
.context("failed to invoke `wrpc-examples.hello/handler.hello`")?;
35+
eprintln!("{prefix}: {hello}")
36+
}
37+
Ok(())
38+
}
39+
40+
/// Connect to NATS.io server and ensure that the connection is fully established before
41+
/// returning the resulting [`async_nats::Client`]
42+
async fn connect(url: Url) -> anyhow::Result<async_nats::Client> {
43+
let (conn_tx, mut conn_rx) = mpsc::channel(1);
44+
let client = async_nats::connect_with_options(
45+
String::from(url),
46+
async_nats::ConnectOptions::new()
47+
.retry_on_initial_connect()
48+
.event_callback(move |event| {
49+
let conn_tx = conn_tx.clone();
50+
async move {
51+
if let async_nats::Event::Connected = event {
52+
conn_tx
53+
.send(())
54+
.await
55+
.expect("failed to send NATS.io server connection notification");
56+
}
57+
}
58+
}),
59+
)
60+
.await
61+
.context("failed to connect to NATS.io server")?;
62+
conn_rx
63+
.recv()
64+
.await
65+
.context("failed to await NATS.io server connection to be established")?;
66+
Ok(client)
67+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[hello]
2+
path = "../../../wit/hello"
3+
sha256 = "3680bb734f3fa9f7325674142a2a9b558efd34ea2cb2df7ccb651ad869078d27"
4+
sha512 = "688fdae594dc43bd65bd15ea66b77a8f97cb4bc1c3629719e91d6c1391c66f7c8c6517d096f686cca996188f64f075c4ccb0d70a40097ce76b8b4bcc71dc7506"

0 commit comments

Comments
 (0)