candid
candid copied to clipboard
didc Rust output not generating records not referenced in service
didc is not generating Rust structs for records that aren't referenced from a service. This is undesirable for my use case, as I have Candid-defined records that may not be used in exported query or update functions, but which are needed to do cross-canister calls for example.
This Candid file:
type CanisterStatusArgs = record {
"canister_id": text;
};
type CanisterStatusResult = record {
"cycles": float64;
};
service: {
"initiateTransfer": () -> (int32);
"randomness": () -> (vec nat8);
"status": () -> (float64);
}
generates this Rust code with didc bind [path-to-did] -t rs:
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use ic_cdk::export::candid::{self, CandidType, Deserialize};
use ic_cdk::api::call::CallResult;
struct SERVICE(candid::Principal);
impl SERVICE{
pub async fn initiateTransfer(&self) -> CallResult<(i32,)> {
ic_cdk::call(self.0, "initiateTransfer", ()).await
}
pub async fn randomness(&self) -> CallResult<(Vec<u8>,)> {
ic_cdk::call(self.0, "randomness", ()).await
}
pub async fn status(&self) -> CallResult<(f64,)> {
ic_cdk::call(self.0, "status", ()).await
}
}
Notice that the CanisterStatusArgs and CanisterStatusResult records have not be translated into Rust structs.
But if you run didc bind [path-to-did] -t rs with this Candid:
type CanisterStatusArgs = record {
"canister_id": text;
};
type CanisterStatusResult = record {
"cycles": float64;
};
you do get the Rust structs:
// This is an experimental feature to generate Rust binding from Candid.
// You may want to manually adjust some of the types.
use ic_cdk::export::candid::{self, CandidType, Deserialize};
use ic_cdk::api::call::CallResult;
#[derive(CandidType, Deserialize)]
struct CanisterStatusArgs { canister_id: String }
#[derive(CandidType, Deserialize)]
struct CanisterStatusResult { cycles: f64 }
I would like the Rust structs to always be generated.
I've observed that didc bind [path-to-did] -t ts correctly outputs the records not referenced in the service:
import type { Principal } from '@dfinity/principal';
export interface CanisterStatusArgs { 'canister_id' : string }
export interface CanisterStatusResult { 'cycles' : number }
export interface _SERVICE {
'initiateTransfer' : () => Promise<number>,
'randomness' : () => Promise<Array<number>>,
'status' : () => Promise<number>,
}
as does didc bind [path-to-did] -t mo:
// This is a generated Motoko binding.
// Please use `import service "ic:canister_id"` instead to call canisters on the IC if possible.
module {
public type CanisterStatusArgs = { canister_id : Text };
public type CanisterStatusResult = { cycles : Float };
public type Self = actor {
initiateTransfer : shared () -> async Int32;
randomness : shared () -> async [Nat8];
status : shared () -> async Float;
}
}
Right. Both JS and Rust binding only generate types referenced in the service. Because both languages require identification of recursion points, and that algorithm requires sorting the type definitions from a root. I will have to think about if that can be relaxed.
A workaround is to separate the type definition in another did file without the main actor.
func types are also not generated if not referenced in the service.