diff --git a/Cargo.lock b/Cargo.lock index 34a3ca56..916202b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,7 +257,7 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "candid" -version = "0.9.5" +version = "0.9.6" dependencies = [ "anyhow", "arbitrary", @@ -266,6 +266,7 @@ dependencies = [ "byteorder", "candid_derive", "codespan-reporting", + "convert_case", "crc32fast", "criterion", "data-encoding", @@ -297,7 +298,7 @@ dependencies = [ [[package]] name = "candid_derive" -version = "0.6.2" +version = "0.6.3" dependencies = [ "lazy_static", "proc-macro2 1.0.66", @@ -443,6 +444,15 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cpufeatures" version = "0.2.9" @@ -600,7 +610,7 @@ dependencies = [ [[package]] name = "didc" -version = "0.3.4" +version = "0.3.5" dependencies = [ "anyhow", "candid", diff --git a/Changelog.md b/Changelog.md index 5e4c280c..ba3d8baf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,11 +1,21 @@ # Changelog -## 2023-07-25 (Rust 0.9.2) +## 2023-09-05 (Rust 0.9.6) + +* Improve Rust binding generation: 1) Fix generated code for agent; 2) Generated names conform to Rust convention: Pascal case for type names and enum tags; snake case for function names. +* Fix a bug when deriving empty struct/tuple enum tag, e.g., `#[derive(CandidType)] enum T { A{}, B() }`. +* Add `IDLDeserialize::new_with_config` to control deserializer behavior. For now, you can only bound the size of zero sized values. + +## 2023-07-25 (Rust 0.9.2--0.9.5) * Fix error message for `subtype::equal` to report the correct missing label. * Recover subtype error from custom deserializer. This fixes some custom types for not applying special opt rule. * Fix Candid UI to support composite query. +* Internally, move away from `BigInt::try_into` to allow more build targets, e.g. WASI and iOS. +* Spec change: allow `record {} <: record {null}`. +* Fix length counting of zero sized values. +* Remove `arc_type` feature. ## 2023-07-11 diff --git a/README.md b/README.md index 00c4a8a7..fb295c8d 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ A list of community maintained Candid libraries: * [Motoko](https://github.com/edjcase/motoko_candid) * [C#](https://github.com/edjCase/ICP.NET/tree/main/src/Candid) * [C++](https://github.com/icppWorld/icpp-candid) +* [Python](https://github.com/rocklabs-io/ic-py) ## Tools diff --git a/rust/candid/Cargo.toml b/rust/candid/Cargo.toml index 2e7481d8..3577941d 100644 --- a/rust/candid/Cargo.toml +++ b/rust/candid/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "candid" -version = "0.9.5" +version = "0.9.6" edition = "2021" authors = ["DFINITY Team"] description = "Candid is an interface description language (IDL) for interacting with canisters running on the Internet Computer." @@ -20,7 +20,7 @@ lalrpop = { version = "0.20.0", optional = true } [dependencies] byteorder = "1.4.3" -candid_derive = { path = "../candid_derive", version = "=0.6.2" } +candid_derive = { path = "../candid_derive", version = "=0.6.3" } codespan-reporting = "0.11" crc32fast = "1.3.0" data-encoding = "2.4.0" @@ -40,6 +40,7 @@ binread = { version = "2.1", features = ["debug_template"] } lalrpop-util = { version = "0.20.0", optional = true } logos = { version = "0.13", optional = true } +convert_case = { version = "0.6", optional = true } arbitrary = { version = "1.0", optional = true } # Don't upgrade serde_dhall. It will introduce dependency with invalid license. @@ -86,7 +87,7 @@ required-features = ["parser"] [features] configs = ["serde_dhall"] random = ["parser", "configs", "arbitrary", "fake", "rand"] -parser = ["lalrpop", "lalrpop-util", "logos"] +parser = ["lalrpop", "lalrpop-util", "logos", "convert_case"] all = ["random"] mute_warnings = [] diff --git a/rust/candid/src/bindings/rust.rs b/rust/candid/src/bindings/rust.rs index 328aee26..2e347bc4 100644 --- a/rust/candid/src/bindings/rust.rs +++ b/rust/candid/src/bindings/rust.rs @@ -1,6 +1,7 @@ use super::analysis::{chase_actor, infer_rec}; use crate::pretty::*; use crate::types::{Field, Function, Label, SharedLabel, Type, TypeEnv, TypeInner}; +use convert_case::{Case, Casing}; use pretty::RcDoc; use std::collections::BTreeSet; @@ -56,25 +57,32 @@ static KEYWORDS: [&str; 51] = [ "while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized", "virtual", "yield", "try", ]; -fn ident_(id: &str) -> (RcDoc, bool) { +fn ident_(id: &str, case: Option) -> (RcDoc, bool) { if id.is_empty() || id.starts_with(|c: char| !c.is_ascii_alphabetic() && c != '_') || id.chars().any(|c| !c.is_ascii_alphanumeric() && c != '_') { - (RcDoc::as_string(format!("_{}_", crate::idl_hash(id))), true) - } else if ["crate", "self", "super", "Self"].contains(&id) { - (str(id).append("_"), true) - } else if KEYWORDS.contains(&id) { - (str("r#").append(id), false) + return (RcDoc::text(format!("_{}_", crate::idl_hash(id))), true); + } + let (is_rename, id) = if let Some(case) = case { + let new_id = id.to_case(case); + (new_id != id, new_id) + } else { + (false, id.to_owned()) + }; + if ["crate", "self", "super", "Self", "Result", "Principal"].contains(&id.as_str()) { + (RcDoc::text(format!("{id}_")), true) + } else if KEYWORDS.contains(&id.as_str()) { + (RcDoc::text(format!("r#{id}")), is_rename) } else { - (str(id), false) + (RcDoc::text(id), is_rename) } } -fn ident(id: &str) -> RcDoc { - ident_(id).0 +fn ident(id: &str, case: Option) -> RcDoc { + ident_(id, case).0 } -fn field_name(id: &str) -> RcDoc { - let (doc, is_rename) = ident_(id); +fn field_name(id: &str, case: Option) -> RcDoc { + let (doc, is_rename) = ident_(id, case); if is_rename { str("#[serde(rename=\"") .append(id.escape_debug().to_string()) @@ -107,7 +115,7 @@ fn pp_ty<'a>(ty: &'a Type, recs: &RecPoints) -> RcDoc<'a> { Reserved => str("candid::Reserved"), Empty => str("candid::Empty"), Var(ref id) => { - let name = ident(id); + let name = ident(id, Some(Case::Pascal)); if recs.contains(id.as_str()) { str("Box<").append(name).append(">") } else { @@ -128,15 +136,15 @@ fn pp_ty<'a>(ty: &'a Type, recs: &RecPoints) -> RcDoc<'a> { } } -fn pp_label(id: &SharedLabel) -> RcDoc { +fn pp_label(id: &SharedLabel, is_variant: bool) -> RcDoc { match &**id { - Label::Named(str) => field_name(str), + Label::Named(str) => field_name(str, if is_variant { Some(Case::Pascal) } else { None }), Label::Id(n) | Label::Unnamed(n) => str("_").append(RcDoc::as_string(n)).append("_"), } } fn pp_record_field<'a>(field: &'a Field, recs: &RecPoints) -> RcDoc<'a> { - pp_label(&field.id) + pp_label(&field.id, false) .append(kwd(":")) .append(pp_ty(&field.ty, recs)) } @@ -153,9 +161,9 @@ fn pp_record_fields<'a>(fs: &'a [Field], recs: &RecPoints) -> RcDoc<'a> { fn pp_variant_field<'a>(field: &'a Field, recs: &RecPoints) -> RcDoc<'a> { match field.ty.as_ref() { - TypeInner::Null => pp_label(&field.id), - TypeInner::Record(fs) => pp_label(&field.id).append(pp_record_fields(fs, recs)), - _ => pp_label(&field.id).append(enclose("(", pp_ty(&field.ty, recs), ")")), + TypeInner::Null => pp_label(&field.id, true), + TypeInner::Record(fs) => pp_label(&field.id, true).append(pp_record_fields(fs, recs)), + _ => pp_label(&field.id, true).append(enclose("(", pp_ty(&field.ty, recs), ")")), } } @@ -177,7 +185,7 @@ fn pp_defs<'a>( }; lines(def_list.iter().map(|id| { let ty = env.find_type(id).unwrap(); - let name = ident(id).append(" "); + let name = ident(id, Some(Case::Pascal)).append(" "); let vis = "pub "; match ty.as_ref() { TypeInner::Record(fs) => { @@ -220,7 +228,7 @@ fn pp_defs<'a>( .append(RcDoc::line()) .append(vis) .append("struct ") - .append(ident(id)) + .append(ident(id, Some(Case::Pascal))) .append(enclose("(", pp_ty(ty, recs), ")")) .append(";") .append(RcDoc::hardline()) @@ -270,11 +278,11 @@ fn pp_ty_service(serv: &[(String, Type)]) -> RcDoc { } fn pp_function<'a>(config: &Config, id: &'a str, func: &'a Function) -> RcDoc<'a> { - let name = ident(id); + let name = ident(id, Some(Case::Snake)); let empty = BTreeSet::new(); let arg_prefix = str(match config.target { Target::CanisterCall => "&self", - Target::Agent => "&self, agent: &ic_agent::Agent", + Target::Agent => "&self", Target::CanisterStub => unimplemented!(), }); let args = concat( @@ -326,19 +334,19 @@ fn pp_function<'a>(config: &Config, id: &'a str, func: &'a Function) -> RcDoc<'a let builder_method = if is_query { "query" } else { "update" }; let call = if is_query { "call" } else { "call_and_wait" }; let args = RcDoc::intersperse( - (0..func.args.len()).map(|i| RcDoc::text(format!("arg{i}"))), + (0..func.args.len()).map(|i| RcDoc::text(format!("&arg{i}"))), RcDoc::text(", "), ); - let blob = str("candid::Encode!").append(enclose("(", args, ")?;")); + let blob = str("Encode!").append(enclose("(", args, ")?;")); let rets = RcDoc::concat( func.rets .iter() .map(|ty| str(", ").append(pp_ty(ty, &empty))), ); str("let args = ").append(blob).append(RcDoc::hardline()) - .append(format!("let bytes = agent.{builder_method}(self.0, \"{method}\").with_arg(args).{call}().await?;")) + .append(format!("let bytes = self.1.{builder_method}(&self.0, \"{method}\").with_arg(args).{call}().await?;")) .append(RcDoc::hardline()) - .append("Ok(candid::Decode!(&bytes").append(rets).append(")?)") + .append("Ok(Decode!(&bytes").append(rets).append(")?)") } Target::CanisterStub => unimplemented!(), }; @@ -355,9 +363,23 @@ fn pp_actor<'a>(config: &'a Config, env: &'a TypeEnv, actor: &'a Type) -> RcDoc< }), RcDoc::hardline(), ); - let res = RcDoc::text("pub struct SERVICE(pub Principal);") + let struct_name = config.service_name.to_case(Case::Pascal); + let service_def = match config.target { + Target::CanisterCall => format!("pub struct {}(pub Principal);", struct_name), + Target::Agent => format!( + "pub struct {}<'a>(pub Principal, pub &'a ic_agent::Agent);", + struct_name + ), + Target::CanisterStub => unimplemented!(), + }; + let service_impl = match config.target { + Target::CanisterCall => format!("impl {} ", struct_name), + Target::Agent => format!("impl<'a> {}<'a> ", struct_name), + Target::CanisterStub => unimplemented!(), + }; + let res = RcDoc::text(service_def) .append(RcDoc::hardline()) - .append("impl SERVICE ") + .append(service_impl) .append(enclose_space("{", body, "}")) .append(RcDoc::hardline()); if let Some(cid) = config.canister_id { @@ -367,10 +389,19 @@ fn pp_actor<'a>(config: &'a Config, env: &'a TypeEnv, actor: &'a Type) -> RcDoc< .map(|b| b.to_string()) .collect::>() .join(", "); - res.append(format!( - r#"pub const {}: SERVICE = SERVICE(Principal::from_slice(&[{}])); // {}"#, - config.service_name, slice, cid - )) + let id = RcDoc::text(format!( + "pub const CANISTER_ID : Principal = Principal::from_slice(&[{}]); // {}", + slice, cid + )); + let instance = match config.target { + Target::CanisterCall => format!( + "pub const {} : {} = {}(CANISTER_ID);", + config.service_name, struct_name, struct_name + ), + Target::Agent => "".to_string(), + Target::CanisterStub => unimplemented!(), + }; + res.append(id).append(RcDoc::hardline()).append(instance) } else { res } @@ -380,14 +411,15 @@ pub fn compile(config: &Config, env: &TypeEnv, actor: &Option) -> 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 {}::{{self, CandidType, Deserialize, Principal}}; +#![allow(dead_code, unused_imports)] +use {}::{{self, CandidType, Deserialize, Principal, Encode, Decode}}; "#, config.candid_crate ); let header = header + match &config.target { Target::CanisterCall => "use ic_cdk::api::call::CallResult as Result;\n", - Target::Agent => "type Result = std::result::Result;", + Target::Agent => "type Result = std::result::Result;\n", Target::CanisterStub => "", }; let (env, actor) = nominalize_all(env, actor); @@ -430,7 +462,7 @@ fn path_to_var(path: &[TypePath]) -> String { TypePath::Init => "init", }) .collect(); - name.join("_") + name.join("_").to_case(Case::Pascal) } // Convert structural typing to nominal typing to fit Rust's type system fn nominalize(env: &mut TypeEnv, path: &mut Vec, t: &Type) -> Type { @@ -508,6 +540,11 @@ fn nominalize(env: &mut TypeEnv, path: &mut Vec, t: &Type) -> Type { .into_iter() .enumerate() .map(|(i, ty)| { + let i = if i == 0 { + "".to_string() + } else { + i.to_string() + }; path.push(TypePath::Func(format!("arg{i}"))); let ty = nominalize(env, path, &ty); path.pop(); @@ -519,6 +556,11 @@ fn nominalize(env: &mut TypeEnv, path: &mut Vec, t: &Type) -> Type { .into_iter() .enumerate() .map(|(i, ty)| { + let i = if i == 0 { + "".to_string() + } else { + i.to_string() + }; path.push(TypePath::Func(format!("ret{i}"))); let ty = nominalize(env, path, &ty); path.pop(); diff --git a/rust/candid/src/types/internal.rs b/rust/candid/src/types/internal.rs index 726245ee..9cfa5a16 100644 --- a/rust/candid/src/types/internal.rs +++ b/rust/candid/src/types/internal.rs @@ -430,7 +430,7 @@ macro_rules! func { /// /// `service!{ "f": func!((HttpRequest) -> ()) }` expands to `Type(Rc::new(TypeInner::Service(...)))` macro_rules! service { - { $($meth:tt : $ty:expr);* } => {{ + { $($meth:tt : $ty:expr);* $(;)? } => {{ let mut ms = vec![ $(($meth.to_string(), $ty)),* ]; ms.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap()); if let Err(e) = $crate::utils::check_unique(ms.iter().map(|m| &m.0)) { diff --git a/rust/candid/src/types/reference.rs b/rust/candid/src/types/reference.rs index 568c987a..4a314319 100644 --- a/rust/candid/src/types/reference.rs +++ b/rust/candid/src/types/reference.rs @@ -31,7 +31,7 @@ macro_rules! define_function { fn _ty() -> $crate::types::Type { $crate::func!($($ty)+) } - fn idl_serialize(&self, serializer: S) -> Result<(), S::Error> + fn idl_serialize(&self, serializer: S) -> std::result::Result<(), S::Error> { self.0.idl_serialize(serializer) } @@ -60,7 +60,7 @@ macro_rules! define_service { fn _ty() -> $crate::types::Type { $crate::service!{$($ty)*} } - fn idl_serialize(&self, serializer: S) -> Result<(), S::Error> + fn idl_serialize(&self, serializer: S) -> std::result::Result<(), S::Error> { self.0.idl_serialize(serializer) } diff --git a/rust/candid/tests/assets/ok/actor.rs b/rust/candid/tests/assets/ok/actor.rs index 8bdb8b28..2f2dc6ad 100644 --- a/rust/candid/tests/assets/ok/actor.rs +++ b/rust/candid/tests/assets/ok/actor.rs @@ -1,17 +1,18 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; -candid::define_function!(pub f : (i8) -> (i8)); -candid::define_function!(pub h : (f) -> (f)); -pub type g = f; +candid::define_function!(pub F : (i8) -> (i8)); +candid::define_function!(pub H : (F) -> (F)); +pub type G = F; #[derive(CandidType, Deserialize)] -pub struct o(Option>); +pub struct O(Option>); -pub struct SERVICE(pub Principal); -impl SERVICE { - pub async fn f(&self, arg0: candid::Nat) -> Result<(h,)> { +pub struct Service(pub Principal); +impl Service { + 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,)> { @@ -20,8 +21,9 @@ impl SERVICE { 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 } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/class.rs b/rust/candid/tests/assets/ok/class.rs index 0a2b6bdb..17a8df14 100644 --- a/rust/candid/tests/assets/ok/class.rs +++ b/rust/candid/tests/assets/ok/class.rs @@ -1,13 +1,14 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; #[derive(CandidType, Deserialize)] pub struct List(Option<(candid::Int,Box,)>); -pub struct SERVICE(pub Principal); -impl SERVICE { +pub struct Service(pub Principal); +impl Service { pub async fn get(&self) -> Result<(List,)> { ic_cdk::call(self.0, "get", ()).await } @@ -15,4 +16,5 @@ impl SERVICE { ic_cdk::call(self.0, "set", (arg0,)).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/comment.rs b/rust/candid/tests/assets/ok/comment.rs index d5cd1448..4839a3f7 100644 --- a/rust/candid/tests/assets/ok/comment.rs +++ b/rust/candid/tests/assets/ok/comment.rs @@ -1,7 +1,8 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; -pub type id = u8; +pub type Id = u8; diff --git a/rust/candid/tests/assets/ok/cyclic.rs b/rust/candid/tests/assets/ok/cyclic.rs index dbd62c0e..cb7c2821 100644 --- a/rust/candid/tests/assets/ok/cyclic.rs +++ b/rust/candid/tests/assets/ok/cyclic.rs @@ -1,6 +1,7 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; pub type C = Box; @@ -11,8 +12,8 @@ pub struct A(Option); pub type Z = Box; pub type Y = Z; pub type X = Y; -pub struct SERVICE(pub Principal); -impl SERVICE { +pub struct Service(pub Principal); +impl Service { pub async fn f( &self, arg0: A, @@ -25,4 +26,5 @@ impl SERVICE { ic_cdk::call(self.0, "f", (arg0,arg1,arg2,arg3,arg4,arg5,)).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/escape.rs b/rust/candid/tests/assets/ok/escape.rs index aa4ca474..597a887c 100644 --- a/rust/candid/tests/assets/ok/escape.rs +++ b/rust/candid/tests/assets/ok/escape.rs @@ -1,10 +1,11 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; #[derive(CandidType, Deserialize)] -pub struct t { +pub struct T { #[serde(rename="\"")] _34_: candid::Nat, #[serde(rename="\'")] @@ -15,10 +16,11 @@ pub struct t { _1020746185_: candid::Nat, } -pub struct SERVICE(pub Principal); -impl SERVICE { - pub async fn _2635468193_(&self, arg0: t) -> Result<()> { +pub struct Service(pub Principal); +impl Service { + pub async fn _2635468193_(&self, arg0: T) -> Result<()> { ic_cdk::call(self.0, "\n\'\"\'\'\"\"\r\t", (arg0,)).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/example.rs b/rust/candid/tests/assets/ok/example.rs index 7b65ee05..e054f72f 100644 --- a/rust/candid/tests/assets/ok/example.rs +++ b/rust/candid/tests/assets/ok/example.rs @@ -1,90 +1,92 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; #[derive(CandidType, Deserialize)] -pub struct node { head: candid::Nat, tail: Box } +pub struct Node { head: candid::Nat, tail: Box } #[derive(CandidType, Deserialize)] -pub struct list(Option); +pub struct List(Option); -pub type my_type = Principal; +pub type MyType = Principal; #[derive(CandidType, Deserialize)] -pub struct List_inner { head: candid::Int, tail: Box } +pub struct ListInner { head: candid::Int, tail: Box } #[derive(CandidType, Deserialize)] -pub struct List(Option); +pub struct List(Option); #[derive(CandidType, Deserialize)] -pub struct nested_3 { _0_: candid::Nat, _42_: candid::Nat, _43_: u8 } +pub struct Nested3 { _0_: candid::Nat, _42_: candid::Nat, _43_: u8 } #[derive(CandidType, Deserialize)] -pub enum nested_41 { _42_, A, B, C } +pub enum Nested41 { _42_, A, B, C } #[derive(CandidType, Deserialize)] -pub struct nested { +pub struct Nested { _0_: candid::Nat, _1_: candid::Nat, _2_: (candid::Nat,candid::Int,), - _3_: nested_3, + _3_: Nested3, _40_: candid::Nat, - _41_: nested_41, + _41_: Nested41, _42_: candid::Nat, } -candid::define_service!(pub broker_find_ret0 : { +candid::define_service!(pub BrokerFindRet : { "current" : candid::func!(() -> (u32)); "up" : candid::func!(() -> ()); }); -candid::define_service!(pub broker : { - "find" : candid::func!((String) -> (broker_find_ret0)); +candid::define_service!(pub Broker : { + "find" : candid::func!((String) -> (BrokerFindRet)); }); #[derive(CandidType, Deserialize)] -pub enum h_arg1 { A(candid::Nat), B(Option) } +pub enum HArg1 { A(candid::Nat), B(Option) } #[derive(CandidType, Deserialize)] -pub struct h_ret0_42 {} +pub struct HRet42 {} #[derive(CandidType, Deserialize)] -pub struct h_ret0 { _42_: h_ret0_42, id: candid::Nat } +pub struct HRet { _42_: HRet42, id: candid::Nat } -candid::define_function!(pub f_arg1 : (i32) -> (i64)); -candid::define_function!(pub f : (List, f_arg1) -> (Option)); +candid::define_function!(pub FArg1 : (i32) -> (i64)); +candid::define_function!(pub F : (List, FArg1) -> (Option)); #[derive(CandidType, Deserialize)] -pub struct b (candid::Int,candid::Nat,); +pub struct B (candid::Int,candid::Nat,); #[derive(CandidType, Deserialize)] -pub enum a { a, b(b) } +pub enum A { #[serde(rename="a")] A, #[serde(rename="b")] B(B) } -pub struct SERVICE(pub Principal); -impl SERVICE { +pub struct Service(pub Principal); +impl Service { pub async fn f( &self, - arg0: list, + arg0: List, arg1: serde_bytes::ByteBuf, arg2: Option, ) -> Result<()> { ic_cdk::call(self.0, "f", (arg0,arg1,arg2,)).await } pub async fn g( &self, - arg0: my_type, + arg0: MyType, arg1: List, arg2: Option, - arg3: nested, - ) -> Result<(candid::Int,broker,)> { + arg3: Nested, + ) -> Result<(candid::Int,Broker,)> { ic_cdk::call(self.0, "g", (arg0,arg1,arg2,arg3,)).await } pub async fn h( &self, arg0: Vec>, - arg1: h_arg1, + arg1: HArg1, arg2: Option, - ) -> Result<(h_ret0,)> { ic_cdk::call(self.0, "h", (arg0,arg1,arg2,)).await } - pub async fn i(&self, arg0: List, arg1: f_arg1) -> Result<(Option,)> { + ) -> Result<(HRet,)> { ic_cdk::call(self.0, "h", (arg0,arg1,arg2,)).await } + pub async fn i(&self, arg0: List, arg1: FArg1) -> Result<(Option,)> { ic_cdk::call(self.0, "i", (arg0,arg1,)).await } - pub async fn x(&self, arg0: a, arg1: b) -> Result<(Option,Option,)> { + pub async fn x(&self, arg0: A, arg1: B) -> Result<(Option,Option,)> { ic_cdk::call(self.0, "x", (arg0,arg1,)).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/fieldnat.rs b/rust/candid/tests/assets/ok/fieldnat.rs index b3e973aa..af18e604 100644 --- a/rust/candid/tests/assets/ok/fieldnat.rs +++ b/rust/candid/tests/assets/ok/fieldnat.rs @@ -1,57 +1,59 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; #[derive(CandidType, Deserialize)] -pub struct bar_arg0 { #[serde(rename="2")] _50_: candid::Int } +pub struct BarArg { #[serde(rename="2")] _50_: candid::Int } #[derive(CandidType, Deserialize)] -pub enum bar_ret0 { e20, e30 } +pub enum BarRet { #[serde(rename="e20")] E20, #[serde(rename="e30")] E30 } #[derive(CandidType, Deserialize)] -pub struct baz_arg0 { _2_: candid::Int, #[serde(rename="2")] _50_: candid::Nat } +pub struct BazArg { _2_: candid::Int, #[serde(rename="2")] _50_: candid::Nat } #[derive(CandidType, Deserialize)] -pub struct baz_ret0 {} +pub struct BazRet {} #[derive(CandidType, Deserialize)] -pub struct tuple (String,String,); +pub struct Tuple (String,String,); #[derive(CandidType, Deserialize)] -pub struct non_tuple { _1_: String, _2_: String } +pub struct NonTuple { _1_: String, _2_: String } #[derive(CandidType, Deserialize)] -pub enum bib_ret0 { _0_(candid::Int) } +pub enum BibRet { _0_(candid::Int) } #[derive(CandidType, Deserialize)] -pub struct foo_arg0 { _2_: candid::Int } +pub struct FooArg { _2_: candid::Int } #[derive(CandidType, Deserialize)] -pub struct foo_ret0 { _2_: candid::Int, _2: candid::Int } +pub struct FooRet { _2_: candid::Int, _2: candid::Int } -pub struct SERVICE(pub Principal); -impl SERVICE { +pub struct Service(pub Principal); +impl Service { 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: bar_arg0) -> Result<(bar_ret0,)> { + 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,),) > { ic_cdk::call(self.0, "bas", (arg0,)).await } - pub async fn baz(&self, arg0: baz_arg0) -> Result<(baz_ret0,)> { + 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<(non_tuple,)> { + 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<(bib_ret0,)> { + pub async fn bib(&self, arg0: (candid::Int,)) -> Result<(BibRet,)> { ic_cdk::call(self.0, "bib", (arg0,)).await } - pub async fn foo(&self, arg0: foo_arg0) -> Result<(foo_ret0,)> { + pub async fn foo(&self, arg0: FooArg) -> Result<(FooRet,)> { ic_cdk::call(self.0, "foo", (arg0,)).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/keyword.rs b/rust/candid/tests/assets/ok/keyword.rs index f61a15ca..fc4e9bc5 100644 --- a/rust/candid/tests/assets/ok/keyword.rs +++ b/rust/candid/tests/assets/ok/keyword.rs @@ -1,78 +1,81 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; #[derive(CandidType, Deserialize)] -pub struct o(Option>); +pub struct O(Option>); #[derive(CandidType, Deserialize)] -pub struct field_arg0 { test: u16, _1291438163_: u8 } +pub struct FieldArg { test: u16, _1291438163_: u8 } #[derive(CandidType, Deserialize)] -pub struct field_ret0 {} +pub struct FieldRet {} #[derive(CandidType, Deserialize)] -pub struct fieldnat_arg0 { +pub struct FieldnatArg { _2_: candid::Int, #[serde(rename="2")] _50_: candid::Nat, } #[derive(CandidType, Deserialize)] -pub struct node { head: candid::Nat, tail: Box } +pub struct Node { head: candid::Nat, tail: Box } #[derive(CandidType, Deserialize)] -pub struct list(Option); +pub struct List(Option); #[derive(CandidType, Deserialize)] -pub enum r#if { - branch{ val: candid::Int, left: Box, right: Box }, - leaf(candid::Int), +pub enum If { + #[serde(rename="branch")] + Branch{ val: candid::Int, left: Box, right: Box }, + #[serde(rename="leaf")] + Leaf(candid::Int), } -candid::define_function!(pub stream_inner_next : () -> (stream) query); +candid::define_function!(pub StreamInnerNext : () -> (Stream) query); #[derive(CandidType, Deserialize)] -pub struct stream_inner { head: candid::Nat, next: stream_inner_next } +pub struct StreamInner { head: candid::Nat, next: StreamInnerNext } #[derive(CandidType, Deserialize)] -pub struct stream(Option); +pub struct Stream(Option); -candid::define_service!(pub r#return : { - "f" : t::ty(); - "g" : candid::func!((list) -> (r#if, stream)); +candid::define_service!(pub Return : { + "f" : T::ty(); + "g" : candid::func!((List) -> (If, Stream)); }); -candid::define_function!(pub t : (r#return) -> ()); +candid::define_function!(pub T : (Return) -> ()); #[derive(CandidType, Deserialize)] -pub enum variant_arg0 { A, B, C, D(f64) } +pub enum VariantArg { A, B, C, D(f64) } -pub struct SERVICE(pub Principal); -impl SERVICE { - pub async fn Oneway(&self) -> Result<()> { +pub struct Service(pub Principal); +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: field_arg0) -> Result<(field_ret0,)> { + pub async fn field(&self, arg0: FieldArg) -> Result<(FieldRet,)> { ic_cdk::call(self.0, "field", (arg0,)).await } - pub async fn fieldnat(&self, arg0: fieldnat_arg0) -> Result< - ((candid::Int,),) - > { ic_cdk::call(self.0, "fieldnat", (arg0,)).await } + 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<()> { 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,) > { 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: r#return) -> Result<()> { + pub async fn service(&self, arg0: Return) -> Result<()> { ic_cdk::call(self.0, "service", (arg0,)).await } pub async fn tuple( @@ -81,8 +84,9 @@ impl SERVICE { ) -> Result<((candid::Int,u8,),)> { ic_cdk::call(self.0, "tuple", (arg0,)).await } - pub async fn variant(&self, arg0: variant_arg0) -> Result<()> { + pub async fn variant(&self, arg0: VariantArg) -> Result<()> { ic_cdk::call(self.0, "variant", (arg0,)).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/management.rs b/rust/candid/tests/assets/ok/management.rs index c927f102..48d784ca 100644 --- a/rust/candid/tests/assets/ok/management.rs +++ b/rust/candid/tests/assets/ok/management.rs @@ -1,66 +1,82 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; type Result = std::result::Result; + #[derive(CandidType, Deserialize)] -pub enum bitcoin_network { mainnet, testnet } +pub enum BitcoinNetwork { + #[serde(rename="mainnet")] + Mainnet, + #[serde(rename="testnet")] + Testnet, +} -pub type bitcoin_address = String; +pub type BitcoinAddress = String; #[derive(CandidType, Deserialize)] -pub struct get_balance_request { - network: bitcoin_network, - address: bitcoin_address, +pub struct GetBalanceRequest { + network: BitcoinNetwork, + address: BitcoinAddress, min_confirmations: Option, } -pub type satoshi = u64; +pub type Satoshi = u64; #[derive(CandidType, Deserialize)] -pub struct get_current_fee_percentiles_request { network: bitcoin_network } +pub struct GetCurrentFeePercentilesRequest { network: BitcoinNetwork } -pub type millisatoshi_per_byte = u64; +pub type MillisatoshiPerByte = u64; #[derive(CandidType, Deserialize)] -pub enum get_utxos_request_filter_inner { - page(serde_bytes::ByteBuf), - min_confirmations(u32), +pub enum GetUtxosRequestFilterInner { + #[serde(rename="page")] + Page(serde_bytes::ByteBuf), + #[serde(rename="min_confirmations")] + MinConfirmations(u32), } #[derive(CandidType, Deserialize)] -pub struct get_utxos_request { - network: bitcoin_network, - filter: Option, - address: bitcoin_address, +pub struct GetUtxosRequest { + network: BitcoinNetwork, + filter: Option, + address: BitcoinAddress, } -pub type block_hash = serde_bytes::ByteBuf; +pub type BlockHash = serde_bytes::ByteBuf; #[derive(CandidType, Deserialize)] -pub struct outpoint { txid: serde_bytes::ByteBuf, vout: u32 } +pub struct Outpoint { txid: serde_bytes::ByteBuf, vout: u32 } #[derive(CandidType, Deserialize)] -pub struct utxo { height: u32, value: satoshi, outpoint: outpoint } +pub struct Utxo { height: u32, value: Satoshi, outpoint: Outpoint } #[derive(CandidType, Deserialize)] -pub struct get_utxos_response { +pub struct GetUtxosResponse { next_page: Option, tip_height: u32, - tip_block_hash: block_hash, - utxos: Vec, + tip_block_hash: BlockHash, + utxos: Vec, } #[derive(CandidType, Deserialize)] -pub struct send_transaction_request { +pub struct SendTransactionRequest { transaction: serde_bytes::ByteBuf, - network: bitcoin_network, + network: BitcoinNetwork, } -pub type canister_id = Principal; +pub type CanisterId = Principal; #[derive(CandidType, Deserialize)] -pub struct canister_status_arg0 { canister_id: canister_id } +pub struct CanisterStatusArg { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub enum canister_status_ret0_status { stopped, stopping, running } +pub enum CanisterStatusRetStatus { + #[serde(rename="stopped")] + Stopped, + #[serde(rename="stopping")] + Stopping, + #[serde(rename="running")] + Running, +} #[derive(CandidType, Deserialize)] -pub struct definite_canister_settings { +pub struct DefiniteCanisterSettings { freezing_threshold: candid::Nat, controllers: Vec, memory_allocation: candid::Nat, @@ -68,17 +84,17 @@ pub struct definite_canister_settings { } #[derive(CandidType, Deserialize)] -pub struct canister_status_ret0 { - status: canister_status_ret0_status, +pub struct CanisterStatusRet { + status: CanisterStatusRetStatus, memory_size: candid::Nat, cycles: candid::Nat, - settings: definite_canister_settings, + settings: DefiniteCanisterSettings, idle_cycles_burned_per_day: candid::Nat, module_hash: Option, } #[derive(CandidType, Deserialize)] -pub struct canister_settings { +pub struct CanisterSettings { freezing_threshold: Option, controllers: Option>, memory_allocation: Option, @@ -86,284 +102,267 @@ pub struct canister_settings { } #[derive(CandidType, Deserialize)] -pub struct create_canister_arg0 { settings: Option } +pub struct CreateCanisterArg { settings: Option } #[derive(CandidType, Deserialize)] -pub struct create_canister_ret0 { canister_id: canister_id } +pub struct CreateCanisterRet { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub struct delete_canister_arg0 { canister_id: canister_id } +pub struct DeleteCanisterArg { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub struct deposit_cycles_arg0 { canister_id: canister_id } +pub struct DepositCyclesArg { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub enum ecdsa_curve { secp256k1 } +pub enum EcdsaCurve { #[serde(rename="secp256k1")] Secp256K1 } #[derive(CandidType, Deserialize)] -pub struct ecdsa_public_key_arg0_key_id { name: String, curve: ecdsa_curve } +pub struct EcdsaPublicKeyArgKeyId { name: String, curve: EcdsaCurve } #[derive(CandidType, Deserialize)] -pub struct ecdsa_public_key_arg0 { - key_id: ecdsa_public_key_arg0_key_id, - canister_id: Option, +pub struct EcdsaPublicKeyArg { + key_id: EcdsaPublicKeyArgKeyId, + canister_id: Option, derivation_path: Vec, } #[derive(CandidType, Deserialize)] -pub struct ecdsa_public_key_ret0 { +pub struct EcdsaPublicKeyRet { public_key: serde_bytes::ByteBuf, chain_code: serde_bytes::ByteBuf, } #[derive(CandidType, Deserialize)] -pub enum http_request_arg0_method { get, head, post } +pub enum HttpRequestArgMethod { + #[serde(rename="get")] + Get, + #[serde(rename="head")] + Head, + #[serde(rename="post")] + Post, +} #[derive(CandidType, Deserialize)] -pub struct http_header { value: String, name: String } +pub struct HttpHeader { value: String, name: String } #[derive(CandidType, Deserialize)] -pub struct http_response { +pub struct HttpResponse { status: candid::Nat, body: serde_bytes::ByteBuf, - headers: Vec, + headers: Vec, } #[derive(CandidType, Deserialize)] -pub struct http_request_arg0_transform_inner_function_arg0 { +pub struct HttpRequestArgTransformInnerFunctionArg { context: serde_bytes::ByteBuf, - response: http_response, + response: HttpResponse, } -candid::define_function!(pub http_request_arg0_transform_inner_function : ( - http_request_arg0_transform_inner_function_arg0, - ) -> (http_response) query); +candid::define_function!(pub HttpRequestArgTransformInnerFunction : ( + HttpRequestArgTransformInnerFunctionArg, + ) -> (HttpResponse) query); #[derive(CandidType, Deserialize)] -pub struct http_request_arg0_transform_inner { - function: http_request_arg0_transform_inner_function, +pub struct HttpRequestArgTransformInner { + function: HttpRequestArgTransformInnerFunction, context: serde_bytes::ByteBuf, } #[derive(CandidType, Deserialize)] -pub struct http_request_arg0 { +pub struct HttpRequestArg { url: String, - method: http_request_arg0_method, + method: HttpRequestArgMethod, max_response_bytes: Option, body: Option, - transform: Option, - headers: Vec, + transform: Option, + headers: Vec, } -pub type wasm_module = serde_bytes::ByteBuf; +pub type WasmModule = serde_bytes::ByteBuf; #[derive(CandidType, Deserialize)] -pub enum install_code_arg0_mode { reinstall, upgrade, install } +pub enum InstallCodeArgMode { + #[serde(rename="reinstall")] + Reinstall, + #[serde(rename="upgrade")] + Upgrade, + #[serde(rename="install")] + Install, +} #[derive(CandidType, Deserialize)] -pub struct install_code_arg0 { +pub struct InstallCodeArg { arg: serde_bytes::ByteBuf, - wasm_module: wasm_module, - mode: install_code_arg0_mode, - canister_id: canister_id, + wasm_module: WasmModule, + mode: InstallCodeArgMode, + canister_id: CanisterId, } #[derive(CandidType, Deserialize)] -pub struct provisional_create_canister_with_cycles_arg0 { - settings: Option, - specified_id: Option, +pub struct ProvisionalCreateCanisterWithCyclesArg { + settings: Option, + specified_id: Option, amount: Option, } #[derive(CandidType, Deserialize)] -pub struct provisional_create_canister_with_cycles_ret0 { - canister_id: canister_id, -} +pub struct ProvisionalCreateCanisterWithCyclesRet { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub struct provisional_top_up_canister_arg0 { - canister_id: canister_id, +pub struct ProvisionalTopUpCanisterArg { + canister_id: CanisterId, amount: candid::Nat, } #[derive(CandidType, Deserialize)] -pub struct sign_with_ecdsa_arg0_key_id { name: String, curve: ecdsa_curve } +pub struct SignWithEcdsaArgKeyId { name: String, curve: EcdsaCurve } #[derive(CandidType, Deserialize)] -pub struct sign_with_ecdsa_arg0 { - key_id: sign_with_ecdsa_arg0_key_id, +pub struct SignWithEcdsaArg { + key_id: SignWithEcdsaArgKeyId, derivation_path: Vec, message_hash: serde_bytes::ByteBuf, } #[derive(CandidType, Deserialize)] -pub struct sign_with_ecdsa_ret0 { signature: serde_bytes::ByteBuf } +pub struct SignWithEcdsaRet { signature: serde_bytes::ByteBuf } #[derive(CandidType, Deserialize)] -pub struct start_canister_arg0 { canister_id: canister_id } +pub struct StartCanisterArg { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub struct stop_canister_arg0 { canister_id: canister_id } +pub struct StopCanisterArg { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub struct uninstall_code_arg0 { canister_id: canister_id } +pub struct UninstallCodeArg { canister_id: CanisterId } #[derive(CandidType, Deserialize)] -pub struct update_settings_arg0 { +pub struct UpdateSettingsArg { canister_id: Principal, - settings: canister_settings, + settings: CanisterSettings, } -pub struct SERVICE(pub Principal); -impl SERVICE { - pub async fn bitcoin_get_balance( - &self, agent: &ic_agent::Agent, - arg0: get_balance_request, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "bitcoin_get_balance").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, satoshi)?) +pub struct Service<'a>(pub Principal, pub &'a ic_agent::Agent); +impl<'a> Service<'a> { + pub async fn bitcoin_get_balance(&self, arg0: GetBalanceRequest) -> Result< + Satoshi + > { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "bitcoin_get_balance").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, Satoshi)?) } pub async fn bitcoin_get_current_fee_percentiles( - &self, agent: &ic_agent::Agent, - arg0: get_current_fee_percentiles_request, - ) -> Result> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "bitcoin_get_current_fee_percentiles").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, Vec)?) + &self, + arg0: GetCurrentFeePercentilesRequest, + ) -> Result> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "bitcoin_get_current_fee_percentiles").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, Vec)?) } - pub async fn bitcoin_get_utxos( - &self, agent: &ic_agent::Agent, - arg0: get_utxos_request, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "bitcoin_get_utxos").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, get_utxos_response)?) + pub async fn bitcoin_get_utxos(&self, arg0: GetUtxosRequest) -> Result< + GetUtxosResponse + > { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "bitcoin_get_utxos").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, GetUtxosResponse)?) } pub async fn bitcoin_send_transaction( - &self, agent: &ic_agent::Agent, - arg0: send_transaction_request, + &self, + arg0: SendTransactionRequest, ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "bitcoin_send_transaction").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "bitcoin_send_transaction").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } - pub async fn canister_status( - &self, agent: &ic_agent::Agent, - arg0: canister_status_arg0, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "canister_status").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, canister_status_ret0)?) + pub async fn canister_status(&self, arg0: CanisterStatusArg) -> Result< + CanisterStatusRet + > { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "canister_status").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, CanisterStatusRet)?) } - pub async fn create_canister( - &self, agent: &ic_agent::Agent, - arg0: create_canister_arg0, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "create_canister").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, create_canister_ret0)?) + pub async fn create_canister(&self, arg0: CreateCanisterArg) -> Result< + CreateCanisterRet + > { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "create_canister").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, CreateCanisterRet)?) } - pub async fn delete_canister( - &self, agent: &ic_agent::Agent, - arg0: delete_canister_arg0, - ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "delete_canister").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + pub async fn delete_canister(&self, arg0: DeleteCanisterArg) -> Result<()> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "delete_canister").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } - pub async fn deposit_cycles( - &self, agent: &ic_agent::Agent, - arg0: deposit_cycles_arg0, - ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "deposit_cycles").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + pub async fn deposit_cycles(&self, arg0: DepositCyclesArg) -> Result<()> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "deposit_cycles").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } - pub async fn ecdsa_public_key( - &self, agent: &ic_agent::Agent, - arg0: ecdsa_public_key_arg0, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "ecdsa_public_key").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, ecdsa_public_key_ret0)?) + pub async fn ecdsa_public_key(&self, arg0: EcdsaPublicKeyArg) -> Result< + EcdsaPublicKeyRet + > { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "ecdsa_public_key").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, EcdsaPublicKeyRet)?) } - pub async fn http_request( - &self, agent: &ic_agent::Agent, - arg0: http_request_arg0, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "http_request").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, http_response)?) + pub async fn http_request(&self, arg0: HttpRequestArg) -> Result< + HttpResponse + > { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "http_request").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, HttpResponse)?) } - pub async fn install_code( - &self, agent: &ic_agent::Agent, - arg0: install_code_arg0, - ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "install_code").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + pub async fn install_code(&self, arg0: InstallCodeArg) -> Result<()> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "install_code").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } pub async fn provisional_create_canister_with_cycles( - &self, agent: &ic_agent::Agent, - arg0: provisional_create_canister_with_cycles_arg0, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "provisional_create_canister_with_cycles").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, provisional_create_canister_with_cycles_ret0)?) + &self, + arg0: ProvisionalCreateCanisterWithCyclesArg, + ) -> Result { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "provisional_create_canister_with_cycles").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, ProvisionalCreateCanisterWithCyclesRet)?) } pub async fn provisional_top_up_canister( - &self, agent: &ic_agent::Agent, - arg0: provisional_top_up_canister_arg0, + &self, + arg0: ProvisionalTopUpCanisterArg, ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "provisional_top_up_canister").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "provisional_top_up_canister").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) + } + pub async fn raw_rand(&self) -> Result { + let args = Encode!()?; + let bytes = self.1.update(&self.0, "raw_rand").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, serde_bytes::ByteBuf)?) } - pub async fn raw_rand(&self, agent: &ic_agent::Agent) -> Result< - serde_bytes::ByteBuf + pub async fn sign_with_ecdsa(&self, arg0: SignWithEcdsaArg) -> Result< + SignWithEcdsaRet > { - let args = candid::Encode!()?; - let bytes = agent.update(self.0, "raw_rand").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, serde_bytes::ByteBuf)?) + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "sign_with_ecdsa").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes, SignWithEcdsaRet)?) } - pub async fn sign_with_ecdsa( - &self, agent: &ic_agent::Agent, - arg0: sign_with_ecdsa_arg0, - ) -> Result { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "sign_with_ecdsa").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes, sign_with_ecdsa_ret0)?) + pub async fn start_canister(&self, arg0: StartCanisterArg) -> Result<()> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "start_canister").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } - pub async fn start_canister( - &self, agent: &ic_agent::Agent, - arg0: start_canister_arg0, - ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "start_canister").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + pub async fn stop_canister(&self, arg0: StopCanisterArg) -> Result<()> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "stop_canister").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } - pub async fn stop_canister( - &self, agent: &ic_agent::Agent, - arg0: stop_canister_arg0, - ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "stop_canister").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + pub async fn uninstall_code(&self, arg0: UninstallCodeArg) -> Result<()> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "uninstall_code").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } - pub async fn uninstall_code( - &self, agent: &ic_agent::Agent, - arg0: uninstall_code_arg0, - ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "uninstall_code").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) - } - pub async fn update_settings( - &self, agent: &ic_agent::Agent, - arg0: update_settings_arg0, - ) -> Result<()> { - let args = candid::Encode!(arg0)?; - let bytes = agent.update(self.0, "update_settings").with_arg(args).call_and_wait().await?; - Ok(candid::Decode!(&bytes)?) + pub async fn update_settings(&self, arg0: UpdateSettingsArg) -> Result<()> { + let args = Encode!(&arg0)?; + let bytes = self.1.update(&self.0, "update_settings").with_arg(args).call_and_wait().await?; + Ok(Decode!(&bytes)?) } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa + diff --git a/rust/candid/tests/assets/ok/recursion.rs b/rust/candid/tests/assets/ok/recursion.rs index 9660947c..f773d7f7 100644 --- a/rust/candid/tests/assets/ok/recursion.rs +++ b/rust/candid/tests/assets/ok/recursion.rs @@ -1,43 +1,47 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; -candid::define_function!(pub t : (s) -> ()); +candid::define_function!(pub T : (S) -> ()); #[derive(CandidType, Deserialize)] -pub struct node { head: candid::Nat, tail: Box } +pub struct Node { head: candid::Nat, tail: Box } #[derive(CandidType, Deserialize)] -pub struct list(Option); +pub struct List(Option); pub type A = Box; #[derive(CandidType, Deserialize)] pub struct B(Option); #[derive(CandidType, Deserialize)] -pub enum tree { - branch{ val: candid::Int, left: Box, right: Box }, - leaf(candid::Int), +pub enum Tree { + #[serde(rename="branch")] + Branch{ val: candid::Int, left: Box, right: Box }, + #[serde(rename="leaf")] + Leaf(candid::Int), } -candid::define_function!(pub stream_inner_next : () -> (stream) query); +candid::define_function!(pub StreamInnerNext : () -> (Stream) query); #[derive(CandidType, Deserialize)] -pub struct stream_inner { head: candid::Nat, next: stream_inner_next } +pub struct StreamInner { head: candid::Nat, next: StreamInnerNext } #[derive(CandidType, Deserialize)] -pub struct stream(Option); +pub struct Stream(Option); -candid::define_service!(pub s : { - "f" : t::ty(); - "g" : candid::func!((list) -> (B, tree, stream)); +candid::define_service!(pub S : { + "f" : T::ty(); + "g" : candid::func!((List) -> (B, Tree, Stream)); }); -pub struct SERVICE(pub Principal); -impl SERVICE { - pub async fn f(&self, arg0: s) -> Result<()> { +pub struct Service(pub Principal); +impl Service { + pub async fn f(&self, arg0: S) -> Result<()> { ic_cdk::call(self.0, "f", (arg0,)).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 const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/recursive_class.rs b/rust/candid/tests/assets/ok/recursive_class.rs index 50c14176..3edd2249 100644 --- a/rust/candid/tests/assets/ok/recursive_class.rs +++ b/rust/candid/tests/assets/ok/recursive_class.rs @@ -1,13 +1,15 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; -candid::define_service!(pub s : { "next" : candid::func!(() -> (s)) }); -pub struct SERVICE(pub Principal); -impl SERVICE { - pub async fn next(&self) -> Result<(s,)> { +candid::define_service!(pub S : { "next" : candid::func!(() -> (S)) }); +pub struct Service(pub Principal); +impl Service { + pub async fn next(&self) -> Result<(S,)> { ic_cdk::call(self.0, "next", ()).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/service.rs b/rust/candid/tests/assets/ok/service.rs index 6050e8f4..d4ac89b4 100644 --- a/rust/candid/tests/assets/ok/service.rs +++ b/rust/candid/tests/assets/ok/service.rs @@ -1,27 +1,34 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; candid::define_function!(pub Func : () -> (Service)); candid::define_service!(pub Service : { "f" : Func::ty() }); pub type Service2 = Box; #[derive(CandidType, Deserialize)] -pub enum asVariant_ret0 { a(Service2), b{ f: Option } } +pub enum AsVariantRet { + #[serde(rename="a")] + A(Service2), + #[serde(rename="b")] + B{ f: Option }, +} -pub struct SERVICE(pub Principal); -impl SERVICE { - pub async fn asArray(&self) -> Result<(Vec,Vec,)> { +pub struct Service(pub Principal); +impl Service { + pub async fn as_array(&self) -> Result<(Vec,Vec,)> { ic_cdk::call(self.0, "asArray", ()).await } - pub async fn asPrincipal(&self) -> Result<(Service2,Func,)> { + pub async fn as_principal(&self) -> Result<(Service2,Func,)> { ic_cdk::call(self.0, "asPrincipal", ()).await } - pub async fn asRecord(&self) -> Result<((Service2,Option,Func,),)> { + pub async fn as_record(&self) -> Result<((Service2,Option,Func,),)> { ic_cdk::call(self.0, "asRecord", ()).await } - pub async fn asVariant(&self) -> Result<(asVariant_ret0,)> { + pub async fn as_variant(&self) -> Result<(AsVariantRet,)> { ic_cdk::call(self.0, "asVariant", ()).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/assets/ok/unicode.rs b/rust/candid/tests/assets/ok/unicode.rs index 7c473496..73dd059d 100644 --- a/rust/candid/tests/assets/ok/unicode.rs +++ b/rust/candid/tests/assets/ok/unicode.rs @@ -1,6 +1,7 @@ // 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, Principal}; +#![allow(dead_code, unused_imports)] +use candid::{self, CandidType, Deserialize, Principal, Encode, Decode}; use ic_cdk::api::call::CallResult as Result; #[derive(CandidType, Deserialize)] @@ -27,8 +28,8 @@ pub enum B { _3099250646_, } -pub struct SERVICE(pub Principal); -impl SERVICE { +pub struct Service(pub Principal); +impl Service { pub async fn _0_(&self, arg0: candid::Nat) -> Result<(candid::Nat,)> { ic_cdk::call(self.0, "", (arg0,)).await } @@ -42,4 +43,5 @@ impl SERVICE { (candid::Nat,) > { ic_cdk::call(self.0, "👀", (arg0,)).await } } -pub const service: SERVICE = SERVICE(Principal::from_slice(&[])); // aaaaa-aa +pub const CANISTER_ID : Principal = Principal::from_slice(&[]); // aaaaa-aa +pub const service : Service = Service(CANISTER_ID); diff --git a/rust/candid/tests/serde.rs b/rust/candid/tests/serde.rs index c0722351..5fb612e8 100644 --- a/rust/candid/tests/serde.rs +++ b/rust/candid/tests/serde.rs @@ -620,6 +620,14 @@ fn test_variant() { let bytes = encode(&E::Bar(true, 42.into())); test_decode(&bytes, &None::); check_error(|| test_decode(&bytes, &Unit::Bar), "Subtyping error"); + + #[derive(PartialEq, Debug, Deserialize, CandidType)] + enum T { + A(), + B {}, + C, + } + all_check(T::A(), "4449444c026b0341014201437f6c00010000"); } #[test] diff --git a/rust/candid_derive/Cargo.toml b/rust/candid_derive/Cargo.toml index d4cf81a3..029f053e 100644 --- a/rust/candid_derive/Cargo.toml +++ b/rust/candid_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "candid_derive" -version = "0.6.2" +version = "0.6.3" edition = "2021" authors = ["DFINITY Team"] description = "Macros implementation of #[derive(CandidType)] for the Candid." diff --git a/rust/candid_derive/src/derive.rs b/rust/candid_derive/src/derive.rs index a83cf3f5..113e9777 100644 --- a/rust/candid_derive/src/derive.rs +++ b/rust/candid_derive/src/derive.rs @@ -17,7 +17,7 @@ pub(crate) fn derive_idl_type( let (ty_body, ser_body) = match input.data { Data::Enum(ref data) => enum_from_ast(&name, &data.variants, custom_candid_path), Data::Struct(ref data) => { - let (ty, idents, is_bytes) = struct_from_ast(&data.fields, custom_candid_path); + let (ty, idents, is_bytes, _) = struct_from_ast(&data.fields, custom_candid_path); (ty, serialize_struct(&idents, &is_bytes, custom_candid_path)) } Data::Union(_) => unimplemented!("doesn't derive union type"), @@ -37,7 +37,7 @@ pub(crate) fn derive_idl_type( } } }; - //panic!(gen.to_string()); + //panic!("{}", gen.to_string()); gen } @@ -48,6 +48,7 @@ struct Variant { ty: TokenStream, members: Vec, with_bytes: bool, + style: Style, } enum Style { Struct, @@ -55,17 +56,8 @@ enum Style { Unit, } impl Variant { - fn style(&self) -> Style { - if self.members.is_empty() { - return Style::Unit; - }; - match self.members[0] { - Ident::Named(_) => Style::Struct, - Ident::Unnamed(_) => Style::Tuple, - } - } fn to_pattern(&self) -> (TokenStream, Vec) { - match self.style() { + match self.style { Style::Unit => (quote! {}, Vec::new()), Style::Struct => { let id: Vec<_> = self.members.iter().map(|ident| ident.to_token()).collect(); @@ -114,7 +106,7 @@ fn enum_from_ast( ), None => (id.clone(), idl_hash(&id.unraw().to_string())), }; - let (ty, idents, _) = struct_from_ast(&variant.fields, custom_candid_path); + let (ty, idents, _, style) = struct_from_ast(&variant.fields, custom_candid_path); Variant { real_ident: id, renamed_ident, @@ -122,6 +114,7 @@ fn enum_from_ast( ty, members: idents, with_bytes: attrs.with_bytes, + style, } }) .collect(); @@ -203,7 +196,7 @@ fn serialize_struct( fn struct_from_ast( fields: &syn::Fields, custom_candid_path: &Option, -) -> (TokenStream, Vec, Vec) { +) -> (TokenStream, Vec, Vec, Style) { let candid = candid_path(custom_candid_path); match *fields { syn::Fields::Named(ref fields) => { @@ -212,18 +205,20 @@ fn struct_from_ast( quote! { #candid::types::TypeInner::Record(#fs).into() }, idents, is_bytes, + Style::Struct, ) } syn::Fields::Unnamed(ref fields) => { let (fs, idents, is_bytes) = fields_from_ast(&fields.unnamed, custom_candid_path); if idents.len() == 1 { let newtype = derive_type(&fields.unnamed[0].ty, custom_candid_path); - (quote! { #newtype }, idents, is_bytes) + (quote! { #newtype }, idents, is_bytes, Style::Tuple) } else { ( quote! { #candid::types::TypeInner::Record(#fs).into() }, idents, is_bytes, + Style::Tuple, ) } } @@ -231,6 +226,7 @@ fn struct_from_ast( quote! { #candid::types::TypeInner::Null.into() }, Vec::new(), Vec::new(), + Style::Unit, ), } } diff --git a/tools/didc/Cargo.toml b/tools/didc/Cargo.toml index e96a17b9..f1cf5e91 100644 --- a/tools/didc/Cargo.toml +++ b/tools/didc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "didc" -version = "0.3.4" +version = "0.3.5" authors = ["DFINITY Team"] edition = "2021"