Skip to content

Commit

Permalink
More rust bindgen fix (#562)
Browse files Browse the repository at this point in the history
* project_methods fail on not found

* snake case for record fields

* add MotokoResult

* change template to take reference for call

* bump dependencies
  • Loading branch information
chenyan-dfinity committed Jul 29, 2024
1 parent cccaa04 commit 47f6cae
Show file tree
Hide file tree
Showing 23 changed files with 296 additions and 199 deletions.
247 changes: 121 additions & 126 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

## 2024-05-03

### Candid 0.10.10

* Add `candid::MotokoResult` type. Use `motoko_result.into_result()` to convert the value into Rust result, and `rust_result.into()` to get Motoko result.

### candid_parser 0.2.0-beta

* Breaking changes:
Expand Down
2 changes: 1 addition & 1 deletion rust/candid/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "candid"
version = "0.10.9"
version = "0.10.10"
edition = "2021"
rust-version.workspace = true
authors = ["DFINITY Team"]
Expand Down
1 change: 1 addition & 0 deletions rust/candid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ pub use types::{
rc,
reference::{Func, Service},
reserved::{Empty, Reserved},
result::MotokoResult,
TypeEnv,
};

Expand Down
1 change: 1 addition & 0 deletions rust/candid/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod number;
pub mod principal;
pub mod reference;
pub mod reserved;
pub mod result;

pub mod arc;
pub mod rc;
Expand Down
60 changes: 60 additions & 0 deletions rust/candid/src/types/result.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crate::types::{CandidType, Compound, Field, Label, Serializer, Type, TypeInner};
use serde::{Deserialize, Serialize};

#[allow(non_camel_case_types)]
#[derive(Deserialize, Debug, Clone, Serialize)]
pub enum MotokoResult<T, E> {
ok(T),
err(E),
}
impl<T, E> MotokoResult<T, E> {
pub fn into_result(self) -> Result<T, E> {
match self {
MotokoResult::ok(v) => Ok(v),
MotokoResult::err(e) => Err(e),
}
}
}
impl<T, E> From<Result<T, E>> for MotokoResult<T, E> {
fn from(r: Result<T, E>) -> Self {
match r {
Ok(v) => MotokoResult::ok(v),
Err(e) => MotokoResult::err(e),
}
}
}
impl<T, E> CandidType for MotokoResult<T, E>
where
T: CandidType,
E: CandidType,
{
fn _ty() -> Type {
TypeInner::Variant(vec![
// Make sure the field id is sorted by idl_hash
Field {
id: Label::Named("ok".to_owned()).into(),
ty: T::ty(),
},
Field {
id: Label::Named("err".to_owned()).into(),
ty: E::ty(),
},
])
.into()
}
fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
where
S: Serializer,
{
match *self {
MotokoResult::ok(ref v) => {
let mut ser = serializer.serialize_variant(0)?;
Compound::serialize_element(&mut ser, v)
}
MotokoResult::err(ref e) => {
let mut ser = serializer.serialize_variant(1)?;
Compound::serialize_element(&mut ser, e)
}
}
}
}
4 changes: 2 additions & 2 deletions rust/candid_parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "candid_parser"
version = "0.2.0-beta.3"
version = "0.2.0-beta.4"
edition = "2021"
rust-version.workspace = true
authors = ["DFINITY Team"]
Expand Down Expand Up @@ -31,7 +31,7 @@ serde.workspace = true
lalrpop-util = "0.20.0"
logos = "0.14"
convert_case = "0.6"
handlebars = "5.1"
handlebars = "6.0"
toml = { version = "0.8", default-features = false, features = ["parse"] }

arbitrary = { workspace = true, optional = true }
Expand Down
30 changes: 25 additions & 5 deletions rust/candid_parser/src/bindings/analysis.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
use crate::Result;
use crate::{Error, Result};
use candid::types::{Type, TypeEnv, TypeInner};
use std::collections::{BTreeMap, BTreeSet};

/// Select a subset of methods from an actor.
pub fn project_methods(env: &TypeEnv, actor: &Option<Type>, methods: &[String]) -> Option<Type> {
let service = env.as_service(actor.as_ref()?).ok()?;
pub fn project_methods(
env: &TypeEnv,
actor: &Option<Type>,
mut methods: Vec<String>,
) -> Result<Type> {
let actor = actor
.as_ref()
.ok_or_else(|| Error::Custom(anyhow::anyhow!("no actor")))?;
let service = env.as_service(actor)?;
let filtered = service
.iter()
.filter(|(name, _)| methods.contains(name))
.filter(|(name, _)| {
if let Some(idx) = methods.iter().position(|m| m == name) {
methods.swap_remove(idx);
true
} else {
false
}
})
.cloned()
.collect();
Some(TypeInner::Service(filtered).into())
if !methods.is_empty() {
return Err(Error::Custom(anyhow::anyhow!(
"methods not found: {:?}",
methods
)));
}
Ok(TypeInner::Service(filtered).into())
}

/// Same as chase_actor, with seen set as part of the type. Used for chasing type names from type definitions.
Expand Down
22 changes: 18 additions & 4 deletions rust/candid_parser/src/bindings/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,19 @@ pub(crate) fn is_tuple(fs: &[Field]) -> bool {
.enumerate()
.any(|(i, field)| field.id.get_id() != (i as u32))
}
fn as_result(fs: &[Field]) -> Option<(&Type, &Type)> {
fn as_result(fs: &[Field]) -> Option<(&Type, &Type, bool)> {
match fs {
[Field { id: ok, ty: t_ok }, Field { id: err, ty: t_err }]
if **ok == Label::Named("Ok".to_string())
&& **err == Label::Named("Err".to_string()) =>
{
Some((t_ok, t_err))
Some((t_ok, t_err, false))
}
[Field { id: ok, ty: t_ok }, Field { id: err, ty: t_err }]
if **ok == Label::Named("ok".to_string())
&& **err == Label::Named("err".to_string()) =>
{
Some((t_ok, t_err, true))
}
_ => None,
}
Expand Down Expand Up @@ -230,7 +236,7 @@ fn test_{test_name}() {{
Record(ref fs) => self.pp_record_fields(fs, false, is_ref),
Variant(ref fs) => {
// only possible for result variant
let (ok, err) = as_result(fs).unwrap();
let (ok, err, is_motoko) = as_result(fs).unwrap();
// This is a hacky way to redirect Result type
let old = self
.state
Expand All @@ -240,6 +246,8 @@ fn test_{test_name}() {{
// not generating test for this use_type. rustc should be able to catch type mismatches.
self.state.update_stats("use_type");
res
} else if is_motoko {
"candid::MotokoResult".to_string()
} else {
"std::result::Result".to_string()
};
Expand Down Expand Up @@ -285,7 +293,13 @@ fn test_{test_name}() {{
self.state.update_stats("name");
res
} else {
let case = if is_variant { Some(Case::Pascal) } else { None };
let case = if is_variant {
Some(Case::Pascal)
} else if !id.starts_with('_') {
Some(Case::Snake)
} else {
None
};
ident_(id, case)
};
let attr = if is_rename {
Expand Down
2 changes: 1 addition & 1 deletion rust/candid_parser/src/bindings/rust_agent.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type Result<T> = std::result::Result<T, ic_agent::AgentError>;
pub struct {{PascalCase service_name}}<'a>(pub Principal, pub &'a ic_agent::Agent);
impl<'a> {{PascalCase service_name}}<'a> {
{{#each methods}}
pub async fn {{this.name}}(&self{{#each this.args}}, {{this.0}}: {{this.1}}{{/each}}) -> Result<{{vec_to_arity this.rets}}> {
pub async fn {{this.name}}(&self{{#each this.args}}, {{this.0}}: &{{this.1}}{{/each}}) -> Result<{{vec_to_arity this.rets}}> {
let args = Encode!({{#each this.args}}&{{this.0}}{{#unless @last}},{{/unless}}{{/each}})?;
let bytes = self.1.{{#if (eq this.mode "update")}}update{{else}}query{{/if}}(&self.0, "{{escape_debug this.original_name}}").with_arg(args).{{#if (eq this.mode "update")}}call_and_wait{{else}}call{{/if}}().await?;
Ok(Decode!(&bytes{{#each this.rets}}, {{this}}{{/each}})?)
Expand Down
2 changes: 1 addition & 1 deletion rust/candid_parser/src/bindings/rust_call.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use ic_cdk::api::call::CallResult as Result;
pub struct {{PascalCase service_name}}(pub Principal);
impl {{PascalCase service_name}} {
{{#each methods}}
pub async fn {{this.name}}(&self{{#each this.args}}, {{this.0}}: {{this.1}}{{/each}}) -> Result<({{#each this.rets}}{{this}},{{/each}})> {
pub async fn {{this.name}}(&self{{#each this.args}}, {{this.0}}: &{{this.1}}{{/each}}) -> Result<({{#each this.rets}}{{this}},{{/each}})> {
ic_cdk::call(self.0, "{{escape_debug this.original_name}}", ({{#each this.args}}{{this.0}},{{/each}})).await
}
{{/each}}
Expand Down
8 changes: 4 additions & 4 deletions rust/candid_parser/tests/assets/ok/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ pub struct O(pub Option<Box<O>>);

pub struct Service(pub Principal);
impl Service {
pub async fn f(&self, arg0: candid::Nat) -> Result<(H,)> {
pub async fn f(&self, arg0: &candid::Nat) -> Result<(H,)> {
ic_cdk::call(self.0, "f", (arg0,)).await
}
pub async fn g(&self, arg0: i8) -> Result<(i8,)> {
pub async fn g(&self, arg0: &i8) -> Result<(i8,)> {
ic_cdk::call(self.0, "g", (arg0,)).await
}
pub async fn h(&self, arg0: i8) -> Result<(i8,)> {
pub async fn h(&self, arg0: &i8) -> Result<(i8,)> {
ic_cdk::call(self.0, "h", (arg0,)).await
}
pub async fn o(&self, arg0: O) -> Result<(O,)> {
pub async fn o(&self, arg0: &O) -> Result<(O,)> {
ic_cdk::call(self.0, "o", (arg0,)).await
}
}
Expand Down
2 changes: 1 addition & 1 deletion rust/candid_parser/tests/assets/ok/cyclic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub type X = Y;

pub struct Service(pub Principal);
impl Service {
pub async fn f(&self, arg0: A, arg1: B, arg2: C, arg3: X, arg4: Y, arg5: Z) -> Result<()> {
pub async fn f(&self, arg0: &A, arg1: &B, arg2: &C, arg3: &X, arg4: &Y, arg5: &Z) -> Result<()> {
ic_cdk::call(self.0, "f", (arg0,arg1,arg2,arg3,arg4,arg5,)).await
}
}
Expand Down
6 changes: 3 additions & 3 deletions rust/candid_parser/tests/assets/ok/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ pub enum HRet { #[serde(rename="a")] A(Box<T>), #[serde(rename="b")] B{} }

pub struct Service(pub Principal);
impl Service {
pub async fn f(&self, arg0: FArg) -> Result<(FRet,)> {
pub async fn f(&self, arg0: &FArg) -> Result<(FRet,)> {
ic_cdk::call(self.0, "f", (arg0,)).await
}
pub async fn g(&self, arg0: T) -> Result<(GRet,)> {
pub async fn g(&self, arg0: &T) -> Result<(GRet,)> {
ic_cdk::call(self.0, "g", (arg0,)).await
}
pub async fn h(&self, arg0: (T,candid::Empty,)) -> Result<(HRet,)> {
pub async fn h(&self, arg0: &(T,candid::Empty,)) -> Result<(HRet,)> {
ic_cdk::call(self.0, "h", (arg0,)).await
}
}
Expand Down
2 changes: 1 addition & 1 deletion rust/candid_parser/tests/assets/ok/escape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct T {

pub struct Service(pub Principal);
impl Service {
pub async fn _2635468193_(&self, arg0: T) -> Result<()> {
pub async fn _2635468193_(&self, arg0: &T) -> Result<()> {
ic_cdk::call(self.0, "\n\'\"\'\'\"\"\r\t", (arg0,)).await
}
}
Expand Down
16 changes: 8 additions & 8 deletions rust/candid_parser/tests/assets/ok/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,28 +104,28 @@ pub(crate) enum Error { #[serde(rename="a")] A, #[serde(rename="b")] B }

pub struct Service(pub Principal);
impl Service {
pub async fn bbbbb(&self, arg0: B) -> Result<()> {
pub async fn bbbbb(&self, arg0: &B) -> Result<()> {
ic_cdk::call(self.0, "bbbbb", (arg0,)).await
}
pub async fn f(&self, arg0: S) -> Result<()> {
pub async fn f(&self, arg0: &S) -> Result<()> {
ic_cdk::call(self.0, "f", (arg0,)).await
}
pub async fn f_1(&self, arg0: List, arg1: serde_bytes::ByteBuf, arg2: Option<bool>) -> Result<()> {
pub async fn f_1(&self, arg0: &List, arg1: &serde_bytes::ByteBuf, arg2: &Option<bool>) -> Result<()> {
ic_cdk::call(self.0, "f1", (arg0,arg1,arg2,)).await
}
pub async fn g(&self, arg0: List) -> Result<(B,Tree,Stream,)> {
pub async fn g(&self, arg0: &List) -> Result<(B,Tree,Stream,)> {
ic_cdk::call(self.0, "g", (arg0,)).await
}
pub async fn G11(&self, id: CanisterId, list: MyList, is_okay: Option<MyList>, arg3: Nested) -> Result<(i128,Broker,NestedRes,)> {
pub async fn G11(&self, id: &CanisterId, list: &MyList, is_okay: &Option<MyList>, arg3: &Nested) -> Result<(i128,Broker,NestedRes,)> {
ic_cdk::call(self.0, "g1", (id,list,is_okay,arg3,)).await
}
pub async fn h(&self, arg0: Vec<Option<String>>, arg1: HArg1, arg2: Option<MyList>) -> Result<(HRet,)> {
pub async fn h(&self, arg0: &Vec<Option<String>>, arg1: &HArg1, arg2: &Option<MyList>) -> Result<(HRet,)> {
ic_cdk::call(self.0, "h", (arg0,arg1,arg2,)).await
}
pub async fn i(&self, arg0: MyList, arg1: FArg1) -> Result<(Option<MyList>,Res,)> {
pub async fn i(&self, arg0: &MyList, arg1: &FArg1) -> Result<(Option<MyList>,Res,)> {
ic_cdk::call(self.0, "i", (arg0,arg1,)).await
}
pub async fn x(&self, arg0: A, arg1: B) -> Result<(Option<A>,Option<B>,std::result::Result<XRet2Ok, Error>,)> {
pub async fn x(&self, arg0: &A, arg1: &B) -> Result<(Option<A>,Option<B>,std::result::Result<XRet2Ok, Error>,)> {
ic_cdk::call(self.0, "x", (arg0,arg1,)).await
}
}
Expand Down
14 changes: 7 additions & 7 deletions rust/candid_parser/tests/assets/ok/fieldnat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,25 @@ pub struct FooRet { pub _2_: candid::Int, pub _2: candid::Int }

pub struct Service(pub Principal);
impl Service {
pub async fn bab(&self, arg0: candid::Int, arg1: candid::Nat) -> Result<()> {
pub async fn bab(&self, arg0: &candid::Int, arg1: &candid::Nat) -> Result<()> {
ic_cdk::call(self.0, "bab", (arg0,arg1,)).await
}
pub async fn bar(&self, arg0: BarArg) -> Result<(BarRet,)> {
pub async fn bar(&self, arg0: &BarArg) -> Result<(BarRet,)> {
ic_cdk::call(self.0, "bar", (arg0,)).await
}
pub async fn bas(&self, arg0: (candid::Int,candid::Int,)) -> Result<((String,candid::Nat,),)> {
pub async fn bas(&self, arg0: &(candid::Int,candid::Int,)) -> Result<((String,candid::Nat,),)> {
ic_cdk::call(self.0, "bas", (arg0,)).await
}
pub async fn baz(&self, arg0: BazArg) -> Result<(BazRet,)> {
pub async fn baz(&self, arg0: &BazArg) -> Result<(BazRet,)> {
ic_cdk::call(self.0, "baz", (arg0,)).await
}
pub async fn bba(&self, arg0: Tuple) -> Result<(NonTuple,)> {
pub async fn bba(&self, arg0: &Tuple) -> Result<(NonTuple,)> {
ic_cdk::call(self.0, "bba", (arg0,)).await
}
pub async fn bib(&self, arg0: (candid::Int,)) -> Result<(BibRet,)> {
pub async fn bib(&self, arg0: &(candid::Int,)) -> Result<(BibRet,)> {
ic_cdk::call(self.0, "bib", (arg0,)).await
}
pub async fn foo(&self, arg0: FooArg) -> Result<(FooRet,)> {
pub async fn foo(&self, arg0: &FooArg) -> Result<(FooRet,)> {
ic_cdk::call(self.0, "foo", (arg0,)).await
}
}
Expand Down
20 changes: 10 additions & 10 deletions rust/candid_parser/tests/assets/ok/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,34 @@ impl Service {
pub async fn oneway(&self) -> Result<()> {
ic_cdk::call(self.0, "Oneway", ()).await
}
pub async fn f(&self, arg0: O) -> Result<(O,)> {
pub async fn f(&self, arg0: &O) -> Result<(O,)> {
ic_cdk::call(self.0, "f_", (arg0,)).await
}
pub async fn field(&self, arg0: FieldArg) -> Result<(FieldRet,)> {
pub async fn field(&self, arg0: &FieldArg) -> Result<(FieldRet,)> {
ic_cdk::call(self.0, "field", (arg0,)).await
}
pub async fn fieldnat(&self, arg0: FieldnatArg) -> Result<((candid::Int,),)> {
pub async fn fieldnat(&self, arg0: &FieldnatArg) -> Result<((candid::Int,),)> {
ic_cdk::call(self.0, "fieldnat", (arg0,)).await
}
pub async fn oneway(&self, arg0: u8) -> Result<()> {
pub async fn oneway(&self, arg0: &u8) -> Result<()> {
ic_cdk::call(self.0, "oneway", (arg0,)).await
}
pub async fn oneway(&self, arg0: u8) -> Result<()> {
pub async fn oneway(&self, arg0: &u8) -> Result<()> {
ic_cdk::call(self.0, "oneway_", (arg0,)).await
}
pub async fn query(&self, arg0: serde_bytes::ByteBuf) -> Result<(serde_bytes::ByteBuf,)> {
pub async fn query(&self, arg0: &serde_bytes::ByteBuf) -> Result<(serde_bytes::ByteBuf,)> {
ic_cdk::call(self.0, "query", (arg0,)).await
}
pub async fn r#return(&self, arg0: O) -> Result<(O,)> {
pub async fn r#return(&self, arg0: &O) -> Result<(O,)> {
ic_cdk::call(self.0, "return", (arg0,)).await
}
pub async fn service(&self, arg0: Return) -> Result<()> {
pub async fn service(&self, arg0: &Return) -> Result<()> {
ic_cdk::call(self.0, "service", (arg0,)).await
}
pub async fn tuple(&self, arg0: (candid::Int,serde_bytes::ByteBuf,String,)) -> Result<((candid::Int,u8,),)> {
pub async fn tuple(&self, arg0: &(candid::Int,serde_bytes::ByteBuf,String,)) -> Result<((candid::Int,u8,),)> {
ic_cdk::call(self.0, "tuple", (arg0,)).await
}
pub async fn variant(&self, arg0: VariantArg) -> Result<()> {
pub async fn variant(&self, arg0: &VariantArg) -> Result<()> {
ic_cdk::call(self.0, "variant", (arg0,)).await
}
}
Expand Down
Loading

0 comments on commit 47f6cae

Please sign in to comment.