Target - Rust ============= Quick Start ----------- .. sourcecode:: bash gugugu-rust \ --input=dir/containing/gugugu/definitions \ --output=dir/containing/rust/code \ --module-prefix=some::prefix \ --derives=Debug,PartialEq \ --with-codec \ --with-server \ --with-client \ ; Compatibility ------------- Gugugu generates Rust 2018 code and utilize ``Future``. The theoritical minimum version of ``rustc`` is 1.36. The example compiles with ``rustc`` 1.39. Module ------ Gugugu module is represented by Rust module, with module name lower-cased. Types ----- Primitives ~~~~~~~~~~ +-------------+---------------+ | Gugugu Type | Rust Type | +=============+===============+ | ``Unit`` | ``()`` | +-------------+---------------+ | ``Bool`` | ``bool`` | +-------------+---------------+ | ``Int32`` | ``i32`` | +-------------+---------------+ | ``Double`` | ``f64`` | +-------------+---------------+ | ``String`` | ``String`` | +-------------+---------------+ | ``Maybe A`` | ``Option`` | +-------------+---------------+ | ``List A`` | ``Vec`` | +-------------+---------------+ Record Type ~~~~~~~~~~~ Record type are represented with Rust struct with ``derive`` attribute specified in command line options. .. sourcecode:: haskell data Book = Book { id :: Int32 , name :: String } becomes .. sourcecode:: rust #[derive(Debug, PartialEq)] pub struct Book { pub id : i32, pub name: String, } Enum Type ~~~~~~~~~ Enum type are represented by Rust enumeration type with ``derive`` attribute specified in command line options. .. sourcecode:: haskell data Color = Red | Green | Blue becomes .. sourcecode:: rust #[derive(Debug, PartialEq)] pub enum Color { Red, Green, Blue, } Foreign Type ~~~~~~~~~~~~ .. sourcecode:: haskell data DateTime {-# FOREIGN rust chrono::NaiveDateTime #-} becomes .. sourcecode:: rust pub type DateTime = chrono::NaiveDateTime; Encoder and Decoder ------------------- All types in this section are located in module ``crate::gugugu::lang::rust::runtime::codec`` with default configuration. .. sourcecode:: rust pub trait Encoding { fn encode ( s: C::State , a: &Self , c: &C ) -> Result where C: EncoderImpl ; } pub trait Decoding: Sized { fn decode ( s: C::State , c: &C ) -> Result<(C::State, Self), C::Error> where C: DecoderImpl ; } All types generated by gugugu will have implementation for trait ``Encoding`` and ``Decoding``. The ``c`` is a value you have to provide to describe how to encode and decode a value. You also have to provide the ``EncoderImpl`` or ``DecoderImpl`` instance. EncoderImpl and DecoderImpl ~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two traits named ``EncoderImpl`` and ``DecoderImpl`` which you should provide implementation to do the encoding/decoding. They both have three associated types ``Repr``, ``Error`` and ``State``. - The ``Repr`` is the serialized type encoding to or decoding from. e.g. ``String`` or ``Vec``. - The ``Error`` is the type for possible errors occurred. - The ``State`` is the intermediate state used during encoding or decoding. You might find :gugugu-source:`examples/lang/rust/src/codec/json.rs` useful to write implementation for ``EncoderImpl``/``DecoderImpl``. Most trait functions do not use ``Repr`` except the following two. .. sourcecode:: rust pub trait EncoderImpl: ForeignEncodersImpl { type Repr; fn encode_value ( &self , a: &A ) -> Result where A: Encoding ; } pub trait DecoderImpl: ForeignDecodersImpl { type Repr; fn decode_value ( &self , r: &Self::Repr ) -> Result where A: Decoding ; } For an ``encode_value``, you usually should * Provide an initial state * Feed it to ``A::encode`` * Transform the state returned to serialized type, ``Repr`` For a ``decode_value``, you usually should * Transform the ``Repr`` into an initial state ``State`` * Feed it to ``A::decode`` * Make sure the returned state did not go wrong * Return the decoded value returned by ``A::decode`` Encode/Decode Record Type ~~~~~~~~~~~~~~~~~~~~~~~~~ .. sourcecode:: rust pub trait EncoderImpl: ForeignEncodersImpl { fn encode_record ( &self , s: Self::State , n_fields: usize , a: &A , k: fn(&Self, Self::State, &A) -> Result ) -> Result ; fn encode_record_field ( &self , s: Self::State , i: usize , name: &str , a: &A ) -> Result where A: Encoding ; } pub trait DecoderImpl: ForeignDecodersImpl { fn decode_record ( &self , s: Self::State , n_fields: usize , k: fn(&Self, Self::State) -> Result<(Self::State, A), Self::Error> ) -> Result<(Self::State, A), Self::Error> ; fn decode_record_field ( &self , s: Self::State , i: usize , name: &str ) -> Result<(Self::State, A), Self::Error> where A: Decoding ; } The generated encoder/decoder for record type consists of a call to ``encode_record``/``decode_record`` with the number of fields. And the provided callback will call the ``encode_record_field``/``decode_record_field`` several times with indices and names of the fields. Encode/Decode Enum Type ~~~~~~~~~~~~~~~~~~~~~~~ .. sourcecode:: rust pub trait EncoderImpl: ForeignEncodersImpl { fn encode_enum ( &self , s: Self::State , a: A , as_index: fn(A) -> i32 , as_name: fn(A) -> &'static str ) -> Result ; } pub trait DecoderImpl: ForeignDecodersImpl { fn decode_enum ( &self , s: Self::State , by_index: fn(i32) -> Option , by_name: fn(&str) -> Option ) -> Result<(Self::State, A), Self::Error> ; } The generated encoder/decoder for enum type consists of a call to ``encode_enum``/``decode_enum``. You should encode/decode the value with the name or the index. Encode/Decode Primitive and Foreign Types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. sourcecode:: rust pub trait ForeignEncodersImpl { type Error; type State; // foreign encoder members, e.g. // fn encode_date_time // ( self: &Self // , s: Self::State // , v: &chrono::NaiveDateTime // ) -> Result // ; } pub trait ForeignDecodersImpl { type Error; type State; // foreign decoder members, e.g. // fn decode_date_time // ( self: &Self // , s: Self::State // ) -> Result<(Self::State, chrono::NaiveDateTime), Self::Error> // ; } pub trait EncoderImpl: ForeignEncodersImpl { fn encode_maybe ( &self , s: Self::State , v: &Option ) -> Result where A: Encoding ; fn encode_list ( &self , s: Self::State , v: &Vec ) -> Result where A: Encoding ; fn encode_unit ( &self , s: Self::State , v: &() ) -> Result ; fn encode_bool ( &self , s: Self::State , v: &bool ) -> Result ; fn encode_int32 ( &self , s: Self::State , v: &i32 ) -> Result ; fn encode_double ( &self , s: Self::State , v: &f64 ) -> Result ; fn encode_string ( &self , s: Self::State , v: &String ) -> Result ; } pub trait DecoderImpl: ForeignDecodersImpl { fn decode_maybe ( &self , s: Self::State ) -> Result<(Self::State, Option), Self::Error> where A: Decoding ; fn decode_list ( &self , s: Self::State ) -> Result<(Self::State, Vec), Self::Error> where A: Decoding ; fn decode_unit ( &self , s: Self::State ) -> Result<(Self::State, ()), Self::Error> ; fn decode_bool ( &self , s: Self::State ) -> Result<(Self::State, bool), Self::Error> ; fn decode_int32 ( &self , s: Self::State ) -> Result<(Self::State, i32), Self::Error> ; fn decode_double ( &self , s: Self::State ) -> Result<(Self::State, f64), Self::Error> ; fn decode_string ( &self , s: Self::State ) -> Result<(Self::State, String), Self::Error> ; } The primitive types and foreign types will generate functions like above. And the encoder/decoder simply calls the function you provide. Client and Server ----------------- All types in this section are located in module ``crate::gugugu::lang::rust::runtime::transport`` with default configuration. .. sourcecode:: haskell module Hello where foo :: FooReq -> IO FooRes bar :: BarReq -> IO BarRes becomes .. sourcecode:: rust use std::future::Future; use std::sync::Arc; pub trait HelloModule { type FooFuture: Future> + Send; fn foo ( self: &Self , a: FooReq , i: I ) -> Self::FooFuture ; type BarFuture: Future> + Send; fn bar ( self: &Self , a: BarReq , i: I ) -> Self::BarFuture ; } pub fn ask_transport ( namespace: &[&str] , name: &str ) -> Option, &CH, Arc, Arc, RA, I) -> CH::OutputFuture> where A: HelloModule + Send + Sync + 'static , CA: DecoderImpl + Send + Sync + 'static , CB: EncoderImpl + Send + Sync + 'static , CH: ServerCodecHandler { // definition omitted } impl HelloModule for GuguguClient where T: ClientTransport , CA: EncoderImpl + Send + Sync + 'static , CB: DecoderImpl + Send + Sync + 'static { // definition omitted } A value with ``HelloModule`` can be used as the client when used in client code, or as the server implementation in server code. The ``CA`` is the encoder or decoder used by request, The ``CB`` is the encoder or decoder used by response. The ``I`` and ``O`` are metadata of request and response. Server Usage ~~~~~~~~~~~~ .. sourcecode:: rust pub trait ServerCodecHandler { type OutputFuture: Future>; fn run ( &self , ca: Arc , cb: Arc , k: impl FnOnce(A, I) -> R + Send + 'static , ra: RA , i: I ) -> Self::OutputFuture where R: Future> + Send , A: Decoding + Send + 'static , B: Encoding + Send + 'static , CA: DecoderImpl + Send + Sync + 'static , CB: EncoderImpl + Send + Sync + 'static ; } pub fn ask_transport ( namespace: &[&str] , name: &str ) -> Option, &CH, Arc, Arc, RA, I) -> CH::OutputFuture> where A: HelloModule + Send + Sync + 'static , CA: DecoderImpl + Send + Sync + 'static , CB: EncoderImpl + Send + Sync + 'static , CH: ServerCodecHandler { // definition omitted } ``ask_transport`` returns a function that can handle the request if found. The implementation for ``ServerCodecHandler`` should be straightforward, it should handle a request deserialized from ``RA`` with response serialized to ``RB``. Please consult :gugugu-source:`examples/lang/rust/src/bin/gugugu-example-rust-jsonhttp-server.rs` for how to use the it. Client Usage ~~~~~~~~~~~~ .. sourcecode:: rust pub trait ClientTransport { fn send ( &self , namespace: &[&str] , name: &str , a: A , i: I , ca: Arc , cb: Arc ) -> Pin> + Send>> where A: Encoding + Send + 'static , B: Decoding + Send + 'static , CA: EncoderImpl + Send + Sync + 'static , CB: DecoderImpl + Send + Sync + 'static ; } pub struct GuguguClient { pub transport: T, pub encoder_impl: Arc, pub decoder_impl: Arc, } impl HelloModule for GuguguClient where T: ClientTransport , CA: EncoderImpl + Send + Sync + 'static , CB: DecoderImpl + Send + Sync + 'static { // definition omitted } Like ``ServerCodecHandler``, ``ClientTransport`` can handle request serialized to ``RA`` with response deserialized from ``RB``. Please consult :gugugu-source:`examples/lang/rust/src/bin/gugugu-example-rust-jsonhttp-client.rs` for how to write a ``ClientTransport``. Command Line Options -------------------- .. sourcecode:: none Usage: gugugu-rust (-i|--input INPUT) (-o|--output OUTPUT) (-p|--module-prefix MODULE_PREFIX) [-r|--runtime-module RUNTIME_MODULE] [--derives DERIVES] [--with-codec] [--with-server] [--with-client] [--trans-module-code ARG] [--trans-module-value ARG] [--trans-module-type ARG] [--trans-func-code ARG] [--trans-func-value ARG] [--trans-type-code ARG] [--trans-type-func ARG] [--trans-field-code ARG] [--trans-field-value ARG] [--trans-enum-code ARG] [--trans-enum-value ARG] [--version] Available options: -i,--input INPUT the directory containing the definition files -o,--output OUTPUT the directory to put the generated sources -p,--module-prefix MODULE_PREFIX the package prefix, e.g. some::prefix -r,--runtime-module RUNTIME_MODULE location of gugugu runtime module (default: "gugugu::lang::rust::runtime") --derives DERIVES derive attribute for data type, use comma to separate multiples, e.g. Debug,PartialEq --with-codec pass this flag to generate codecs, default to false --with-server pass this flag to generate server, default to false, implies with-codec --with-client pass this flag to generate client, default to false, implies with-codec --trans-module-code ARG module name transformer for code (default: lower) --trans-module-value ARG module name transformer for value (default: snake) --trans-module-type ARG module name transformer for type of client/server (default: id) --trans-func-code ARG function name transformer for code (default: snake) --trans-func-value ARG function name transformer for value (default: snake) --trans-type-code ARG type name transformer for code (default: id) --trans-type-func ARG type name transformer in function (default: snake) --trans-field-code ARG record field name transformer for code (default: snake) --trans-field-value ARG record field name transformer for value (default: snake) --trans-enum-code ARG enum name transformer for code (default: id) --trans-enum-value ARG enum name transformer for value (default: upper-snake) -h,--help Show this help text --help-transformers list available name transformers --version show version