candid icon indicating copy to clipboard operation
candid copied to clipboard

didc Rust output not generating records not referenced in service

Open lastmjs opened this issue 2 years ago • 3 comments

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.

lastmjs avatar Mar 29 '22 13:03 lastmjs

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;
  }
}

lastmjs avatar Mar 29 '22 13:03 lastmjs

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.

chenyan-dfinity avatar Mar 29 '22 23:03 chenyan-dfinity

func types are also not generated if not referenced in the service.

lastmjs avatar May 10 '22 14:05 lastmjs