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