diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/jaegertracing/thrift/tutorial/rs | |
parent | Initial commit. (diff) | |
download | ceph-upstream/16.2.11+ds.tar.xz ceph-upstream/16.2.11+ds.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/jaegertracing/thrift/tutorial/rs')
-rw-r--r-- | src/jaegertracing/thrift/tutorial/rs/Cargo.toml | 16 | ||||
-rw-r--r-- | src/jaegertracing/thrift/tutorial/rs/Makefile.am | 52 | ||||
-rw-r--r-- | src/jaegertracing/thrift/tutorial/rs/README.md | 318 | ||||
-rw-r--r-- | src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_client.rs | 130 | ||||
-rw-r--r-- | src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_server.rs | 179 | ||||
-rw-r--r-- | src/jaegertracing/thrift/tutorial/rs/src/lib.rs | 23 |
6 files changed, 718 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/tutorial/rs/Cargo.toml b/src/jaegertracing/thrift/tutorial/rs/Cargo.toml new file mode 100644 index 000000000..60f02d73c --- /dev/null +++ b/src/jaegertracing/thrift/tutorial/rs/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "thrift-tutorial" +version = "0.1.0" +license = "Apache-2.0" +authors = ["Apache Thrift Developers <dev@thrift.apache.org>"] +exclude = ["Makefile*", "shared.rs", "tutorial.rs"] +publish = false + +[dependencies] +clap = "2.33" +ordered-float = "1.0" +try_from = "0.3" + +[dependencies.thrift] +path = "../../lib/rs" + diff --git a/src/jaegertracing/thrift/tutorial/rs/Makefile.am b/src/jaegertracing/thrift/tutorial/rs/Makefile.am new file mode 100644 index 000000000..666331e4a --- /dev/null +++ b/src/jaegertracing/thrift/tutorial/rs/Makefile.am @@ -0,0 +1,52 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-rs/tutorial.rs gen-rs/shared.rs: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) -out src --gen rs -r $< + +all-local: gen-rs/tutorial.rs + $(CARGO) build + [ -d bin ] || mkdir bin + cp target/debug/tutorial_server bin/tutorial_server + cp target/debug/tutorial_client bin/tutorial_client + +check: all + +tutorialserver: all + bin/tutorial_server + +tutorialclient: all + bin/tutorial_client + +clean-local: + $(CARGO) clean + -$(RM) Cargo.lock + -$(RM) src/shared.rs + -$(RM) src/tutorial.rs + -$(RM) -r bin + +EXTRA_DIST = \ + Cargo.toml \ + src/lib.rs \ + src/bin/tutorial_server.rs \ + src/bin/tutorial_client.rs \ + README.md + diff --git a/src/jaegertracing/thrift/tutorial/rs/README.md b/src/jaegertracing/thrift/tutorial/rs/README.md new file mode 100644 index 000000000..166854bea --- /dev/null +++ b/src/jaegertracing/thrift/tutorial/rs/README.md @@ -0,0 +1,318 @@ +# Rust Language Bindings for Thrift + +## Getting Started + +1. Get the [Thrift compiler](https://thrift.apache.org). + +2. Add the following crates to your `Cargo.toml`. + +```toml +thrift = "x.y.z" # x.y.z is the version of the thrift compiler +ordered-float = "0.3.0" +try_from = "0.2.0" +``` + +3. Add the same crates to your `lib.rs` or `main.rs`. + +```rust +extern crate ordered_float; +extern crate thrift; +extern crate try_from; +``` + +4. Generate Rust sources for your IDL (for example, `Tutorial.thrift`). + +```shell +thrift -out my_rust_program/src --gen rs -r Tutorial.thrift +``` + +5. Use the generated source in your code. + +```rust +// add extern crates here, or in your lib.rs +extern crate ordered_float; +extern crate thrift; +extern crate try_from; + +// generated Rust module +mod tutorial; + +use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol}; +use thrift::protocol::{TInputProtocol, TOutputProtocol}; +use thrift::transport::{TFramedReadTransport, TFramedWriteTransport}; +use thrift::transport::{TIoChannel, TTcpChannel}; + +use tutorial::{CalculatorSyncClient, TCalculatorSyncClient}; +use tutorial::{Operation, Work}; + +fn main() { + match run() { + Ok(()) => println!("client ran successfully"), + Err(e) => { + println!("client failed with {:?}", e); + std::process::exit(1); + } + } +} + +fn run() -> thrift::Result<()> { + // + // build client + // + + println!("connect to server on 127.0.0.1:9090"); + let mut c = TTcpChannel::new(); + c.open("127.0.0.1:9090")?; + + let (i_chan, o_chan) = c.split()?; + + let i_prot = TCompactInputProtocol::new( + TFramedReadTransport::new(i_chan) + ); + let o_prot = TCompactOutputProtocol::new( + TFramedWriteTransport::new(o_chan) + ); + + let mut client = CalculatorSyncClient::new(i_prot, o_prot); + + // + // alright! - let's make some calls + // + + // two-way, void return + client.ping()?; + + // two-way with some return + let res = client.calculate( + 72, + Work::new(7, 8, Operation::Multiply, None) + )?; + println!("multiplied 7 and 8, got {}", res); + + // two-way and returns a Thrift-defined exception + let res = client.calculate( + 77, + Work::new(2, 0, Operation::Divide, None) + ); + match res { + Ok(v) => panic!("shouldn't have succeeded with result {}", v), + Err(e) => println!("divide by zero failed with {:?}", e), + } + + // one-way + client.zip()?; + + // done! + Ok(()) +} +``` + +## Code Generation + +### Thrift Files and Generated Modules + +The Thrift code generator takes each Thrift file and generates a Rust module +with the same name snake-cased. For example, running the compiler on +`ThriftTest.thrift` creates `thrift_test.rs`. To use these generated files add +`mod ...` and `use ...` declarations to your `lib.rs` or `main.rs` - one for +each generated file. + +### Results and Errors + +The Thrift runtime library defines a `thrift::Result` and a `thrift::Error` type, +both of which are used throught the runtime library and in all generated code. +Conversions are defined from `std::io::Error`, `str` and `String` into +`thrift::Error`. + +### Thrift Type and their Rust Equivalents + +Thrift defines a number of types, each of which is translated into its Rust +equivalent by the code generator. + +* Primitives (bool, i8, i16, i32, i64, double, string, binary) +* Typedefs +* Enums +* Containers +* Structs +* Unions +* Exceptions +* Services +* Constants (primitives, containers, structs) + +In addition, unless otherwise noted, thrift includes are translated into +`use ...` statements in the generated code, and all declarations, parameters, +traits and types in the generated code are namespaced appropriately. + +The following subsections cover each type and their generated Rust equivalent. + +### Primitives + +Thrift primitives have straightforward Rust equivalents. + +* bool: `bool` +* i8: `i8` +* i16: `i16` +* i32: `i32` +* i64: `i64` +* double: `OrderedFloat<f64>` +* string: `String` +* binary: `Vec<u8>` + +### Typedefs + +A typedef is translated to a `pub type` declaration. + +```thrift +typedef i64 UserId + +typedef map<string, UserId> MapType +``` +```rust +pub type UserId = i64; + +pub type MapType = BTreeMap<String, Bonk>; +``` + +### Enums + +A Thrift enum is represented as a Rust enum, and each variant is transcribed 1:1. + +```thrift +enum Numberz +{ + ONE = 1, + TWO, + THREE, + FIVE = 5, + SIX, + EIGHT = 8 +} +``` + +```rust +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum Numberz { + ONE = 1, + TWO = 2, + THREE = 3, + FIVE = 5, + SIX = 6, + EIGHT = 8, +} + +impl TryFrom<i32> for Numberz { + // ... +} + +``` + +### Containers + +Thrift has three container types: list, set and map. They are translated into +Rust `Vec`, `BTreeSet` and `BTreeMap` respectively. Any Thrift type (this +includes structs, enums and typedefs) can be a list/set element or a map +key/value. + +#### List + +```thrift +list <i32> numbers +``` + +```rust +numbers: Vec<i32> +``` + +#### Set + +```thrift +set <i32> numbers +``` + +```rust +numbers: BTreeSet<i32> +``` + +#### Map + +```thrift +map <string, i32> numbers +``` + +```rust +numbers: BTreeMap<String, i32> +``` + +### Structs + +A Thrift struct is represented as a Rust struct, and each field transcribed 1:1. + +```thrift +struct CrazyNesting { + 1: string string_field, + 2: optional set<Insanity> set_field, + 3: required list< + map<set<i32>, map<i32,set<list<map<Insanity,string>>>>> + > + 4: binary binary_field +} +``` +```rust +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct CrazyNesting { + pub string_field: Option<String>, + pub set_field: Option<BTreeSet<Insanity>>, + pub list_field: Vec< + BTreeMap< + BTreeSet<i32>, + BTreeMap<i32, BTreeSet<Vec<BTreeMap<Insanity, String>>>> + > + >, + pub binary_field: Option<Vec<u8>>, +} + +impl CrazyNesting { + pub fn read_from_in_protocol(i_prot: &mut TInputProtocol) + -> + thrift::Result<CrazyNesting> { + // ... + } + pub fn write_to_out_protocol(&self, o_prot: &mut TOutputProtocol) + -> + thrift::Result<()> { + // ... + } +} + +``` +##### Optionality + +Thrift has 3 "optionality" types: + +1. Required +2. Optional +3. Default + +The Rust code generator encodes *Required* fields as the bare type itself, while +*Optional* and *Default* fields are encoded as `Option<TypeName>`. + +```thrift +struct Foo { + 1: required string bar // 1. required + 2: optional string baz // 2. optional + 3: string qux // 3. default +} +``` + +```rust +pub struct Foo { + bar: String, // 1. required + baz: Option<String>, // 2. optional + qux: Option<String>, // 3. default +} +``` + +## Known Issues + +* Struct constants are not supported +* Map, list and set constants require a const holder struct diff --git a/src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_client.rs b/src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_client.rs new file mode 100644 index 000000000..bfd81d4c3 --- /dev/null +++ b/src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_client.rs @@ -0,0 +1,130 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#[macro_use] +extern crate clap; + +extern crate thrift; +extern crate thrift_tutorial; + +use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol}; +use thrift::transport::{ + ReadHalf, TFramedReadTransport, TFramedWriteTransport, TIoChannel, TTcpChannel, WriteHalf, +}; + +use thrift_tutorial::shared::TSharedServiceSyncClient; +use thrift_tutorial::tutorial::{CalculatorSyncClient, Operation, TCalculatorSyncClient, Work}; + +fn main() { + match run() { + Ok(()) => println!("tutorial client ran successfully"), + Err(e) => { + println!("tutorial client failed with error {:?}", e); + std::process::exit(1); + } + } +} + +fn run() -> thrift::Result<()> { + let options = clap_app!(rust_tutorial_client => + (version: "0.1.0") + (author: "Apache Thrift Developers <dev@thrift.apache.org>") + (about: "Thrift Rust tutorial client") + (@arg host: --host +takes_value "host on which the tutorial server listens") + (@arg port: --port +takes_value "port on which the tutorial server listens") + ); + let matches = options.get_matches(); + + // get any passed-in args or the defaults + let host = matches.value_of("host").unwrap_or("127.0.0.1"); + let port = value_t!(matches, "port", u16).unwrap_or(9090); + + // build our client and connect to the host:port + let mut client = new_client(host, port)?; + + // alright! + // let's start making some calls + + // let's start with a ping; the server should respond + println!("ping!"); + client.ping()?; + + // simple add + println!("add"); + let res = client.add(1, 2)?; + println!("added 1, 2 and got {}", res); + + let logid = 32; + + // let's do...a multiply! + let res = client.calculate(logid, Work::new(7, 8, Operation::Multiply, None))?; + println!("multiplied 7 and 8 and got {}", res); + + // let's get the log for it + let res = client.get_struct(logid /* 32 */)?; + println!("got log {:?} for operation {}", res, logid); + + // ok - let's be bad :( + // do a divide by 0 + // logid doesn't matter; won't be recorded + let res = client.calculate(77, Work::new(2, 0, Operation::Divide, "we bad".to_owned())); + + // we should have gotten an exception back + match res { + Ok(v) => panic!("should not have succeeded with result {}", v), + Err(e) => println!("divide by zero failed with error {:?}", e), + } + + // let's do a one-way call + println!("zip"); + client.zip()?; + + // and then close out with a final ping + println!("ping!"); + client.ping()?; + + Ok(()) +} + +type ClientInputProtocol = TCompactInputProtocol<TFramedReadTransport<ReadHalf<TTcpChannel>>>; +type ClientOutputProtocol = TCompactOutputProtocol<TFramedWriteTransport<WriteHalf<TTcpChannel>>>; + +fn new_client( + host: &str, + port: u16, +) -> thrift::Result<CalculatorSyncClient<ClientInputProtocol, ClientOutputProtocol>> { + let mut c = TTcpChannel::new(); + + // open the underlying TCP stream + println!("connecting to tutorial server on {}:{}", host, port); + c.open(&format!("{}:{}", host, port))?; + + // clone the TCP channel into two halves, one which + // we'll use for reading, the other for writing + let (i_chan, o_chan) = c.split()?; + + // wrap the raw sockets (slow) with a buffered transport of some kind + let i_tran = TFramedReadTransport::new(i_chan); + let o_tran = TFramedWriteTransport::new(o_chan); + + // now create the protocol implementations + let i_prot = TCompactInputProtocol::new(i_tran); + let o_prot = TCompactOutputProtocol::new(o_tran); + + // we're done! + Ok(CalculatorSyncClient::new(i_prot, o_prot)) +} diff --git a/src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_server.rs b/src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_server.rs new file mode 100644 index 000000000..95b1a2b6e --- /dev/null +++ b/src/jaegertracing/thrift/tutorial/rs/src/bin/tutorial_server.rs @@ -0,0 +1,179 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#[macro_use] +extern crate clap; + +extern crate thrift; +extern crate thrift_tutorial; + +use std::collections::HashMap; +use std::convert::{From, Into}; +use std::default::Default; +use std::sync::Mutex; + +use thrift::protocol::{TCompactInputProtocolFactory, TCompactOutputProtocolFactory}; +use thrift::server::TServer; + +use thrift::transport::{TFramedReadTransportFactory, TFramedWriteTransportFactory}; +use thrift_tutorial::shared::{SharedServiceSyncHandler, SharedStruct}; +use thrift_tutorial::tutorial::{CalculatorSyncHandler, CalculatorSyncProcessor}; +use thrift_tutorial::tutorial::{InvalidOperation, Operation, Work}; + +fn main() { + match run() { + Ok(()) => println!("tutorial server ran successfully"), + Err(e) => { + println!("tutorial server failed with error {:?}", e); + std::process::exit(1); + } + } +} + +fn run() -> thrift::Result<()> { + let options = clap_app!(rust_tutorial_server => + (version: "0.1.0") + (author: "Apache Thrift Developers <dev@thrift.apache.org>") + (about: "Thrift Rust tutorial server") + (@arg port: --port +takes_value "port on which the tutorial server listens") + ); + let matches = options.get_matches(); + + let port = value_t!(matches, "port", u16).unwrap_or(9090); + let listen_address = format!("127.0.0.1:{}", port); + + println!("binding to {}", listen_address); + + let i_tran_fact = TFramedReadTransportFactory::new(); + let i_prot_fact = TCompactInputProtocolFactory::new(); + + let o_tran_fact = TFramedWriteTransportFactory::new(); + let o_prot_fact = TCompactOutputProtocolFactory::new(); + + // demux incoming messages + let processor = CalculatorSyncProcessor::new(CalculatorServer { + ..Default::default() + }); + + // create the server and start listening + let mut server = TServer::new( + i_tran_fact, + i_prot_fact, + o_tran_fact, + o_prot_fact, + processor, + 10, + ); + + server.listen(&listen_address) +} + +/// Handles incoming Calculator service calls. +struct CalculatorServer { + log: Mutex<HashMap<i32, SharedStruct>>, +} + +impl Default for CalculatorServer { + fn default() -> CalculatorServer { + CalculatorServer { + log: Mutex::new(HashMap::new()), + } + } +} + +// since Calculator extends SharedService we have to implement the +// handler for both traits. +// + +// SharedService handler +impl SharedServiceSyncHandler for CalculatorServer { + fn handle_get_struct(&self, key: i32) -> thrift::Result<SharedStruct> { + let log = self.log.lock().unwrap(); + log.get(&key) + .cloned() + .ok_or_else(|| format!("could not find log for key {}", key).into()) + } +} + +// Calculator handler +impl CalculatorSyncHandler for CalculatorServer { + fn handle_ping(&self) -> thrift::Result<()> { + println!("pong!"); + Ok(()) + } + + fn handle_add(&self, num1: i32, num2: i32) -> thrift::Result<i32> { + println!("handling add: n1:{} n2:{}", num1, num2); + Ok(num1 + num2) + } + + fn handle_calculate(&self, logid: i32, w: Work) -> thrift::Result<i32> { + println!("handling calculate: l:{}, w:{:?}", logid, w); + + let res = if let Some(ref op) = w.op { + if w.num1.is_none() || w.num2.is_none() { + Err(InvalidOperation { + what_op: Some(*op as i32), + why: Some("no operands specified".to_owned()), + }) + } else { + // so that I don't have to call unwrap() multiple times below + let num1 = w.num1.as_ref().expect("operands checked"); + let num2 = w.num2.as_ref().expect("operands checked"); + + match *op { + Operation::Add => Ok(num1 + num2), + Operation::Subtract => Ok(num1 - num2), + Operation::Multiply => Ok(num1 * num2), + Operation::Divide => { + if *num2 == 0 { + Err(InvalidOperation { + what_op: Some(*op as i32), + why: Some("divide by 0".to_owned()), + }) + } else { + Ok(num1 / num2) + } + } + } + } + } else { + Err(InvalidOperation::new( + None, + "no operation specified".to_owned(), + )) + }; + + // if the operation was successful log it + if let Ok(ref v) = res { + let mut log = self.log.lock().unwrap(); + log.insert(logid, SharedStruct::new(logid, format!("{}", v))); + } + + // the try! macro automatically maps errors + // but, since we aren't using that here we have to map errors manually + // + // exception structs defined in the IDL have an auto-generated + // impl of From::from + res.map_err(From::from) + } + + fn handle_zip(&self) -> thrift::Result<()> { + println!("handling zip"); + Ok(()) + } +} diff --git a/src/jaegertracing/thrift/tutorial/rs/src/lib.rs b/src/jaegertracing/thrift/tutorial/rs/src/lib.rs new file mode 100644 index 000000000..40007e5d3 --- /dev/null +++ b/src/jaegertracing/thrift/tutorial/rs/src/lib.rs @@ -0,0 +1,23 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +extern crate ordered_float; +extern crate thrift; +extern crate try_from; + +pub mod shared; +pub mod tutorial; |