Target - Rust¶
Quick Start¶
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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Record Type¶
Record type are represented with Rust struct
with derive attribute specified in command line options.
data Book
= Book
{ id :: Int32
, name :: String
}
becomes
#[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.
data Color
= Red
| Green
| Blue
becomes
#[derive(Debug, PartialEq)]
pub enum Color {
Red,
Green,
Blue,
}
Foreign Type¶
data DateTime
{-# FOREIGN rust chrono::NaiveDateTime #-}
becomes
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.
pub trait Encoding {
fn encode<C>
( s: C::State
, a: &Self
, c: &C
) -> Result<C::State, C::Error>
where C: EncoderImpl
;
}
pub trait Decoding: Sized {
fn decode<C>
( 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
Repris the serialized type encoding to or decoding from. e.g.StringorVec<u8>.The
Erroris the type for possible errors occurred.The
Stateis the intermediate state used during encoding or decoding.
You might find
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.
pub trait EncoderImpl: ForeignEncodersImpl {
type Repr;
fn encode_value<A>
( &self
, a: &A
) -> Result<Self::Repr, Self::Error>
where A: Encoding
;
}
pub trait DecoderImpl: ForeignDecodersImpl {
type Repr;
fn decode_value<A>
( &self
, r: &Self::Repr
) -> Result<A, Self::Error>
where A: Decoding
;
}
For an encode_value, you usually should
Provide an initial state
Feed it to
A::encodeTransform the state returned to serialized type,
Repr
For a decode_value, you usually should
Transform the
Reprinto an initial stateStateFeed it to
A::decodeMake sure the returned state did not go wrong
Return the decoded value returned by
A::decode
Encode/Decode Record Type¶
pub trait EncoderImpl: ForeignEncodersImpl {
fn encode_record<A>
( &self
, s: Self::State
, n_fields: usize
, a: &A
, k: fn(&Self, Self::State, &A) -> Result<Self::State, Self::Error>
) -> Result<Self::State, Self::Error>
;
fn encode_record_field<A>
( &self
, s: Self::State
, i: usize
, name: &str
, a: &A
) -> Result<Self::State, Self::Error>
where A: Encoding
;
}
pub trait DecoderImpl: ForeignDecodersImpl {
fn decode_record<A>
( &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<A>
( &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¶
pub trait EncoderImpl: ForeignEncodersImpl {
fn encode_enum<A>
( &self
, s: Self::State
, a: A
, as_index: fn(A) -> i32
, as_name: fn(A) -> &'static str
) -> Result<Self::State, Self::Error>
;
}
pub trait DecoderImpl: ForeignDecodersImpl {
fn decode_enum<A>
( &self
, s: Self::State
, by_index: fn(i32) -> Option<A>
, by_name: fn(&str) -> Option<A>
) -> 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¶
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<Self::State, Self::Error>
// ;
}
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<A>
( &self
, s: Self::State
, v: &Option<A>
) -> Result<Self::State, Self::Error>
where A: Encoding
;
fn encode_list<A>
( &self
, s: Self::State
, v: &Vec<A>
) -> Result<Self::State, Self::Error>
where A: Encoding
;
fn encode_unit
( &self
, s: Self::State
, v: &()
) -> Result<Self::State, Self::Error>
;
fn encode_bool
( &self
, s: Self::State
, v: &bool
) -> Result<Self::State, Self::Error>
;
fn encode_int32
( &self
, s: Self::State
, v: &i32
) -> Result<Self::State, Self::Error>
;
fn encode_double
( &self
, s: Self::State
, v: &f64
) -> Result<Self::State, Self::Error>
;
fn encode_string
( &self
, s: Self::State
, v: &String
) -> Result<Self::State, Self::Error>
;
}
pub trait DecoderImpl: ForeignDecodersImpl {
fn decode_maybe<A>
( &self
, s: Self::State
) -> Result<(Self::State, Option<A>), Self::Error>
where A: Decoding
;
fn decode_list<A>
( &self
, s: Self::State
) -> Result<(Self::State, Vec<A>), 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.
module Hello where
foo :: FooReq -> IO FooRes
bar :: BarReq -> IO BarRes
becomes
use std::future::Future;
use std::sync::Arc;
pub trait HelloModule<E, I, O>
{
type FooFuture: Future<Output = Result<(O, FooRes), E>> + Send;
fn foo
( self: &Self
, a: FooReq
, i: I
) -> Self::FooFuture
;
type BarFuture: Future<Output = Result<(O, BarRes), E>> + Send;
fn bar
( self: &Self
, a: BarReq
, i: I
) -> Self::BarFuture
;
}
pub fn ask_transport<A, CA, CB, CH, E, I, O, EA, EB, RA, RB>
( namespace: &[&str]
, name: &str
) -> Option<fn(Arc<A>, &CH, Arc<CA>, Arc<CB>, RA, I) -> CH::OutputFuture>
where A: HelloModule<E, I, O> + Send + Sync + 'static
, CA: DecoderImpl<Error = EA, Repr = RA> + Send + Sync + 'static
, CB: EncoderImpl<Error = EB, Repr = RB> + Send + Sync + 'static
, CH: ServerCodecHandler<E, I, O, RA, RB, EA, EB>
{
// definition omitted
}
impl<T, CA, CB, E, I, O> HelloModule<E, I, O> for GuguguClient<T, CA, CB>
where T: ClientTransport<E, I, O, CA::Repr, CB::Repr, CA::Error, CB::Error>
, 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¶
pub trait ServerCodecHandler<E, I, O, RA, RB, EA, EB> {
type OutputFuture: Future<Output = Result<(O, RB), E>>;
fn run<A, CA, CB, B, R>
( &self
, ca: Arc<CA>
, cb: Arc<CB>
, k: impl FnOnce(A, I) -> R + Send + 'static
, ra: RA
, i: I
) -> Self::OutputFuture
where R: Future<Output = Result<(O, B), E>> + Send
, A: Decoding + Send + 'static
, B: Encoding + Send + 'static
, CA: DecoderImpl<Error = EA, Repr = RA> + Send + Sync + 'static
, CB: EncoderImpl<Error = EB, Repr = RB> + Send + Sync + 'static
;
}
pub fn ask_transport<A, CA, CB, CH, E, I, O, EA, EB, RA, RB>
( namespace: &[&str]
, name: &str
) -> Option<fn(Arc<A>, &CH, Arc<CA>, Arc<CB>, RA, I) -> CH::OutputFuture>
where A: HelloModule<E, I, O> + Send + Sync + 'static
, CA: DecoderImpl<Error = EA, Repr = RA> + Send + Sync + 'static
, CB: EncoderImpl<Error = EB, Repr = RB> + Send + Sync + 'static
, CH: ServerCodecHandler<E, I, O, RA, RB, EA, EB>
{
// 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 examples/lang/rust/src/bin/gugugu-example-rust-jsonhttp-server.rs for how to use the it.
Client Usage¶
pub trait ClientTransport<E, I, O, RA, RB, EA, EB> {
fn send<A, B, CA, CB>
( &self
, namespace: &[&str]
, name: &str
, a: A
, i: I
, ca: Arc<CA>
, cb: Arc<CB>
) -> Pin<Box<dyn Future<Output = Result<(O, B), E>> + Send>>
where A: Encoding + Send + 'static
, B: Decoding + Send + 'static
, CA: EncoderImpl<Error = EA, Repr = RA> + Send + Sync + 'static
, CB: DecoderImpl<Error = EB, Repr = RB> + Send + Sync + 'static
;
}
pub struct GuguguClient<T, CA, CB> {
pub transport: T,
pub encoder_impl: Arc<CA>,
pub decoder_impl: Arc<CB>,
}
impl<T, CA, CB, E, I, O> HelloModule<E, I, O> for GuguguClient<T, CA, CB>
where T: ClientTransport<E, I, O, CA::Repr, CB::Repr, CA::Error, CB::Error>
, 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
examples/lang/rust/src/bin/gugugu-example-rust-jsonhttp-client.rs
for how to write a ClientTransport.
Command Line Options¶
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