Candid export not exporting correctly

I have structured my code in a similar manner to how OpenChat have structured theirs. That is to say, I have the following structure


- my_canister

-- api

---- Cargo.toml

---- src

-- impl

---- Cargo.toml

---- src

API defines mostly just structs, types and enums that are used to define the interface of my canister methods. a typical file inside API folder looks like this


use candid::CandidType;

use serde::{ Deserialize, Serialize };

use types::TokenInfo;

#[derive(CandidType, Serialize, Deserialize, Debug)]

pub struct Args {

pub token_list: Vec<(String, TokenInfo)>,

}

#[derive(CandidType, Serialize, Deserialize, Debug, PartialEq, Eq)]

pub enum Response {

Success,

InternalError(String),

}

Then, inside the impl folder, i use the types from the API folder like so


#[update(guard = "caller_is_governance_principal")]

#[trace]

pub async fn set_reward_token_types(args: Args) -> Response {

// ... more code

}

At the end of my lib.rs file inside the impl folder, I call export_candid!();

I then build and extract the candid information by doing something like this


cargo build --target wasm32-unknown-unknown --target-dir $BASE_CANISTER_PATH/my_canister/target --release --locked -p $1

candid-extractor "backend/canisters/my_canister/target/wasm32-unknown-unknown/release/my_canister_canister.wasm" > some_path_to_api_folder/can.did

The problem though, is that When i look at my extracted .did file i get very unexpected results.


type SnsMetadata = record {

url : opt text;

logo : opt text;

name : opt text;

description : opt text;

};

service : (record {}) -> {

set_reward_token_types : (record {}) -> (SnsMetadata);

}

What could be the problem here? I noticed that the i can remove #[update(hidden = true)] or add the update=true it is respected in the extracted candid output but its like the types never seem to pull through.

Ah I think I know what is happening. In every query/update file i name the types as Arg and Response. It seems if i rename my imports like

use sns_rewards_api_canister::set_reward_token_types::{
    Args as SetRewardTokenTypesArgs,
    Response as SetRewardTokenTypesResponse,
};
export_candid!();

then it works

Not sure what is happening exactly but it looks like if you have duplicate names then it just gets ignored completely.

Not sure I understand it. You mean you have multiple Args and Response types in set_reward_token_types module? Or they are in different namespace?

Ignoring the type names, do you get structurally equal type definitions in the did file? For example, if you have struct A {} and struct B {}, it’s okay to export just one of the struct in the did file, because they are structurally equal.

I mean i have multiple module files in the api folder that export Args and Response e.g.

  • method_1.rs // exports Args & Response
  • method_2.rs // exports Args & Response
  • … repeats n times

When i import Args and Response as they are then i get the bad candid file.

If I import like this

use Args as ArgsB and use Response as ResponseB

then it works fine

I cannot reproduce this. Same type name in different modules should work fine. Can you create a minimal repo somewhere, so that I can try out?

I think what likely to happen is that some type names get ignored because they are structurally equal to another type. The exported did file is still correct.