THRIFT-2945 Add Rust support
Client: Rust
Patch: Allen George <allen.george@gmail.com>
This closes #1147
diff --git a/tutorial/rs/README.md b/tutorial/rs/README.md
new file mode 100644
index 0000000..4d0d7c8
--- /dev/null
+++ b/tutorial/rs/README.md
@@ -0,0 +1,330 @@
+# 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 std::cell::RefCell;
+use std::rc::Rc;
+use thrift::protocol::{TInputProtocol, TOutputProtocol};
+use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol};
+use thrift::transport::{TFramedTransport, TTcpTransport, TTransport};
+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 t = TTcpTransport::new();
+ let t = match t.open("127.0.0.1:9090") {
+ Ok(()) => t,
+ Err(e) => {
+ return Err(
+ format!("failed to connect with {:?}", e).into()
+ );
+ }
+ };
+
+ let t = Rc::new(RefCell::new(
+ Box::new(t) as Box<TTransport>
+ ));
+ let t = Rc::new(RefCell::new(
+ Box::new(TFramedTransport::new(t)) as Box<TTransport>
+ ));
+
+ let i_prot: Box<TInputProtocol> = Box::new(
+ TCompactInputProtocol::new(t.clone())
+ );
+ let o_prot: Box<TOutputProtocol> = Box::new(
+ TCompactOutputProtocol::new(t.clone())
+ );
+
+ let 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, Bonk> MapType
+```
+```rust
+pub type UserId = 164;
+
+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
\ No newline at end of file