Skip to content

Commit

Permalink
Merge 6ad0825 into 55d34ec
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyan-dfinity committed Apr 25, 2023
2 parents 55d34ec + 6ad0825 commit 61c01ee
Show file tree
Hide file tree
Showing 20 changed files with 143 additions and 52 deletions.
3 changes: 2 additions & 1 deletion rust/candid/src/bindings/motoko.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ fn pp_ty(ty: &Type) -> RcDoc {
Var(ref s) => escape(s, false),
Principal => str("Principal"),
Opt(ref t) => str("?").append(pp_ty(t)),
Vec(ref t) => enclose("[", pp_ty(t), "]"), // TODO blob
Vec(ref t) if matches!(t.as_ref(), Nat8) => str("Blob"),
Vec(ref t) => enclose("[", pp_ty(t), "]"),
Record(ref fs) => {
if is_tuple(ty) {
let tuple = concat(fs.iter().map(|f| pp_ty(&f.ty)), ",");
Expand Down
67 changes: 56 additions & 11 deletions rust/candid/src/bindings/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@ use pretty::RcDoc;
use std::collections::BTreeSet;
use std::rc::Rc;

#[derive(Clone)]
pub struct Config {
pub candid_crate: String,
/// Applies to all types for now
pub type_attributes: String,
/// Only generates SERVICE struct if canister_id is not provided
pub canister_id: Option<crate::Principal>,
/// Service name when canister id is provided
pub service_name: String,
}
impl Config {
pub fn new() -> Self {
Config {
candid_crate: "candid".to_string(),
type_attributes: "".to_string(),
canister_id: None,
service_name: "service".to_string(),
}
}
}

type RecPoints<'a> = BTreeSet<&'a str>;
// The definition of tuple is language specific.
pub(crate) fn is_tuple(fs: &[Field]) -> bool {
Expand Down Expand Up @@ -80,8 +101,9 @@ fn pp_ty<'a>(ty: &'a Type, recs: &RecPoints) -> RcDoc<'a> {
name
}
}
Principal => str("candid::Principal"),
Principal => str("Principal"),
Opt(ref t) => str("Option").append(enclose("<", pp_ty(t, recs), ">")),
Vec(ref t) if matches!(t.as_ref(), Nat8) => str("serde_bytes::ByteBuf"),
Vec(ref t) => str("Vec").append(enclose("<", pp_ty(t, recs), ">")),
Record(ref fs) => pp_record_fields(fs, recs),
Variant(_) => unreachable!(), // not possible after rewriting
Expand Down Expand Up @@ -128,8 +150,17 @@ fn pp_variant_fields<'a>(fs: &'a [Field], recs: &RecPoints) -> RcDoc<'a> {
enclose_space("{", fields, "}")
}

fn pp_defs<'a>(env: &'a TypeEnv, def_list: &'a [&'a str], recs: &'a RecPoints) -> RcDoc<'a> {
let derive = "#[derive(CandidType, Deserialize)]";
fn pp_defs<'a>(
config: &'a Config,
env: &'a TypeEnv,
def_list: &'a [&'a str],
recs: &'a RecPoints,
) -> RcDoc<'a> {
let derive = if config.type_attributes.is_empty() {
"#[derive(CandidType, Deserialize)]"
} else {
&config.type_attributes
};
lines(def_list.iter().map(|id| {
let ty = env.find_type(id).unwrap();
let name = ident(id).append(" ");
Expand Down Expand Up @@ -256,7 +287,7 @@ fn pp_function<'a>(id: &'a str, func: &'a Function) -> RcDoc<'a> {
sig.append(enclose_space("{", body, "}"))
}

fn pp_actor<'a>(env: &'a TypeEnv, actor: &'a Type) -> RcDoc<'a> {
fn pp_actor<'a>(config: &'a Config, env: &'a TypeEnv, actor: &'a Type) -> RcDoc<'a> {
// TODO trace to service before we figure out what canister means in Rust
let serv = env.as_service(actor).unwrap();
let body = RcDoc::intersperse(
Expand All @@ -266,30 +297,44 @@ fn pp_actor<'a>(env: &'a TypeEnv, actor: &'a Type) -> RcDoc<'a> {
}),
RcDoc::hardline(),
);
RcDoc::text("pub struct SERVICE(pub candid::Principal);")
let res = RcDoc::text("pub struct SERVICE(pub Principal);")
.append(RcDoc::hardline())
.append("impl SERVICE")
.append(enclose_space("{", body, "}"))
.append(RcDoc::hardline());
if let Some(cid) = config.canister_id {
res.append(format!(
r#"pub fn {}() -> SERVICE {{
SERVICE(Principal::from_text("{}").unwrap())
}}"#,
config.service_name, cid
))
} else {
res
}
}

pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
let header = r#"// This is an experimental feature to generate Rust binding from Candid.
pub fn compile(config: &Config, env: &TypeEnv, actor: &Option<Type>) -> String {
let header = format!(
r#"// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use {}::{{self, CandidType, Deserialize, Principal}};
use ic_cdk::api::call::CallResult;
"#;
"#,
config.candid_crate
);
let (env, actor) = nominalize_all(env, actor);
let def_list: Vec<_> = if let Some(actor) = &actor {
chase_actor(&env, actor).unwrap()
} else {
env.0.iter().map(|pair| pair.0.as_ref()).collect()
};
let recs = infer_rec(&env, &def_list).unwrap();
let defs = pp_defs(&env, &def_list, &recs);
let defs = pp_defs(config, &env, &def_list, &recs);
let doc = match &actor {
None => defs,
Some(actor) => {
let actor = pp_actor(&env, actor);
let actor = pp_actor(config, &env, actor);
defs.append(actor)
}
};
Expand Down
7 changes: 5 additions & 2 deletions rust/candid/tests/assets/ok/actor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

candid::define_function!(pub f : (i8) -> (i8));
Expand All @@ -9,7 +9,7 @@ pub type g = f;
#[derive(CandidType, Deserialize)]
pub struct o(Option<Box<o>>);

pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn f(&self, arg0: candid::Nat) -> CallResult<(h,)> {
ic_cdk::call(self.0, "f", (arg0,)).await
Expand All @@ -24,3 +24,6 @@ impl SERVICE{
ic_cdk::call(self.0, "o", (arg0,)).await
}
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}
7 changes: 5 additions & 2 deletions rust/candid/tests/assets/ok/class.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

#[derive(CandidType, Deserialize)]
pub struct List(Option<(candid::Int,Box<List>,)>);

pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn get(&self) -> CallResult<(List,)> {
ic_cdk::call(self.0, "get", ()).await
Expand All @@ -15,3 +15,6 @@ impl SERVICE{
ic_cdk::call(self.0, "set", (arg0,)).await
}
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}
2 changes: 1 addition & 1 deletion rust/candid/tests/assets/ok/comment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

pub type id = u8;
Expand Down
7 changes: 5 additions & 2 deletions rust/candid/tests/assets/ok/cyclic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

pub type C = Box<A>;
Expand All @@ -11,7 +11,7 @@ pub struct A(Option<B>);
pub type Z = Box<A>;
pub type Y = Z;
pub type X = Y;
pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn f(
&self,
Expand All @@ -25,3 +25,6 @@ impl SERVICE{
ic_cdk::call(self.0, "f", (arg0,arg1,arg2,arg3,arg4,arg5,)).await
}
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}
7 changes: 5 additions & 2 deletions rust/candid/tests/assets/ok/escape.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

#[derive(CandidType, Deserialize)]
Expand All @@ -15,9 +15,12 @@ pub struct t {
_1020746185_: candid::Nat,
}

pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn _2635468193_(&self, arg0: t) -> CallResult<()> {
ic_cdk::call(self.0, "\n\'\"\'\'\"\"\r\t", (arg0,)).await
}
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}
2 changes: 1 addition & 1 deletion rust/candid/tests/assets/ok/example.mo
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module {
#leaf : Int;
};
public type Self = actor {
f : shared (list, [Nat8], ?Bool) -> ();
f : shared (list, Blob, ?Bool) -> ();
g : shared query (my_type, List, ?List, nested) -> async (Int, broker);
h : shared ([?Text], { #A : Nat; #B : ?Text }, ?List) -> async {
_42_ : {};
Expand Down
11 changes: 7 additions & 4 deletions rust/candid/tests/assets/ok/example.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

#[derive(CandidType, Deserialize)]
Expand All @@ -9,7 +9,7 @@ pub struct node { head: candid::Nat, tail: Box<list> }
#[derive(CandidType, Deserialize)]
pub struct list(Option<node>);

pub type my_type = candid::Principal;
pub type my_type = Principal;
#[derive(CandidType, Deserialize)]
pub struct List_inner { head: candid::Int, tail: Box<List> }

Expand Down Expand Up @@ -57,12 +57,12 @@ pub struct b (candid::Int,candid::Nat,);
#[derive(CandidType, Deserialize)]
pub enum a { a, b(b) }

pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn f(
&self,
arg0: list,
arg1: Vec<u8>,
arg1: serde_bytes::ByteBuf,
arg2: Option<bool>,
) -> CallResult<()> { ic_cdk::call(self.0, "f", (arg0,arg1,arg2,)).await }
pub async fn g(
Expand All @@ -89,3 +89,6 @@ impl SERVICE{
(Option<a>,Option<b>,)
> { ic_cdk::call(self.0, "x", (arg0,arg1,)).await }
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}
7 changes: 5 additions & 2 deletions rust/candid/tests/assets/ok/fieldnat.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

#[derive(CandidType, Deserialize)]
Expand Down Expand Up @@ -30,7 +30,7 @@ pub struct foo_arg0 { _2_: candid::Int }
#[derive(CandidType, Deserialize)]
pub struct foo_ret0 { _2_: candid::Int, _2: candid::Int }

pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn bab(&self, arg0: candid::Int, arg1: candid::Nat) -> CallResult<
()
Expand All @@ -54,3 +54,6 @@ impl SERVICE{
ic_cdk::call(self.0, "foo", (arg0,)).await
}
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}
4 changes: 2 additions & 2 deletions rust/candid/tests/assets/ok/keyword.mo
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ module {
fieldnat : shared { _2_ : Int; _50_ : Nat } -> async { _0_ : Int };
oneway : shared Nat8 -> ();
oneway__ : shared Nat8 -> ();
query_ : shared query [Nat8] -> async [Nat8];
query_ : shared query Blob -> async Blob;
return_ : shared o -> async o;
service : t;
tuple : shared ((Int, [Nat8], Text)) -> async ((Int, Nat8));
tuple : shared ((Int, Blob, Text)) -> async ((Int, Nat8));
variant : shared { #A; #B; #C; #D : Float } -> async ();
}
}
22 changes: 14 additions & 8 deletions rust/candid/tests/assets/ok/keyword.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

#[derive(CandidType, Deserialize)]
Expand Down Expand Up @@ -46,7 +46,7 @@ candid::define_function!(pub t : (r#return) -> ());
#[derive(CandidType, Deserialize)]
pub enum variant_arg0 { A, B, C, D(f64) }

pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn Oneway(&self) -> CallResult<()> {
ic_cdk::call(self.0, "Oneway", ()).await
Expand All @@ -66,19 +66,25 @@ impl SERVICE{
pub async fn oneway_(&self, arg0: u8) -> CallResult<()> {
ic_cdk::call(self.0, "oneway_", (arg0,)).await
}
pub async fn query(&self, arg0: Vec<u8>) -> CallResult<(Vec<u8>,)> {
ic_cdk::call(self.0, "query", (arg0,)).await
}
pub async fn query(&self, arg0: serde_bytes::ByteBuf) -> CallResult<
(serde_bytes::ByteBuf,)
> { ic_cdk::call(self.0, "query", (arg0,)).await }
pub async fn r#return(&self, arg0: o) -> CallResult<(o,)> {
ic_cdk::call(self.0, "return", (arg0,)).await
}
pub async fn service(&self, arg0: r#return) -> CallResult<()> {
ic_cdk::call(self.0, "service", (arg0,)).await
}
pub async fn tuple(&self, arg0: (candid::Int,Vec<u8>,String,)) -> CallResult<
((candid::Int,u8,),)
> { ic_cdk::call(self.0, "tuple", (arg0,)).await }
pub async fn tuple(
&self,
arg0: (candid::Int,serde_bytes::ByteBuf,String,),
) -> CallResult<((candid::Int,u8,),)> {
ic_cdk::call(self.0, "tuple", (arg0,)).await
}
pub async fn variant(&self, arg0: variant_arg0) -> CallResult<()> {
ic_cdk::call(self.0, "variant", (arg0,)).await
}
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}
7 changes: 5 additions & 2 deletions rust/candid/tests/assets/ok/recursion.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use candid::{self, CandidType, Deserialize};
use candid::{self, CandidType, Deserialize, Principal};
use ic_cdk::api::call::CallResult;

candid::define_function!(pub t : (s) -> ());
Expand Down Expand Up @@ -31,7 +31,7 @@ candid::define_service!(pub s : {
"f" : t::ty();
"g" : candid::func!((list) -> (B, tree, stream));
});
pub struct SERVICE(pub candid::Principal);
pub struct SERVICE(pub Principal);
impl SERVICE{
pub async fn f(&self, arg0: s) -> CallResult<()> {
ic_cdk::call(self.0, "f", (arg0,)).await
Expand All @@ -40,3 +40,6 @@ impl SERVICE{
ic_cdk::call(self.0, "g", (arg0,)).await
}
}
pub fn service() -> SERVICE {
SERVICE(Principal::from_text("aaaaa-aa").unwrap())
}

0 comments on commit 61c01ee

Please sign in to comment.